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

Commit

Permalink
Fix Issue 21006 - core.internal.hash.bytesHash: in 64-bit builds use …
Browse files Browse the repository at this point in the history
…a 64-bit-oriented algorithm
  • Loading branch information
n8sh committed Aug 2, 2020
1 parent 104ac71 commit 35ad7df
Showing 1 changed file with 92 additions and 8 deletions.
100 changes: 92 additions & 8 deletions src/core/internal/hash.d
Original file line number Diff line number Diff line change
Expand Up @@ -623,14 +623,16 @@ size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)

private template bytesHashAlignedBy(AlignType)
{
alias bytesHashAlignedBy = bytesHash!(AlignType.alignof >= uint.alignof);
static if (size_t.sizeof == 4)
alias bytesHashAlignedBy = bytesHash32!(AlignType.alignof >= uint.alignof);
else
alias bytesHashAlignedBy = bytesHash64!();
}

private template bytesHashWithExactSizeAndAlignment(SizeAndAlignType)
{
static if (SizeAndAlignType.alignof < uint.alignof
? SizeAndAlignType.sizeof <= 12
: SizeAndAlignType.sizeof <= 10)
static if (SizeAndAlignType.sizeof <= 3 || size_t.sizeof <= 4 &&
SizeAndAlignType.sizeof <= (SizeAndAlignType.alignof < uint.alignof ? 12 : 10))
alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
else
alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy!SizeAndAlignType;
Expand Down Expand Up @@ -670,9 +672,11 @@ private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
Params:
dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
+/
static if (size_t.sizeof == 4)
@nogc nothrow pure @trusted
private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
private uint bytesHash32(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
{
// MurmurHash3_x86_32.
auto len = bytes.length;
auto data = bytes.ptr;
auto nblocks = len / 4;
Expand Down Expand Up @@ -725,6 +729,85 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
return h1;
}

static if (size_t.sizeof == 8)
@nogc nothrow pure @trusted
private ulong bytesHash64()(scope const ubyte[] bytes, ulong seed)
{
// MurmurHash3_x86_32 modified to be 64-bit using constants from MurmurHash3_x64_128.
alias h1 = seed;

enum ulong c1 = 0x87c37b91114253d5;
enum ulong c2 = 0x4cf5ad432745937f;
enum uint c3 = 0x52dce729;

const(ubyte)* data = bytes.ptr;
//----------
// body
for (const end_data = bytes.ptr + (bytes.length & ~7);
data !is end_data;
data += 8)
{
version (BigEndian)
{
auto k1 =
((cast(ulong) data[0]) << 56) |
((cast(ulong) data[1]) << 48) |
((cast(ulong) data[2]) << 40) |
((cast(ulong) data[3]) << 32) |
((cast(ulong) data[4]) << 24) |
((cast(ulong) data[5]) << 16) |
((cast(ulong) data[6]) << 8) |
(cast(ulong) data[7]);
}
else
{
auto k1 =
((cast(ulong) data[7]) << 56) |
((cast(ulong) data[6]) << 48) |
((cast(ulong) data[5]) << 40) |
((cast(ulong) data[4]) << 32) |
((cast(ulong) data[3]) << 24) |
((cast(ulong) data[2]) << 16) |
((cast(ulong) data[1]) << 8) |
(cast(ulong) data[0]);
}

k1 *= c1;
k1 = (k1 << 31) | (k1 >> (64 - 31));
k1 *= c2;

h1 ^= k1;
h1 = (h1 << 27) | (h1 >> (64 - 27));
h1 = h1*5+c3;
}

//----------
// tail
ulong k1 = 0;

switch (bytes.length & 7)
{
case 7: k1 ^= (cast(ulong) data[6]) << 48; goto case;
case 6: k1 ^= (cast(ulong) data[5]) << 40; goto case;
case 5: k1 ^= (cast(ulong) data[4]) << 32; goto case;
case 4: k1 ^= (cast(ulong) data[3]) << 24; goto case;
case 3: k1 ^= (cast(ulong) data[2]) << 16; goto case;
case 2: k1 ^= (cast(ulong) data[1]) << 8; goto case;
case 1: k1 ^= (cast(ulong) data[0]);
k1 *= c1; k1 = (k1 << 31) | (k1 >> (64 - 31)); k1 *= c2; h1 ^= k1;
goto default;
default:
}

//----------
// finalization
h1 ^= bytes.length;
// Force all bits of the hash block to avalanche.
h1 = (h1 ^ (h1 >> 33)) * 0xff51afd7ed558ccd;
h1 = (h1 ^ (h1 >> 33)) * 0xc4ceb9fe1a85ec53;
return h1 ^= h1 >> 33;
}

// Check that bytesHash works with CTFE
pure nothrow @system @nogc unittest
{
Expand All @@ -750,7 +833,8 @@ pure nothrow @system @nogc unittest
}
// It is okay to change the below values if you make a change
// that you expect to change the result of bytesHash.
assert(bytesHash(&a[1], a.length - 2, 0) == 2727459272);
assert(bytesHash(&b, 5, 0) == 2727459272);
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == 2727459272);
enum expectedResult = size_t.sizeof == 4 ? 2727459272U : 10677742034643552556UL;
assert(bytesHash(&a[1], a.length - 2, 0) == expectedResult);
assert(bytesHash(&b, 5, 0) == expectedResult);
assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == expectedResult);
}

0 comments on commit 35ad7df

Please sign in to comment.