@@ -674,14 +674,21 @@ size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
674674
675675private template bytesHashAlignedBy (AlignType)
676676{
677- alias bytesHashAlignedBy = bytesHash! (AlignType.alignof >= uint .alignof);
677+ static if (size_t .sizeof == 4 )
678+ alias bytesHashAlignedBy = bytesHash32! (
679+ AlignType.alignof >= uint .alignof ? uint .alignof :
680+ ubyte .alignof);
681+ else
682+ alias bytesHashAlignedBy = bytesHash64! (
683+ AlignType.alignof >= ulong .alignof ? ulong .alignof :
684+ AlignType.alignof >= uint .alignof ? uint .alignof :
685+ ubyte .alignof);
678686}
679687
680688private template bytesHashWithExactSizeAndAlignment (SizeAndAlignType)
681689{
682- static if (SizeAndAlignType.alignof < uint .alignof
683- ? SizeAndAlignType.sizeof <= 12
684- : SizeAndAlignType.sizeof <= 10 )
690+ static if (SizeAndAlignType.sizeof <= 3 || size_t .sizeof <= 4 &&
691+ SizeAndAlignType.sizeof <= (SizeAndAlignType.alignof < uint .alignof ? 12 : 10 ))
685692 alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
686693 else
687694 alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy! SizeAndAlignType;
@@ -717,13 +724,36 @@ private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
717724 }
718725}
719726
720- /+
721- Params:
722- dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
723- +/
727+ private ulong get64bits ()(scope const (ubyte )* x) @nogc nothrow pure @system
728+ {
729+ version (BigEndian )
730+ {
731+ return ((cast (ulong ) x[0 ]) << 56 ) |
732+ ((cast (ulong ) x[1 ]) << 48 ) |
733+ ((cast (ulong ) x[2 ]) << 40 ) |
734+ ((cast (ulong ) x[3 ]) << 32 ) |
735+ ((cast (ulong ) x[4 ]) << 24 ) |
736+ ((cast (ulong ) x[5 ]) << 16 ) |
737+ ((cast (ulong ) x[6 ]) << 8 ) |
738+ (cast (ulong ) x[7 ]); }
739+ else
740+ {
741+ return ((cast (ulong ) x[7 ]) << 56 ) |
742+ ((cast (ulong ) x[6 ]) << 48 ) |
743+ ((cast (ulong ) x[5 ]) << 40 ) |
744+ ((cast (ulong ) x[4 ]) << 32 ) |
745+ ((cast (ulong ) x[3 ]) << 24 ) |
746+ ((cast (ulong ) x[2 ]) << 16 ) |
747+ ((cast (ulong ) x[1 ]) << 8 ) |
748+ (cast (ulong ) x[0 ]);
749+ }
750+ }
751+
752+ static if (size_t .sizeof == 4 )
724753@nogc nothrow pure @trusted
725- private size_t bytesHash ( bool dataKnownToBeAligned )(scope const (ubyte )[] bytes, size_t seed)
754+ private uint bytesHash32 ( uint alignment )(scope const (ubyte )[] bytes, size_t seed)
726755{
756+ // MurmurHash3_x86_32.
727757 auto len = bytes.length;
728758 auto data = bytes.ptr;
729759 auto nblocks = len / 4 ;
@@ -739,10 +769,12 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
739769 auto end_data = data+ nblocks* uint .sizeof;
740770 for (; data!= end_data; data += uint .sizeof)
741771 {
742- static if (dataKnownToBeAligned )
772+ static if (alignment == uint .alignof )
743773 uint k1 = __ctfe ? get32bits(data) : * (cast (const uint * ) data);
744- else
774+ else static if (alignment == ubyte .alignof)
745775 uint k1 = get32bits(data);
776+ else
777+ static assert (0 , " Do not create unnecessary template instantiations." );
746778 k1 *= c1;
747779 k1 = (k1 << 15 ) | (k1 >> (32 - 15 ));
748780 k1 *= c2;
@@ -776,6 +808,74 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
776808 return h1;
777809}
778810
811+ static if (size_t .sizeof == 8 )
812+ @nogc nothrow pure @trusted
813+ private ulong bytesHash64 (uint alignment)(scope const ubyte [] bytes, ulong seed)
814+ {
815+ // MurmurHash3_x86_32 modified to be 64-bit using constants from MurmurHash3_x64_128.
816+ alias h1 = seed;
817+
818+ enum ulong c1 = 0x87c37b91114253d5 ;
819+ enum ulong c2 = 0x4cf5ad432745937f ;
820+ enum uint c3 = 0x52dce729 ;
821+
822+ const (ubyte )* data = bytes.ptr;
823+ // ----------
824+ // body
825+ for (const end_data = bytes.ptr + (bytes.length & ~ 7 );
826+ data ! is end_data;
827+ data += 8 )
828+ {
829+ static if (alignment == ulong .alignof)
830+ auto k1 = __ctfe ? get64bits(data) : * cast (ulong * ) data;
831+ else static if (alignment == uint .alignof)
832+ {
833+ version (BigEndian )
834+ auto k1 = __ctfe ? get64bits(data) : (((cast (ulong ) * cast (uint * ) data) << 32 ) | * cast (uint * ) (data + 4 ));
835+ else
836+ auto k1 = __ctfe ? get64bits(data) : (((cast (ulong ) * cast (uint * ) (data + 4 )) << 32 ) | * cast (uint * ) data);
837+ }
838+ else static if (alignment == ubyte .alignof)
839+ auto k1 = get64bits(data);
840+ else
841+ static assert (0 , " Do not create unnecessary template instantiations." );
842+
843+ k1 *= c1;
844+ k1 = (k1 << 31 ) | (k1 >> (64 - 31 ));
845+ k1 *= c2;
846+
847+ h1 ^= k1;
848+ h1 = (h1 << 27 ) | (h1 >> (64 - 27 ));
849+ h1 = h1* 5 + c3;
850+ }
851+
852+ // ----------
853+ // tail
854+ ulong k1 = 0 ;
855+
856+ switch (bytes.length & 7 )
857+ {
858+ case 7 : k1 ^= (cast (ulong ) data[6 ]) << 48 ; goto case ;
859+ case 6 : k1 ^= (cast (ulong ) data[5 ]) << 40 ; goto case ;
860+ case 5 : k1 ^= (cast (ulong ) data[4 ]) << 32 ; goto case ;
861+ case 4 : k1 ^= (cast (ulong ) data[3 ]) << 24 ; goto case ;
862+ case 3 : k1 ^= (cast (ulong ) data[2 ]) << 16 ; goto case ;
863+ case 2 : k1 ^= (cast (ulong ) data[1 ]) << 8 ; goto case ;
864+ case 1 : k1 ^= (cast (ulong ) data[0 ]);
865+ k1 *= c1; k1 = (k1 << 31 ) | (k1 >> (64 - 31 )); k1 *= c2; h1 ^= k1;
866+ goto default ;
867+ default :
868+ }
869+
870+ // ----------
871+ // finalization
872+ h1 ^= bytes.length;
873+ // Force all bits of the hash block to avalanche.
874+ h1 = (h1 ^ (h1 >> 33 )) * 0xff51afd7ed558ccd ;
875+ h1 = (h1 ^ (h1 >> 33 )) * 0xc4ceb9fe1a85ec53 ;
876+ return h1 ^= h1 >> 33 ;
877+ }
878+
779879// Check that bytesHash works with CTFE
780880pure nothrow @system @nogc unittest
781881{
@@ -793,15 +893,21 @@ pure nothrow @system @nogc unittest
793893 {
794894 const ubyte [7 ] a = [99 , 4 , 3 , 2 , 1 , 5 , 88 ];
795895 const uint [2 ] b = [0x04_03_02_01, 0x05_ff_ff_ff];
896+ const ulong [1 ] c = [0x04_03_02_01_05_ff_ff_ff];
796897 }
797898 else
798899 {
799900 const ubyte [7 ] a = [99 , 1 , 2 , 3 , 4 , 5 , 88 ];
800901 const uint [2 ] b = [0x04_03_02_01, 0xff_ff_ff_05];
902+ const ulong [1 ] c = [0xff_ff_ff_05_04_03_02_01];
801903 }
802904 // It is okay to change the below values if you make a change
803905 // that you expect to change the result of bytesHash.
804- assert (bytesHash(&a[1 ], a.length - 2 , 0 ) == 2727459272 );
805- assert (bytesHash(&b, 5 , 0 ) == 2727459272 );
806- assert (bytesHashAlignedBy! uint ((cast (const ubyte * ) &b)[0 .. 5 ], 0 ) == 2727459272 );
906+ enum expectedResult = size_t .sizeof == 4 ? 2727459272U : 10677742034643552556UL ;
907+ assert (bytesHash(&a[1 ], a.length - 2 , 0 ) == expectedResult);
908+ assert (bytesHash(&b, 5 , 0 ) == expectedResult);
909+ assert (bytesHashAlignedBy! uint ((cast (const ubyte * ) &b)[0 .. 5 ], 0 ) == expectedResult);
910+ assert (bytesHashAlignedBy! ulong ((cast (const ubyte * ) &c)[0 .. 5 ], 0 ) == expectedResult);
911+ assert (bytesHashAlignedBy! ulong ((cast (const ubyte * ) &c)[0 .. c.sizeof], 0 ) ==
912+ (size_t .sizeof == 4 ? 2948526704 : 7625915911016357963 ));
807913}
0 commit comments