@@ -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
{
526
526
}
527
527
else static if ((is (T == struct ) && ! canBitwiseHash! T) || T.tupleof.length == 1 )
528
528
{
529
+ static if (isChained) size_t h = seed;
529
530
static foreach (i, F; typeof (val.tupleof))
530
531
{
531
- static if (i != 0 )
532
- h = hashOf(val.tupleof[i], h);
533
- else static if (isChained)
534
- size_t h = hashOf(val.tupleof[i], seed);
532
+ static if (__traits(isStaticArray, F))
533
+ {
534
+ static if (i == 0 && ! isChained) size_t h = 0 ;
535
+ static if (F.sizeof > 0 && canBitwiseHash! F)
536
+ // May use smallBytesHash instead of bytesHash.
537
+ h = bytesHashWithExactSizeAndAlignment! F(toUbyte(val.tupleof[i]), h);
538
+ else
539
+ // We can avoid the "double hashing" the top-level version uses
540
+ // for consistency with TypeInfo.getHash.
541
+ foreach (ref e; val.tupleof[i])
542
+ h = hashOf(e, h);
543
+ }
544
+ else static if (is (F == struct ) || is (F == union ))
545
+ {
546
+ static if (hasCallableToHash! F)
547
+ {
548
+ static if (i == 0 && ! isChained)
549
+ size_t h = val.tupleof[i].toHash();
550
+ else
551
+ h = hashOf(cast (size_t ) val.tupleof[i].toHash(), h);
552
+ }
553
+ else static if (F.tupleof.length == 1 )
554
+ {
555
+ // Handle the single member case separately to avoid unnecessarily using bytesHash.
556
+ static if (i == 0 && ! isChained)
557
+ size_t h = hashOf(val.tupleof[i].tupleof[0 ]);
558
+ else
559
+ h = hashOf(val.tupleof[i].tupleof[0 ], h);
560
+ }
561
+ else static if (canBitwiseHash! F)
562
+ {
563
+ // May use smallBytesHash instead of bytesHash.
564
+ static if (i == 0 && ! isChained) size_t h = 0 ;
565
+ h = bytesHashWithExactSizeAndAlignment! F(toUbyte(val.tupleof[i]), h);
566
+ }
567
+ else
568
+ {
569
+ // Nothing special happening.
570
+ static if (i == 0 && ! isChained)
571
+ size_t h = hashOf(val.tupleof[i]);
572
+ else
573
+ h = hashOf(val.tupleof[i], h);
574
+ }
575
+ }
535
576
else
536
- size_t h = hashOf(val.tupleof[i]);
577
+ {
578
+ // Nothing special happening.
579
+ static if (i == 0 && ! isChained)
580
+ size_t h = hashOf(val.tupleof[i]);
581
+ else
582
+ h = hashOf(val.tupleof[i], h);
583
+ }
537
584
}
538
585
return h;
539
586
}
540
587
else static if (is (typeof (toUbyte(val)) == const (ubyte )[]))// CTFE ready for structs without reference fields
541
588
{
589
+ // Not using bytesHashWithExactSizeAndAlignment here because
590
+ // the result may differ from typeid(T).hashOf(&val).
542
591
return bytesHashAlignedBy! T(toUbyte(val), seed);
543
592
}
544
593
else // CTFE unsupported
545
594
{
546
- assert (! __ctfe, " unable to compute hash of " ~ T.stringof);
595
+ assert (! __ctfe, " unable to compute hash of " ~ T.stringof~ " at compile time " );
547
596
const (ubyte )[] bytes = (() @trusted => (cast (const (ubyte )* )&val)[0 .. T.sizeof])();
597
+ // Not using bytesHashWithExactSizeAndAlignment here because
598
+ // the result may differ from typeid(T).hashOf(&val).
548
599
return bytesHashAlignedBy! T(bytes, seed);
549
600
}
550
601
}
@@ -578,9 +629,9 @@ if (!is(T == enum) && (is(T == struct) || is(T == union))
578
629
@trusted @nogc nothrow pure
579
630
size_t hashOf (T)(scope const T val, size_t seed = 0 ) if (! is (T == enum ) && is (T == delegate ))
580
631
{
581
- assert (! __ctfe, " unable to compute hash of " ~ T.stringof);
632
+ assert (! __ctfe, " unable to compute hash of " ~ T.stringof~ " at compile time " );
582
633
const (ubyte )[] bytes = (cast (const (ubyte )* )&val)[0 .. T.sizeof];
583
- return bytesHashAlignedBy ! T(bytes, seed);
634
+ return bytesHashWithExactSizeAndAlignment ! T(bytes, seed);
584
635
}
585
636
586
637
// address-based class hash. CTFE only if null.
@@ -667,6 +718,31 @@ private template bytesHashAlignedBy(AlignType)
667
718
alias bytesHashAlignedBy = bytesHash! (AlignType.alignof >= uint .alignof);
668
719
}
669
720
721
+ private template bytesHashWithExactSizeAndAlignment (SizeAndAlignType)
722
+ {
723
+ static if (SizeAndAlignType.alignof < uint .alignof
724
+ ? SizeAndAlignType.sizeof <= 12
725
+ : SizeAndAlignType.sizeof <= 10 )
726
+ alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
727
+ else
728
+ alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy! SizeAndAlignType;
729
+ }
730
+
731
+ // Fowler/Noll/Vo hash. http://www.isthe.com/chongo/tech/comp/fnv/
732
+ private size_t fnv ()(scope const (ubyte )[] bytes, size_t seed) @nogc nothrow pure @safe
733
+ {
734
+ static if (size_t .max <= uint .max)
735
+ enum prime = (1U << 24 ) + (1U << 8 ) + 0x93U ;
736
+ else static if (size_t .max <= ulong .max)
737
+ enum prime = (1UL << 40 ) + (1UL << 8 ) + 0xb3UL ;
738
+ else
739
+ enum prime = (size_t (1 ) << 88 ) + (size_t (1 ) << 8 ) + size_t (0x3b );
740
+ foreach (b; bytes)
741
+ seed = (seed ^ b) * prime;
742
+ return seed;
743
+ }
744
+ private alias smallBytesHash = fnv;
745
+
670
746
// -----------------------------------------------------------------------------
671
747
// Block read - if your platform needs to do endian-swapping or can only
672
748
// handle aligned reads, do the conversion here
0 commit comments