Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 711ab1c

Browse files
authored
Merge pull request #2246 from n8sh/core-hash-19071
Fix Issue 19071 - core.internal.hash should have non-chained toHash overloads merged-on-behalf-of: Sebastian Wilzbach <sebi.wilzbach@gmail.com>
2 parents df539c5 + 2f1275c commit 711ab1c

22 files changed

+234
-111
lines changed

src/core/internal/hash.d

Lines changed: 171 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ private template useScopeConstPassByValue(T)
147147
else static if (is(T == struct) || is(T == union))
148148
{
149149
// 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.
151153
}
152154
else static if (is(T : E[], E))
153155
{
@@ -157,8 +159,8 @@ private template useScopeConstPassByValue(T)
157159
else static if (T.length == 0)
158160
enum useScopeConstPassByValue = true;
159161
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]));
162164
}
163165
else static if (is(T : V[K], K, V))
164166
{
@@ -189,7 +191,21 @@ private template useScopeConstPassByValue(T)
189191
}
190192

191193
//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)
193209
if (is(T EType == enum) && useScopeConstPassByValue!EType)
194210
{
195211
static if (is(T EType == enum)) //for EType
@@ -273,10 +289,19 @@ if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T)
273289
size_t hashOf(T)(scope const T val, size_t seed = 0)
274290
if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T)
275291
&& !is(T == struct) && !is(T == class) && !is(T == union)
276-
&& canBitwiseHash!S)
292+
&& (__traits(isScalar, S) || canBitwiseHash!S))
277293
{
278294
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)[]))
280305
//ubyteble array (arithmetic types and structs without toHash) CTFE ready for arithmetic types and structs without reference fields
281306
{
282307
return bytesHashAlignedBy!ElementType(toUbyte(val), seed);
@@ -292,7 +317,7 @@ if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStati
292317
size_t hashOf(T)(T val, size_t seed = 0)
293318
if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T)
294319
&& !is(T == struct) && !is(T == class) && !is(T == union)
295-
&& !canBitwiseHash!S)
320+
&& !(__traits(isScalar, S) || canBitwiseHash!S))
296321
{
297322
size_t hash = seed;
298323
foreach (ref o; val)
@@ -316,7 +341,47 @@ if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStati
316341

317342
//arithmetic type hash
318343
@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))
320385
{
321386
static if(__traits(isFloating, val))
322387
{
@@ -351,56 +416,52 @@ size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && __tra
351416
}
352417
else
353418
{
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;
391423
}
392424
}
393425

394426
//typeof(null) hash. CTFE supported
395427
@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)))
397429
{
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);
399438
}
400439

401440
//Pointers hash. CTFE unsupported if not null
402441
@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)
404465
if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null))
405466
&& !is(T == struct) && !is(T == class) && !is(T == union))
406467
{
@@ -421,13 +482,14 @@ if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null))
421482

422483
private enum _hashOfStruct =
423484
q{
485+
enum bool isChained = is(typeof(seed) : size_t);
486+
static if (!isChained) enum size_t seed = 0;
424487
static if (hasCallableToHash!T) //CTFE depends on toHash()
425488
{
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();
431493
}
432494
else
433495
{
@@ -436,13 +498,22 @@ q{
436498
pragma(msg, "Warning: struct "~__traits(identifier, T)~" has method toHash, however it cannot be called with "~T.stringof~" this.");
437499
}
438500

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)
440506
{
441507
static foreach (i, F; typeof(val.tupleof))
442508
{
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]);
444515
}
445-
return seed;
516+
return h;
446517
}
447518
else static if (is(typeof(toUbyte(val)) == const(ubyte)[]))//CTFE ready for structs without reference fields
448519
{
@@ -466,7 +537,15 @@ if (!is(T == enum) && (is(T == struct) || is(T == union))
466537
}
467538

468539
//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)
470549
if (!is(T == enum) && (is(T == struct) || is(T == union))
471550
&& !canBitwiseHash!T)
472551
{
@@ -505,16 +584,39 @@ size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T
505584
return bytesHashAlignedBy!T(bytes, seed);
506585
}
507586

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)
510600
if (!is(T == enum) && (is(T == interface) || is(T == class))
511601
&& isFinalClassWithAddressBasedHash!T)
512602
{
603+
if (__ctfe) if (val is null) return hashOf(size_t(0), seed);
513604
return hashOf(cast(const void*) val, seed);
514605
}
515606

516607
//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)
518620
if (!is(T == enum) && (is(T == interface) || is(T == class))
519621
&& !isFinalClassWithAddressBasedHash!T)
520622
{
@@ -525,9 +627,9 @@ if (!is(T == enum) && (is(T == interface) || is(T == class))
525627
}
526628

527629
//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))
529631
{
530-
if (!aa.length) return hashOf(0, seed);
632+
if (!aa.length) return 0;
531633
size_t h = 0;
532634

533635
// 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
538640
hpair[1] = val.hashOf();
539641
h += hpair.hashOf();
540642
}
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);
542650
}
543651

544652
unittest

0 commit comments

Comments
 (0)