From 35ad7df24afdff061cc0ba7d2e2db6c3006c9376 Mon Sep 17 00:00:00 2001 From: Nathan Sashihara <21227491+n8sh@users.noreply.github.com> Date: Wed, 1 Jul 2020 11:10:23 -0700 Subject: [PATCH] Fix Issue 21006 - core.internal.hash.bytesHash: in 64-bit builds use a 64-bit-oriented algorithm --- src/core/internal/hash.d | 100 +++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/src/core/internal/hash.d b/src/core/internal/hash.d index 05bbafaae69..36efc53449d 100644 --- a/src/core/internal/hash.d +++ b/src/core/internal/hash.d @@ -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; @@ -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; @@ -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 { @@ -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); }