@@ -147,7 +147,9 @@ private template useScopeConstPassByValue(T)
147
147
else static if (is (T == struct ) || is (T == union ))
148
148
{
149
149
// Overly restrictive for simplicity.
150
- enum useScopeConstPassByValue = false ;
150
+ enum useScopeConstPassByValue = T.sizeof <= (int []).sizeof &&
151
+ __traits (isPOD , T) && // "isPOD" just to check there's no dtor or postblit.
152
+ canBitwiseHash! T; // We can't verify toHash doesn't leak.
151
153
}
152
154
else static if (is (T : E[], E))
153
155
{
@@ -157,8 +159,8 @@ private template useScopeConstPassByValue(T)
157
159
else static if (T.length == 0 )
158
160
enum useScopeConstPassByValue = true ;
159
161
else
160
- enum useScopeConstPassByValue = T.sizeof <= size_t .sizeof
161
- && .useScopeConstPassByValue! (typeof (T[0 ]));
162
+ enum useScopeConstPassByValue = T.sizeof <= ( uint []) .sizeof
163
+ && .useScopeConstPassByValue! (typeof (T.init [0 ]));
162
164
}
163
165
else static if (is (T : V[K], K, V))
164
166
{
@@ -189,7 +191,21 @@ private template useScopeConstPassByValue(T)
189
191
}
190
192
191
193
// enum hash. CTFE depends on base type
192
- size_t hashOf (T)(scope const T val, size_t seed = 0 )
194
+ size_t hashOf (T)(scope const T val)
195
+ if (is (T EType == enum ) && useScopeConstPassByValue! EType)
196
+ {
197
+ static if (is (T EType == enum )) // for EType
198
+ {
199
+ return hashOf (cast (const EType) val);
200
+ }
201
+ else
202
+ {
203
+ static assert (0 );
204
+ }
205
+ }
206
+
207
+ // enum hash. CTFE depends on base type
208
+ size_t hashOf (T)(scope const T val, size_t seed)
193
209
if (is (T EType == enum ) && useScopeConstPassByValue! EType)
194
210
{
195
211
static if (is (T EType == enum )) // for EType
@@ -273,10 +289,19 @@ if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T)
273
289
size_t hashOf (T)(scope const T val, size_t seed = 0 )
274
290
if (! is (T == enum ) && ! is (T : typeof (null )) && is (T S: S[]) && ! __traits(isStaticArray, T)
275
291
&& ! is (T == struct ) && ! is (T == class ) && ! is (T == union )
276
- && canBitwiseHash! S)
292
+ && (__traits(isScalar, S) || canBitwiseHash! S) )
277
293
{
278
294
alias ElementType = typeof (val[0 ]);
279
- static if (is (typeof (toUbyte(val)) == const (ubyte )[]))
295
+ static if (! canBitwiseHash! ElementType)
296
+ {
297
+ size_t hash = seed;
298
+ foreach (ref o; val)
299
+ {
300
+ hash = hashOf(hashOf(o), hash); // double hashing to match TypeInfo.getHash
301
+ }
302
+ return hash;
303
+ }
304
+ else static if (is (typeof (toUbyte(val)) == const (ubyte )[]))
280
305
// ubyteble array (arithmetic types and structs without toHash) CTFE ready for arithmetic types and structs without reference fields
281
306
{
282
307
return bytesHashAlignedBy! ElementType(toUbyte(val), seed);
@@ -292,7 +317,7 @@ if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStati
292
317
size_t hashOf (T)(T val, size_t seed = 0 )
293
318
if (! is (T == enum ) && ! is (T : typeof (null )) && is (T S: S[]) && ! __traits(isStaticArray, T)
294
319
&& ! is (T == struct ) && ! is (T == class ) && ! is (T == union )
295
- && ! canBitwiseHash! S)
320
+ && ! (__traits(isScalar, S) || canBitwiseHash! S) )
296
321
{
297
322
size_t hash = seed;
298
323
foreach (ref o; val)
@@ -316,7 +341,47 @@ if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStati
316
341
317
342
// arithmetic type hash
318
343
@trusted @nogc nothrow pure
319
- size_t hashOf (T)(scope const T val, size_t seed = 0 ) if (! is (T == enum ) && __traits(isArithmetic, T))
344
+ size_t hashOf (T)(scope const T val) if (! is (T == enum ) && __traits(isArithmetic, T)
345
+ && __traits(isIntegral, T) && T.sizeof <= size_t .sizeof)
346
+ {
347
+ return cast (UnqualUnsigned! T) val;
348
+ }
349
+
350
+ // arithmetic type hash
351
+ @trusted @nogc nothrow pure
352
+ size_t hashOf (T)(scope const T val, size_t seed) if (! is (T == enum ) && __traits(isArithmetic, T)
353
+ && __traits(isIntegral, T) && T.sizeof <= size_t .sizeof)
354
+ {
355
+ static if (size_t .sizeof < ulong .sizeof)
356
+ {
357
+ // MurmurHash3 32-bit single round
358
+ enum uint c1 = 0xcc9e2d51 ;
359
+ enum uint c2 = 0x1b873593 ;
360
+ enum uint c3 = 0xe6546b64 ;
361
+ enum uint r1 = 15 ;
362
+ enum uint r2 = 13 ;
363
+ }
364
+ else
365
+ {
366
+ // Half of MurmurHash3 64-bit single round
367
+ // (omits second interleaved update)
368
+ enum ulong c1 = 0x87c37b91114253d5 ;
369
+ enum ulong c2 = 0x4cf5ad432745937f ;
370
+ enum ulong c3 = 0x52dce729 ;
371
+ enum uint r1 = 31 ;
372
+ enum uint r2 = 27 ;
373
+ }
374
+ auto h = c1 * cast (UnqualUnsigned! T) val;
375
+ h = (h << r1) | (h >>> (typeof (h).sizeof * 8 - r1));
376
+ h = (h * c2) ^ seed;
377
+ h = (h << r2) | (h >>> (typeof (h).sizeof * 8 - r2));
378
+ return h * 5 + c3;
379
+ }
380
+
381
+ // arithmetic type hash
382
+ @trusted @nogc nothrow pure
383
+ size_t hashOf (T)(scope const T val, size_t seed = 0 ) if (! is (T == enum ) && __traits(isArithmetic, T)
384
+ && (! __traits(isIntegral, T) || T.sizeof > size_t .sizeof))
320
385
{
321
386
static if (__traits(isFloating, val))
322
387
{
@@ -351,56 +416,52 @@ size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && __tra
351
416
}
352
417
else
353
418
{
354
- static if (T.sizeof <= size_t .sizeof && __traits(isIntegral, T))
355
- {
356
- static if (size_t .sizeof < ulong .sizeof)
357
- {
358
- // MurmurHash3 32-bit single round
359
- enum uint c1 = 0xcc9e2d51 ;
360
- enum uint c2 = 0x1b873593 ;
361
- enum uint c3 = 0xe6546b64 ;
362
- enum uint r1 = 15 ;
363
- enum uint r2 = 13 ;
364
- }
365
- else
366
- {
367
- // Half of MurmurHash3 64-bit single round
368
- // (omits second interleaved update)
369
- enum ulong c1 = 0x87c37b91114253d5 ;
370
- enum ulong c2 = 0x4cf5ad432745937f ;
371
- enum ulong c3 = 0x52dce729 ;
372
- enum uint r1 = 31 ;
373
- enum uint r2 = 27 ;
374
- }
375
- auto h = c1 * cast (UnqualUnsigned! T) val;
376
- h = (h << r1) | (h >>> (typeof (h).sizeof * 8 - r1));
377
- h = (h * c2) ^ seed;
378
- h = (h << r2) | (h >>> (typeof (h).sizeof * 8 - r2));
379
- return h * 5 + c3;
380
- }
381
- else static if (T.sizeof > size_t .sizeof && __traits(isIntegral, T))
382
- {
383
- static foreach (i; 0 .. T.sizeof / size_t .sizeof)
384
- seed = hashOf(cast (size_t ) (val >>> (size_t .sizeof * 8 * i)), seed);
385
- return seed;
386
- }
387
- else
388
- {
389
- return bytesHashAlignedBy! T(toUbyte(val), seed);
390
- }
419
+ static assert (T.sizeof > size_t .sizeof && __traits(isIntegral, T));
420
+ static foreach (i; 0 .. T.sizeof / size_t .sizeof)
421
+ seed = hashOf(cast (size_t ) (val >>> (size_t .sizeof * 8 * i)), seed);
422
+ return seed;
391
423
}
392
424
}
393
425
394
426
// typeof(null) hash. CTFE supported
395
427
@trusted @nogc nothrow pure
396
- size_t hashOf (T)(scope const T val, size_t seed = 0 ) if (! is (T == enum ) && is (T : typeof (null )))
428
+ size_t hashOf (T)(scope const T val) if (! is (T == enum ) && is (T : typeof (null )))
397
429
{
398
- return hashOf (cast (void * )null , seed);
430
+ return 0 ;
431
+ }
432
+
433
+ // typeof(null) hash. CTFE supported
434
+ @trusted @nogc nothrow pure
435
+ size_t hashOf (T)(scope const T val, size_t seed) if (! is (T == enum ) && is (T : typeof (null )))
436
+ {
437
+ return hashOf (size_t (0 ), seed);
399
438
}
400
439
401
440
// Pointers hash. CTFE unsupported if not null
402
441
@trusted @nogc nothrow pure
403
- size_t hashOf (T)(scope const T val, size_t seed = 0 )
442
+ size_t hashOf (T)(scope const T val)
443
+ if (! is (T == enum ) && is (T V : V* ) && ! is (T : typeof (null ))
444
+ && ! is (T == struct ) && ! is (T == class ) && ! is (T == union ))
445
+ {
446
+ if (__ctfe)
447
+ {
448
+ if (val is null )
449
+ {
450
+ return 0 ;
451
+ }
452
+ else
453
+ {
454
+ assert (0 , " Unable to calculate hash of non-null pointer at compile time" );
455
+ }
456
+
457
+ }
458
+ auto addr = cast (size_t ) val;
459
+ return addr ^ (addr >>> 4 );
460
+ }
461
+
462
+ // Pointers hash. CTFE unsupported if not null
463
+ @trusted @nogc nothrow pure
464
+ size_t hashOf (T)(scope const T val, size_t seed)
404
465
if (! is (T == enum ) && is (T V : V* ) && ! is (T : typeof (null ))
405
466
&& ! is (T == struct ) && ! is (T == class ) && ! is (T == union ))
406
467
{
@@ -421,13 +482,14 @@ if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null))
421
482
422
483
private enum _hashOfStruct =
423
484
q{
485
+ enum bool isChained = is (typeof (seed) : size_t );
486
+ static if (! isChained) enum size_t seed = 0 ;
424
487
static if (hasCallableToHash! T) // CTFE depends on toHash()
425
488
{
426
- return hashOf (cast (size_t ) val.toHash(), seed);
427
- }
428
- else static if (T.tupleof.length == 1 )
429
- {
430
- return hashOf (val.tupleof[0 ], seed);
489
+ static if (isChained)
490
+ return hashOf (cast (size_t ) val.toHash(), seed);
491
+ else
492
+ return val.toHash();
431
493
}
432
494
else
433
495
{
436
498
pragma (msg, " Warning: struct " ~ __traits(identifier, T)~ " has method toHash, however it cannot be called with " ~ T.stringof~ " this." );
437
499
}
438
500
439
- static if (is (T == struct ) && ! canBitwiseHash! T)
501
+ static if (T.tupleof.length == 0 )
502
+ {
503
+ return seed;
504
+ }
505
+ else static if ((is (T == struct ) && ! canBitwiseHash! T) || T.tupleof.length == 1 )
440
506
{
441
507
static foreach (i, F; typeof (val.tupleof))
442
508
{
443
- seed = hashOf(val.tupleof[i], seed);
509
+ static if (i != 0 )
510
+ h = hashOf(val.tupleof[i], h);
511
+ else static if (isChained)
512
+ size_t h = hashOf(val.tupleof[i], seed);
513
+ else
514
+ size_t h = hashOf(val.tupleof[i]);
444
515
}
445
- return seed ;
516
+ return h ;
446
517
}
447
518
else static if (is (typeof (toUbyte(val)) == const (ubyte )[]))// CTFE ready for structs without reference fields
448
519
{
@@ -466,7 +537,15 @@ if (!is(T == enum) && (is(T == struct) || is(T == union))
466
537
}
467
538
468
539
// struct or union hash
469
- size_t hashOf (T)(auto ref T val, size_t seed = 0 )
540
+ size_t hashOf (T)(auto ref T val)
541
+ if (! is (T == enum ) && (is (T == struct ) || is (T == union ))
542
+ && ! canBitwiseHash! T)
543
+ {
544
+ mixin (_hashOfStruct);
545
+ }
546
+
547
+ // struct or union hash
548
+ size_t hashOf (T)(auto ref T val, size_t seed)
470
549
if (! is (T == enum ) && (is (T == struct ) || is (T == union ))
471
550
&& ! canBitwiseHash! T)
472
551
{
@@ -505,16 +584,39 @@ size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T
505
584
return bytesHashAlignedBy! T(bytes, seed);
506
585
}
507
586
508
- // class or interface hash. CTFE depends on toHash
509
- size_t hashOf (T)(scope const T val, size_t seed = 0 )
587
+ // address-based class hash. CTFE only if null.
588
+ @nogc nothrow pure @trusted
589
+ size_t hashOf (T)(scope const T val)
590
+ if (! is (T == enum ) && (is (T == interface ) || is (T == class ))
591
+ && isFinalClassWithAddressBasedHash! T)
592
+ {
593
+ if (__ctfe) if (val is null ) return 0 ;
594
+ return hashOf (cast (const void * ) val);
595
+ }
596
+
597
+ // address-based class hash. CTFE only if null.
598
+ @nogc nothrow pure @trusted
599
+ size_t hashOf (T)(scope const T val, size_t seed)
510
600
if (! is (T == enum ) && (is (T == interface ) || is (T == class ))
511
601
&& isFinalClassWithAddressBasedHash! T)
512
602
{
603
+ if (__ctfe) if (val is null ) return hashOf(size_t (0 ), seed);
513
604
return hashOf (cast (const void * ) val, seed);
514
605
}
515
606
516
607
// class or interface hash. CTFE depends on toHash
517
- size_t hashOf (T)(T val, size_t seed = 0 )
608
+ size_t hashOf (T)(T val)
609
+ if (! is (T == enum ) && (is (T == interface ) || is (T == class ))
610
+ && ! isFinalClassWithAddressBasedHash! T)
611
+ {
612
+ static if (__traits(compiles, {size_t h = val.toHash();}))
613
+ return val ? val.toHash() : 0 ;
614
+ else
615
+ return val ? (cast (Object )val).toHash() : 0 ;
616
+ }
617
+
618
+ // class or interface hash. CTFE depends on toHash
619
+ size_t hashOf (T)(T val, size_t seed)
518
620
if (! is (T == enum ) && (is (T == interface ) || is (T == class ))
519
621
&& ! isFinalClassWithAddressBasedHash! T)
520
622
{
@@ -525,9 +627,9 @@ if (!is(T == enum) && (is(T == interface) || is(T == class))
525
627
}
526
628
527
629
// associative array hash. CTFE depends on base types
528
- size_t hashOf (T)(T aa, size_t seed = 0 ) if (! is (T == enum ) && __traits(isAssociativeArray, T))
630
+ size_t hashOf (T)(T aa) if (! is (T == enum ) && __traits(isAssociativeArray, T))
529
631
{
530
- if (! aa.length) return hashOf( 0 , seed) ;
632
+ if (! aa.length) return 0 ;
531
633
size_t h = 0 ;
532
634
533
635
// The computed hash is independent of the foreach traversal order.
@@ -538,7 +640,13 @@ size_t hashOf(T)(T aa, size_t seed = 0) if (!is(T == enum) && __traits(isAssocia
538
640
hpair[1 ] = val.hashOf();
539
641
h += hpair.hashOf();
540
642
}
541
- return h.hashOf(seed);
643
+ return h;
644
+ }
645
+
646
+ // associative array hash. CTFE depends on base types
647
+ size_t hashOf (T)(T aa, size_t seed) if (! is (T == enum ) && __traits(isAssociativeArray, T))
648
+ {
649
+ return hashOf (hashOf(aa), seed);
542
650
}
543
651
544
652
unittest
0 commit comments