@@ -257,7 +257,7 @@ if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T)
257
257
// else static if (T.length == 1)
258
258
// return hashOf(val[0], seed);
259
259
// else
260
- // /+ hash like a dynamic array +/
260
+ // return bytesHashWithExactSizeAndAlignment!T(toUbyte(val), seed);
261
261
//
262
262
// ... but that's inefficient when using a runtime TypeInfo (introduces a branch)
263
263
// and PR #2243 wants typeid(T).getHash(&val) to produce the same result as
@@ -412,7 +412,7 @@ size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && __tra
412
412
else static if (T.mant_dig == double .mant_dig && T.sizeof == ulong .sizeof)
413
413
return hashOf (* cast (const ulong * ) &data, seed);
414
414
else
415
- return bytesHashAlignedBy ! T(toUbyte(data), seed);
415
+ return bytesHashWithExactSizeAndAlignment ! T(toUbyte(data), seed);
416
416
}
417
417
else
418
418
{
534
534
}
535
535
else static if ((is (T == struct ) && ! canBitwiseHash! T) || T.tupleof.length == 1 )
536
536
{
537
+ static if (isChained) size_t h = seed;
537
538
static foreach (i, F; typeof (val.tupleof))
538
539
{
539
- static if (i != 0 )
540
- h = hashOf(val.tupleof[i], h);
541
- else static if (isChained)
542
- size_t h = hashOf(val.tupleof[i], seed);
540
+ static if (__traits(isStaticArray, F))
541
+ {
542
+ static if (i == 0 && ! isChained) size_t h = 0 ;
543
+ static if (F.sizeof > 0 && canBitwiseHash! F)
544
+ // May use smallBytesHash instead of bytesHash.
545
+ h = bytesHashWithExactSizeAndAlignment! F(toUbyte(val.tupleof[i]), h);
546
+ else
547
+ // We can avoid the "double hashing" the top-level version uses
548
+ // for consistency with TypeInfo.getHash.
549
+ foreach (ref e; val.tupleof[i])
550
+ h = hashOf(e, h);
551
+ }
552
+ else static if (is (F == struct ) || is (F == union ))
553
+ {
554
+ static if (hasCallableToHash! F)
555
+ {
556
+ static if (i == 0 && ! isChained)
557
+ size_t h = val.tupleof[i].toHash();
558
+ else
559
+ h = hashOf(cast (size_t ) val.tupleof[i].toHash(), h);
560
+ }
561
+ else static if (F.tupleof.length == 1 )
562
+ {
563
+ // Handle the single member case separately to avoid unnecessarily using bytesHash.
564
+ static if (i == 0 && ! isChained)
565
+ size_t h = hashOf(val.tupleof[i].tupleof[0 ]);
566
+ else
567
+ h = hashOf(val.tupleof[i].tupleof[0 ], h);
568
+ }
569
+ else static if (canBitwiseHash! F)
570
+ {
571
+ // May use smallBytesHash instead of bytesHash.
572
+ static if (i == 0 && ! isChained) size_t h = 0 ;
573
+ h = bytesHashWithExactSizeAndAlignment! F(toUbyte(val.tupleof[i]), h);
574
+ }
575
+ else
576
+ {
577
+ // Nothing special happening.
578
+ static if (i == 0 && ! isChained)
579
+ size_t h = hashOf(val.tupleof[i]);
580
+ else
581
+ h = hashOf(val.tupleof[i], h);
582
+ }
583
+ }
543
584
else
544
- size_t h = hashOf(val.tupleof[i]);
585
+ {
586
+ // Nothing special happening.
587
+ static if (i == 0 && ! isChained)
588
+ size_t h = hashOf(val.tupleof[i]);
589
+ else
590
+ h = hashOf(val.tupleof[i], h);
591
+ }
545
592
}
546
593
return h;
547
594
}
548
595
else static if (is (typeof (toUbyte(val)) == const (ubyte )[]))// CTFE ready for structs without reference fields
549
596
{
597
+ // Not using bytesHashWithExactSizeAndAlignment here because
598
+ // the result may differ from typeid(T).hashOf(&val).
550
599
return bytesHashAlignedBy! T(toUbyte(val), seed);
551
600
}
552
601
else // CTFE unsupported
553
602
{
554
- assert (! __ctfe, " unable to compute hash of " ~ T.stringof);
603
+ assert (! __ctfe, " unable to compute hash of " ~ T.stringof~ " at compile time " );
555
604
const (ubyte )[] bytes = (() @trusted => (cast (const (ubyte )* )&val)[0 .. T.sizeof])();
605
+ // Not using bytesHashWithExactSizeAndAlignment here because
606
+ // the result may differ from typeid(T).hashOf(&val).
556
607
return bytesHashAlignedBy! T(bytes, seed);
557
608
}
558
609
}
@@ -596,9 +647,9 @@ if (!is(T == enum) && (is(T == struct) || is(T == union))
596
647
@trusted @nogc nothrow pure
597
648
size_t hashOf (T)(scope const T val, size_t seed = 0 ) if (! is (T == enum ) && is (T == delegate ))
598
649
{
599
- assert (! __ctfe, " unable to compute hash of " ~ T.stringof);
650
+ assert (! __ctfe, " unable to compute hash of " ~ T.stringof~ " at compile time " );
600
651
const (ubyte )[] bytes = (cast (const (ubyte )* )&val)[0 .. T.sizeof];
601
- return bytesHashAlignedBy ! T(bytes, seed);
652
+ return bytesHashWithExactSizeAndAlignment ! T(bytes, seed);
602
653
}
603
654
604
655
// address-based class hash. CTFE only if null.
@@ -685,6 +736,31 @@ private template bytesHashAlignedBy(AlignType)
685
736
alias bytesHashAlignedBy = bytesHash! (AlignType.alignof >= uint .alignof);
686
737
}
687
738
739
+ private template bytesHashWithExactSizeAndAlignment (SizeAndAlignType)
740
+ {
741
+ static if (SizeAndAlignType.alignof < uint .alignof
742
+ ? SizeAndAlignType.sizeof <= 12
743
+ : SizeAndAlignType.sizeof <= 10 )
744
+ alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
745
+ else
746
+ alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy! SizeAndAlignType;
747
+ }
748
+
749
+ // Fowler/Noll/Vo hash. http://www.isthe.com/chongo/tech/comp/fnv/
750
+ private size_t fnv ()(scope const (ubyte )[] bytes, size_t seed) @nogc nothrow pure @safe
751
+ {
752
+ static if (size_t .max <= uint .max)
753
+ enum prime = (1U << 24 ) + (1U << 8 ) + 0x93U ;
754
+ else static if (size_t .max <= ulong .max)
755
+ enum prime = (1UL << 40 ) + (1UL << 8 ) + 0xb3UL ;
756
+ else
757
+ enum prime = (size_t (1 ) << 88 ) + (size_t (1 ) << 8 ) + size_t (0x3b );
758
+ foreach (b; bytes)
759
+ seed = (seed ^ b) * prime;
760
+ return seed;
761
+ }
762
+ private alias smallBytesHash = fnv;
763
+
688
764
// -----------------------------------------------------------------------------
689
765
// Block read - if your platform needs to do endian-swapping or can only
690
766
// handle aligned reads, do the conversion here
0 commit comments