Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 0040975

Browse files
n8shRazvanN7
authored andcommitted
Fix Issue 21006 - core.internal.hash.bytesHash: in 64-bit builds use a 64-bit-oriented algorithm
1 parent 678bce1 commit 0040975

File tree

1 file changed

+120
-14
lines changed

1 file changed

+120
-14
lines changed

src/core/internal/hash.d

Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -674,14 +674,21 @@ size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
674674

675675
private template bytesHashAlignedBy(AlignType)
676676
{
677-
alias bytesHashAlignedBy = bytesHash!(AlignType.alignof >= uint.alignof);
677+
static if (size_t.sizeof == 4)
678+
alias bytesHashAlignedBy = bytesHash32!(
679+
AlignType.alignof >= uint.alignof ? uint.alignof :
680+
ubyte.alignof);
681+
else
682+
alias bytesHashAlignedBy = bytesHash64!(
683+
AlignType.alignof >= ulong.alignof ? ulong.alignof :
684+
AlignType.alignof >= uint.alignof ? uint.alignof :
685+
ubyte.alignof);
678686
}
679687

680688
private template bytesHashWithExactSizeAndAlignment(SizeAndAlignType)
681689
{
682-
static if (SizeAndAlignType.alignof < uint.alignof
683-
? SizeAndAlignType.sizeof <= 12
684-
: SizeAndAlignType.sizeof <= 10)
690+
static if (SizeAndAlignType.sizeof <= 3 || size_t.sizeof <= 4 &&
691+
SizeAndAlignType.sizeof <= (SizeAndAlignType.alignof < uint.alignof ? 12 : 10))
685692
alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
686693
else
687694
alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy!SizeAndAlignType;
@@ -717,13 +724,36 @@ private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
717724
}
718725
}
719726

720-
/+
721-
Params:
722-
dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
723-
+/
727+
private ulong get64bits()(scope const(ubyte)* x) @nogc nothrow pure @system
728+
{
729+
version (BigEndian)
730+
{
731+
return ((cast(ulong) x[0]) << 56) |
732+
((cast(ulong) x[1]) << 48) |
733+
((cast(ulong) x[2]) << 40) |
734+
((cast(ulong) x[3]) << 32) |
735+
((cast(ulong) x[4]) << 24) |
736+
((cast(ulong) x[5]) << 16) |
737+
((cast(ulong) x[6]) << 8) |
738+
(cast(ulong) x[7]); }
739+
else
740+
{
741+
return ((cast(ulong) x[7]) << 56) |
742+
((cast(ulong) x[6]) << 48) |
743+
((cast(ulong) x[5]) << 40) |
744+
((cast(ulong) x[4]) << 32) |
745+
((cast(ulong) x[3]) << 24) |
746+
((cast(ulong) x[2]) << 16) |
747+
((cast(ulong) x[1]) << 8) |
748+
(cast(ulong) x[0]);
749+
}
750+
}
751+
752+
static if (size_t.sizeof == 4)
724753
@nogc nothrow pure @trusted
725-
private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
754+
private uint bytesHash32(uint alignment)(scope const(ubyte)[] bytes, size_t seed)
726755
{
756+
// MurmurHash3_x86_32.
727757
auto len = bytes.length;
728758
auto data = bytes.ptr;
729759
auto nblocks = len / 4;
@@ -739,10 +769,12 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
739769
auto end_data = data+nblocks*uint.sizeof;
740770
for (; data!=end_data; data += uint.sizeof)
741771
{
742-
static if (dataKnownToBeAligned)
772+
static if (alignment == uint.alignof)
743773
uint k1 = __ctfe ? get32bits(data) : *(cast(const uint*) data);
744-
else
774+
else static if (alignment == ubyte.alignof)
745775
uint k1 = get32bits(data);
776+
else
777+
static assert(0, "Do not create unnecessary template instantiations.");
746778
k1 *= c1;
747779
k1 = (k1 << 15) | (k1 >> (32 - 15));
748780
k1 *= c2;
@@ -776,6 +808,74 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
776808
return h1;
777809
}
778810

811+
static if (size_t.sizeof == 8)
812+
@nogc nothrow pure @trusted
813+
private ulong bytesHash64(uint alignment)(scope const ubyte[] bytes, ulong seed)
814+
{
815+
// MurmurHash3_x86_32 modified to be 64-bit using constants from MurmurHash3_x64_128.
816+
alias h1 = seed;
817+
818+
enum ulong c1 = 0x87c37b91114253d5;
819+
enum ulong c2 = 0x4cf5ad432745937f;
820+
enum uint c3 = 0x52dce729;
821+
822+
const(ubyte)* data = bytes.ptr;
823+
//----------
824+
// body
825+
for (const end_data = bytes.ptr + (bytes.length & ~7);
826+
data !is end_data;
827+
data += 8)
828+
{
829+
static if (alignment == ulong.alignof)
830+
auto k1 = __ctfe ? get64bits(data) : *cast(ulong*) data;
831+
else static if (alignment == uint.alignof)
832+
{
833+
version (BigEndian)
834+
auto k1 = __ctfe ? get64bits(data) : (((cast(ulong) *cast(uint*) data) << 32) | *cast(uint*) (data + 4));
835+
else
836+
auto k1 = __ctfe ? get64bits(data) : (((cast(ulong) *cast(uint*) (data + 4)) << 32) | *cast(uint*) data);
837+
}
838+
else static if (alignment == ubyte.alignof)
839+
auto k1 = get64bits(data);
840+
else
841+
static assert(0, "Do not create unnecessary template instantiations.");
842+
843+
k1 *= c1;
844+
k1 = (k1 << 31) | (k1 >> (64 - 31));
845+
k1 *= c2;
846+
847+
h1 ^= k1;
848+
h1 = (h1 << 27) | (h1 >> (64 - 27));
849+
h1 = h1*5+c3;
850+
}
851+
852+
//----------
853+
// tail
854+
ulong k1 = 0;
855+
856+
switch (bytes.length & 7)
857+
{
858+
case 7: k1 ^= (cast(ulong) data[6]) << 48; goto case;
859+
case 6: k1 ^= (cast(ulong) data[5]) << 40; goto case;
860+
case 5: k1 ^= (cast(ulong) data[4]) << 32; goto case;
861+
case 4: k1 ^= (cast(ulong) data[3]) << 24; goto case;
862+
case 3: k1 ^= (cast(ulong) data[2]) << 16; goto case;
863+
case 2: k1 ^= (cast(ulong) data[1]) << 8; goto case;
864+
case 1: k1 ^= (cast(ulong) data[0]);
865+
k1 *= c1; k1 = (k1 << 31) | (k1 >> (64 - 31)); k1 *= c2; h1 ^= k1;
866+
goto default;
867+
default:
868+
}
869+
870+
//----------
871+
// finalization
872+
h1 ^= bytes.length;
873+
// Force all bits of the hash block to avalanche.
874+
h1 = (h1 ^ (h1 >> 33)) * 0xff51afd7ed558ccd;
875+
h1 = (h1 ^ (h1 >> 33)) * 0xc4ceb9fe1a85ec53;
876+
return h1 ^= h1 >> 33;
877+
}
878+
779879
// Check that bytesHash works with CTFE
780880
pure nothrow @system @nogc unittest
781881
{
@@ -793,15 +893,21 @@ pure nothrow @system @nogc unittest
793893
{
794894
const ubyte[7] a = [99, 4, 3, 2, 1, 5, 88];
795895
const uint[2] b = [0x04_03_02_01, 0x05_ff_ff_ff];
896+
const ulong[1] c = [0x04_03_02_01_05_ff_ff_ff];
796897
}
797898
else
798899
{
799900
const ubyte[7] a = [99, 1, 2, 3, 4, 5, 88];
800901
const uint[2] b = [0x04_03_02_01, 0xff_ff_ff_05];
902+
const ulong[1] c = [0xff_ff_ff_05_04_03_02_01];
801903
}
802904
// It is okay to change the below values if you make a change
803905
// that you expect to change the result of bytesHash.
804-
assert(bytesHash(&a[1], a.length - 2, 0) == 2727459272);
805-
assert(bytesHash(&b, 5, 0) == 2727459272);
806-
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == 2727459272);
906+
enum expectedResult = size_t.sizeof == 4 ? 2727459272U : 10677742034643552556UL;
907+
assert(bytesHash(&a[1], a.length - 2, 0) == expectedResult);
908+
assert(bytesHash(&b, 5, 0) == expectedResult);
909+
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == expectedResult);
910+
assert(bytesHashAlignedBy!ulong((cast(const ubyte*) &c)[0 .. 5], 0) == expectedResult);
911+
assert(bytesHashAlignedBy!ulong((cast(const ubyte*) &c)[0 .. c.sizeof], 0) ==
912+
(size_t.sizeof == 4 ? 2948526704 : 7625915911016357963));
807913
}

0 commit comments

Comments
 (0)