@@ -669,14 +669,21 @@ size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
669669
670670private 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
675683private 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
775875pure 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