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

Commit 4b0e672

Browse files
committed
Fix Issue 21006 - core.internal.hash.bytesHash: in 64-bit builds use a 64-bit-oriented algorithm
1 parent 104ac71 commit 4b0e672

File tree

1 file changed

+90
-8
lines changed

1 file changed

+90
-8
lines changed

src/core/internal/hash.d

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -623,14 +623,16 @@ size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
623623

624624
private template bytesHashAlignedBy(AlignType)
625625
{
626-
alias bytesHashAlignedBy = bytesHash!(AlignType.alignof >= uint.alignof);
626+
static if (size_t.sizeof == 4)
627+
alias bytesHashAlignedBy = bytesHash32!(AlignType.alignof >= uint.alignof);
628+
else
629+
alias bytesHashAlignedBy = bytesHash64!();
627630
}
628631

629632
private template bytesHashWithExactSizeAndAlignment(SizeAndAlignType)
630633
{
631-
static if (SizeAndAlignType.alignof < uint.alignof
632-
? SizeAndAlignType.sizeof <= 12
633-
: SizeAndAlignType.sizeof <= 10)
634+
static if (SizeAndAlignType.sizeof <= 3 || size_t.sizeof <= 4 &&
635+
SizeAndAlignType.sizeof <= (SizeAndAlignType.alignof < uint.alignof ? 12 : 10))
634636
alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
635637
else
636638
alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy!SizeAndAlignType;
@@ -670,8 +672,9 @@ private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
670672
Params:
671673
dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
672674
+/
675+
static if (size_t.sizeof == 4)
673676
@nogc nothrow pure @trusted
674-
private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
677+
private uint bytesHash32(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
675678
{
676679
auto len = bytes.length;
677680
auto data = bytes.ptr;
@@ -725,6 +728,84 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
725728
return h1;
726729
}
727730

731+
static if (size_t.sizeof == 8)
732+
@nogc nothrow pure @trusted
733+
private ulong bytesHash64()(scope const ubyte[] bytes, ulong seed)
734+
{
735+
alias h1 = seed;
736+
737+
enum ulong c1 = 0x87c37b91114253d5;
738+
enum ulong c2 = 0x4cf5ad432745937f;
739+
enum uint c3 = 0x52dce729;
740+
741+
const(ubyte)* data = bytes.ptr;
742+
//----------
743+
// body
744+
for (const end_data = bytes.ptr + (bytes.length & ~7);
745+
data !is end_data;
746+
data += 8)
747+
{
748+
version (BigEndian)
749+
{
750+
auto k1 =
751+
((cast(ulong) data[0]) << 56) |
752+
((cast(ulong) data[1]) << 48) |
753+
((cast(ulong) data[2]) << 40) |
754+
((cast(ulong) data[3]) << 32) |
755+
((cast(ulong) data[4]) << 24) |
756+
((cast(ulong) data[5]) << 16) |
757+
((cast(ulong) data[6]) << 8) |
758+
(cast(ulong) data[7]);
759+
}
760+
else
761+
{
762+
auto k1 =
763+
((cast(ulong) data[7]) << 56) |
764+
((cast(ulong) data[6]) << 48) |
765+
((cast(ulong) data[5]) << 40) |
766+
((cast(ulong) data[4]) << 32) |
767+
((cast(ulong) data[3]) << 24) |
768+
((cast(ulong) data[2]) << 16) |
769+
((cast(ulong) data[1]) << 8) |
770+
(cast(ulong) data[0]);
771+
}
772+
773+
k1 *= c1;
774+
k1 = (k1 << 31) | (k1 >> (64 - 31));
775+
k1 *= c2;
776+
777+
h1 ^= k1;
778+
h1 = (h1 << 27) | (h1 >> (64 - 27));
779+
h1 = h1*5+c3;
780+
}
781+
782+
//----------
783+
// tail
784+
ulong k1 = 0;
785+
786+
switch (bytes.length & 7)
787+
{
788+
case 7: k1 ^= (cast(ulong) data[6]) << 48; goto case;
789+
case 6: k1 ^= (cast(ulong) data[5]) << 40; goto case;
790+
case 5: k1 ^= (cast(ulong) data[4]) << 32; goto case;
791+
case 4: k1 ^= (cast(ulong) data[3]) << 24; goto case;
792+
case 3: k1 ^= (cast(ulong) data[2]) << 16; goto case;
793+
case 2: k1 ^= (cast(ulong) data[1]) << 8; goto case;
794+
case 1: k1 ^= (cast(ulong) data[0]);
795+
k1 *= c1; k1 = (k1 << 31) | (k1 >> (64 - 31)); k1 *= c2; h1 ^= k1;
796+
goto default;
797+
default:
798+
}
799+
800+
//----------
801+
// finalization
802+
h1 ^= bytes.length;
803+
// Force all bits of the hash block to avalanche.
804+
h1 = (h1 ^ (h1 >> 33)) * 0xff51afd7ed558ccd;
805+
h1 = (h1 ^ (h1 >> 33)) * 0xc4ceb9fe1a85ec53;
806+
return h1 ^= h1 >> 33;
807+
}
808+
728809
// Check that bytesHash works with CTFE
729810
pure nothrow @system @nogc unittest
730811
{
@@ -750,7 +831,8 @@ pure nothrow @system @nogc unittest
750831
}
751832
// It is okay to change the below values if you make a change
752833
// that you expect to change the result of bytesHash.
753-
assert(bytesHash(&a[1], a.length - 2, 0) == 2727459272);
754-
assert(bytesHash(&b, 5, 0) == 2727459272);
755-
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == 2727459272);
834+
enum expectedResult = size_t.sizeof == 4 ? 2727459272U : 10677742034643552556UL;
835+
assert(bytesHash(&a[1], a.length - 2, 0) == expectedResult);
836+
assert(bytesHash(&b, 5, 0) == expectedResult);
837+
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == expectedResult);
756838
}

0 commit comments

Comments
 (0)