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

Commit c9b3fda

Browse files
committed
Fix Issue 21006 - core.internal.hash.bytesHash: in 64-bit builds use a 64-bit-oriented algorithm
1 parent 16c836a commit c9b3fda

File tree

1 file changed

+120
-14
lines changed

1 file changed

+120
-14
lines changed

src/core/internal/hash.d

+120-14
Original file line numberDiff line numberDiff line change
@@ -669,14 +669,21 @@ size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
669669

670670
private template bytesHashAlignedBy(AlignType)
671671
{
672-
alias bytesHashAlignedBy = bytesHash!(AlignType.alignof >= uint.alignof);
672+
static if (size_t.sizeof == 4)
673+
alias bytesHashAlignedBy = bytesHash32!(
674+
AlignType.alignof >= uint.alignof ? uint.alignof :
675+
ubyte.alignof);
676+
else
677+
alias bytesHashAlignedBy = bytesHash64!(
678+
AlignType.alignof >= ulong.alignof ? ulong.alignof :
679+
AlignType.alignof >= uint.alignof ? uint.alignof :
680+
ubyte.alignof);
673681
}
674682

675683
private template bytesHashWithExactSizeAndAlignment(SizeAndAlignType)
676684
{
677-
static if (SizeAndAlignType.alignof < uint.alignof
678-
? SizeAndAlignType.sizeof <= 12
679-
: SizeAndAlignType.sizeof <= 10)
685+
static if (SizeAndAlignType.sizeof <= 3 || size_t.sizeof <= 4 &&
686+
SizeAndAlignType.sizeof <= (SizeAndAlignType.alignof < uint.alignof ? 12 : 10))
680687
alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
681688
else
682689
alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy!SizeAndAlignType;
@@ -712,13 +719,36 @@ private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
712719
}
713720
}
714721

715-
/+
716-
Params:
717-
dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
718-
+/
722+
private ulong get64bits()(scope const(ubyte)* x) @nogc nothrow pure @system
723+
{
724+
version (BigEndian)
725+
{
726+
return ((cast(ulong) x[0]) << 56) |
727+
((cast(ulong) x[1]) << 48) |
728+
((cast(ulong) x[2]) << 40) |
729+
((cast(ulong) x[3]) << 32) |
730+
((cast(ulong) x[4]) << 24) |
731+
((cast(ulong) x[5]) << 16) |
732+
((cast(ulong) x[6]) << 8) |
733+
(cast(ulong) x[7]); }
734+
else
735+
{
736+
return ((cast(ulong) x[7]) << 56) |
737+
((cast(ulong) x[6]) << 48) |
738+
((cast(ulong) x[5]) << 40) |
739+
((cast(ulong) x[4]) << 32) |
740+
((cast(ulong) x[3]) << 24) |
741+
((cast(ulong) x[2]) << 16) |
742+
((cast(ulong) x[1]) << 8) |
743+
(cast(ulong) x[0]);
744+
}
745+
}
746+
747+
static if (size_t.sizeof == 4)
719748
@nogc nothrow pure @trusted
720-
private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
749+
private uint bytesHash32(uint alignment)(scope const(ubyte)[] bytes, size_t seed)
721750
{
751+
// MurmurHash3_x86_32.
722752
auto len = bytes.length;
723753
auto data = bytes.ptr;
724754
auto nblocks = len / 4;
@@ -734,10 +764,12 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
734764
auto end_data = data+nblocks*uint.sizeof;
735765
for (; data!=end_data; data += uint.sizeof)
736766
{
737-
static if (dataKnownToBeAligned)
767+
static if (alignment == uint.alignof)
738768
uint k1 = __ctfe ? get32bits(data) : *(cast(const uint*) data);
739-
else
769+
else static if (alignment == ubyte.alignof)
740770
uint k1 = get32bits(data);
771+
else
772+
static assert(0, "Do not create unnecessary template instantiations.");
741773
k1 *= c1;
742774
k1 = (k1 << 15) | (k1 >> (32 - 15));
743775
k1 *= c2;
@@ -771,6 +803,74 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
771803
return h1;
772804
}
773805

806+
static if (size_t.sizeof == 8)
807+
@nogc nothrow pure @trusted
808+
private ulong bytesHash64(uint alignment)(scope const ubyte[] bytes, ulong seed)
809+
{
810+
// MurmurHash3_x86_32 modified to be 64-bit using constants from MurmurHash3_x64_128.
811+
alias h1 = seed;
812+
813+
enum ulong c1 = 0x87c37b91114253d5;
814+
enum ulong c2 = 0x4cf5ad432745937f;
815+
enum uint c3 = 0x52dce729;
816+
817+
const(ubyte)* data = bytes.ptr;
818+
//----------
819+
// body
820+
for (const end_data = bytes.ptr + (bytes.length & ~7);
821+
data !is end_data;
822+
data += 8)
823+
{
824+
static if (alignment == ulong.alignof)
825+
auto k1 = __ctfe ? get64bits(data) : *cast(ulong*) data;
826+
else static if (alignment == uint.alignof)
827+
{
828+
version (BigEndian)
829+
auto k1 = __ctfe ? get64bits(data) : (((cast(ulong) *cast(uint*) data) << 32) | *cast(uint*) (data + 4));
830+
else
831+
auto k1 = __ctfe ? get64bits(data) : (((cast(ulong) *cast(uint*) (data + 4)) << 32) | *cast(uint*) data);
832+
}
833+
else static if (alignment == ubyte.alignof)
834+
auto k1 = get64bits(data);
835+
else
836+
static assert(0, "Do not create unnecessary template instantiations.");
837+
838+
k1 *= c1;
839+
k1 = (k1 << 31) | (k1 >> (64 - 31));
840+
k1 *= c2;
841+
842+
h1 ^= k1;
843+
h1 = (h1 << 27) | (h1 >> (64 - 27));
844+
h1 = h1*5+c3;
845+
}
846+
847+
//----------
848+
// tail
849+
ulong k1 = 0;
850+
851+
switch (bytes.length & 7)
852+
{
853+
case 7: k1 ^= (cast(ulong) data[6]) << 48; goto case;
854+
case 6: k1 ^= (cast(ulong) data[5]) << 40; goto case;
855+
case 5: k1 ^= (cast(ulong) data[4]) << 32; goto case;
856+
case 4: k1 ^= (cast(ulong) data[3]) << 24; goto case;
857+
case 3: k1 ^= (cast(ulong) data[2]) << 16; goto case;
858+
case 2: k1 ^= (cast(ulong) data[1]) << 8; goto case;
859+
case 1: k1 ^= (cast(ulong) data[0]);
860+
k1 *= c1; k1 = (k1 << 31) | (k1 >> (64 - 31)); k1 *= c2; h1 ^= k1;
861+
goto default;
862+
default:
863+
}
864+
865+
//----------
866+
// finalization
867+
h1 ^= bytes.length;
868+
// Force all bits of the hash block to avalanche.
869+
h1 = (h1 ^ (h1 >> 33)) * 0xff51afd7ed558ccd;
870+
h1 = (h1 ^ (h1 >> 33)) * 0xc4ceb9fe1a85ec53;
871+
return h1 ^= h1 >> 33;
872+
}
873+
774874
// Check that bytesHash works with CTFE
775875
pure nothrow @system @nogc unittest
776876
{
@@ -788,15 +888,21 @@ pure nothrow @system @nogc unittest
788888
{
789889
const ubyte[7] a = [99, 4, 3, 2, 1, 5, 88];
790890
const uint[2] b = [0x04_03_02_01, 0x05_ff_ff_ff];
891+
const ulong[1] c = [0x04_03_02_01_05_ff_ff_ff];
791892
}
792893
else
793894
{
794895
const ubyte[7] a = [99, 1, 2, 3, 4, 5, 88];
795896
const uint[2] b = [0x04_03_02_01, 0xff_ff_ff_05];
897+
const ulong[1] c = [0xff_ff_ff_05_04_03_02_01];
796898
}
797899
// It is okay to change the below values if you make a change
798900
// that you expect to change the result of bytesHash.
799-
assert(bytesHash(&a[1], a.length - 2, 0) == 2727459272);
800-
assert(bytesHash(&b, 5, 0) == 2727459272);
801-
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == 2727459272);
901+
enum expectedResult = size_t.sizeof == 4 ? 2727459272U : 10677742034643552556UL;
902+
assert(bytesHash(&a[1], a.length - 2, 0) == expectedResult);
903+
assert(bytesHash(&b, 5, 0) == expectedResult);
904+
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == expectedResult);
905+
assert(bytesHashAlignedBy!ulong((cast(const ubyte*) &c)[0 .. 5], 0) == expectedResult);
906+
assert(bytesHashAlignedBy!ulong((cast(const ubyte*) &c)[0 .. c.sizeof], 0) ==
907+
(size_t.sizeof == 4 ? 2948526704 : 7625915911016357963));
802908
}

0 commit comments

Comments
 (0)