+2019-02-10 Iain Buclaw <ibuclaw@gdcproject.org>
+
+ * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Remove rt/util/hash.d
+ * libdruntime/Makefile.in: Rebuild.
+ * testsuite/libphobos.aa/aa.exp: New file.
+ * testsuite/libphobos.aa/test_aa.d: New test.
+ * testsuite/libphobos.hash/hash.exp: New file.
+ * testsuite/libphobos.hash/test_hash.d: New test.
+
2019-01-12 Iain Buclaw <ibuclaw@gdcproject.org>
* README.gcc: New file.
-f2db21937e650553066c30f1a9d5a7d08a1b3573
+cc215408bbdbc3324a95080aeef31287f663e57c
The first line of this file holds the git revision number of the last
merge done from the dlang/druntime repository.
rt/typeinfo/ti_ucent.d rt/typeinfo/ti_uint.d rt/typeinfo/ti_ulong.d \
rt/typeinfo/ti_ushort.d rt/typeinfo/ti_void.d rt/typeinfo/ti_wchar.d \
rt/util/array.d rt/util/container/array.d rt/util/container/common.d \
- rt/util/container/hashtab.d rt/util/container/treap.d rt/util/hash.d \
- rt/util/random.d rt/util/typeinfo.d rt/util/utf.d
+ rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
+ rt/util/typeinfo.d rt/util/utf.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
core/stdcpp/typeinfo.d
rt/typeinfo/ti_wchar.lo rt/util/array.lo \
rt/util/container/array.lo rt/util/container/common.lo \
rt/util/container/hashtab.lo rt/util/container/treap.lo \
- rt/util/hash.lo rt/util/random.lo rt/util/typeinfo.lo \
- rt/util/utf.lo
+ rt/util/random.lo rt/util/typeinfo.lo rt/util/utf.lo
am__objects_2 = gc/bits.lo gc/config.lo gc/gcinterface.lo \
gc/impl/conservative/gc.lo gc/impl/manual/gc.lo gc/os.lo \
gc/pooltable.lo gc/proxy.lo
rt/typeinfo/ti_ucent.d rt/typeinfo/ti_uint.d rt/typeinfo/ti_ulong.d \
rt/typeinfo/ti_ushort.d rt/typeinfo/ti_void.d rt/typeinfo/ti_wchar.d \
rt/util/array.d rt/util/container/array.d rt/util/container/common.d \
- rt/util/container/hashtab.d rt/util/container/treap.d rt/util/hash.d \
- rt/util/random.d rt/util/typeinfo.d rt/util/utf.d
+ rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
+ rt/util/typeinfo.d rt/util/utf.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
core/stdcpp/typeinfo.d
rt/util/container/common.lo: rt/util/container/$(am__dirstamp)
rt/util/container/hashtab.lo: rt/util/container/$(am__dirstamp)
rt/util/container/treap.lo: rt/util/container/$(am__dirstamp)
-rt/util/hash.lo: rt/util/$(am__dirstamp)
rt/util/random.lo: rt/util/$(am__dirstamp)
rt/util/typeinfo.lo: rt/util/$(am__dirstamp)
rt/util/utf.lo: rt/util/$(am__dirstamp)
}
}
-@trusted pure nothrow
+@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) ||
is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
{
ulong mantissa2 = parsed.mantissa2;
off_bytes--; // go back one, since mantissa only stored data in 56
// bits, ie 7 bytes
- for(; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes)
+ for (; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes)
{
buff[off_bytes] = cast(ubyte)mantissa2;
mantissa2 >>= 8;
}
}
-@safe pure nothrow
+@safe pure nothrow @nogc
private Float parse(bool is_denormalized = false, T)(T x) if (is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
{
return parse(x.im);
}
-@safe pure nothrow
+@safe pure nothrow @nogc
private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!T != FloatFormat.Real80)
{
Unqual!T x = x_;
}
}
-@safe pure nothrow
+@safe pure nothrow @nogc
private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80)
{
Unqual!T x = x_;
}
-@safe pure nothrow
+@safe pure nothrow @nogc
private real binPow2(int pow)
{
- static real binPosPow2(int pow) @safe pure nothrow
+ static real binPosPow2(int pow) @safe pure nothrow @nogc
{
assert(pow > 0);
//Need in CTFE, because CTFE float and double expressions computed more precisely that run-time expressions.
-@safe pure nothrow
+@safe pure nothrow @nogc
private ulong shiftrRound(ulong x)
{
return (x >> 1) + (x & 1);
}
-@safe pure nothrow
-private uint binLog2(T)(T x)
+@safe pure nothrow @nogc
+private uint binLog2(T)(const T x)
{
assert(x > 0);
int max = 2 ^^ (FloatTraits!T.EXPONENT-1)-1;
return max;
}
-@safe pure nothrow
+@safe pure nothrow @nogc
private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Real80)
{
x *= 2.0L^^FloatTraits!T.MANTISSA;
return Float(fl.mantissa >> pow, 0, sign);
}
-@safe pure nothrow
+@safe pure nothrow @nogc
private Float denormalizedMantissa(T)(T x, uint sign)
if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double)
{
return Float(shiftrRound(mant), 0, sign);
}
-@safe pure nothrow
+@safe pure nothrow @nogc
private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Quadruple)
{
x *= 2.0L^^FloatTraits!T.MANTISSA;
}
// all toUbyte functions must be evaluable at compile time
-@trusted pure nothrow
-const(ubyte)[] toUbyte(T)(T[] arr) if (T.sizeof == 1)
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof == 1)
{
return cast(const(ubyte)[])arr;
}
-@trusted pure nothrow
-const(ubyte)[] toUbyte(T)(T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyte)[])) && (T.sizeof > 1))
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof > 1)
{
if (__ctfe)
{
- const(ubyte)[] ret;
- foreach (cur; arr)
+ ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
+ static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases.
+ alias E = OriginalType!EType;
+ else
+ alias E = T;
+ static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null)))
+ {
+ size_t offset = 0;
+ foreach (ref cur; arr)
+ {
+ ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
+ offset += T.sizeof;
+ }
+ }
+ else
{
- ret ~= toUbyte(cur);
+ foreach (cur; arr)
+ assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
}
return ret;
}
}
}
-@trusted pure nothrow
-const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enum))
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
{
static if (T.sizeof == 1)
{
if (__ctfe)
{
- return cast(const(ubyte)[])[val];
+ ubyte[] result = ctfe_alloc(1);
+ result[0] = cast(ubyte) val;
+ return result;
}
else
{
}
else if (__ctfe)
{
- ubyte[T.sizeof] tmp;
+ ubyte[] tmp = ctfe_alloc(T.sizeof);
Unqual!T val_ = val;
for (size_t i = 0; i < T.sizeof; ++i)
{
tmp[idx] = cast(ubyte)(val_&0xff);
val_ >>= 8;
}
- return tmp[].dup;
+ return tmp;
}
else
{
}
}
-@trusted pure nothrow
-const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal))
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == __vector))
+{
+ if (!__ctfe)
+ return (cast(const ubyte*) &val)[0 .. T.sizeof];
+ else static if (is(typeof(val[0]) : void))
+ assert(0, "Unable to compute byte representation of " ~ T.stringof ~ " at compile time.");
+ else
+ {
+ // This code looks like it should work in CTFE but it segfaults:
+ // auto a = val.array;
+ // return toUbyte(a);
+ alias E = typeof(val[0]);
+ ubyte[] result = ctfe_alloc(T.sizeof);
+ for (size_t i = 0, j = 0; i < T.sizeof; i += E.sizeof, ++j)
+ {
+ result[i .. i + E.sizeof] = toUbyte(val[j]);
+ }
+ return result;
+ }
+}
+
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal))
{
if (__ctfe)
{
auto re = val.re;
auto im = val.im;
- return (re.toUbyte() ~ im.toUbyte());
+ auto a = re.toUbyte();
+ auto b = im.toUbyte();
+ ubyte[] result = ctfe_alloc(a.length + b.length);
+ result[0 .. a.length] = a[0 .. a.length];
+ result[a.length .. $] = b[0 .. b.length];
+ return result;
}
else
{
}
}
-@trusted pure nothrow
-const(ubyte)[] toUbyte(T)(ref T val) if (is(T == enum) && is(typeof(toUbyte(cast(V)val)) == const(ubyte)[]))
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == enum))
{
if (__ctfe)
{
static if (is(T V == enum)){}
- V e_val = val;
- return toUbyte(e_val);
+ return toUbyte(cast(const V) val);
}
else
{
}
}
-private bool isNonReference(T)()
+nothrow pure @safe unittest
{
- static if (is(T == struct) || is(T == union))
- {
- return isNonReferenceStruct!T();
- }
- else static if (__traits(isStaticArray, T))
- {
- return isNonReference!(typeof(T.init[0]))();
- }
- else static if (is(T E == enum))
- {
- return isNonReference!(E)();
- }
- else static if (!__traits(isScalar, T))
- {
- return false;
- }
- else static if (is(T V : V*))
- {
- return false;
- }
- else static if (is(T == function))
- {
- return false;
- }
- else
- {
- return true;
- }
+ // Issue 19008 - check toUbyte works on enums.
+ enum Month : uint { jan = 1}
+ Month m = Month.jan;
+ const bytes = toUbyte(m);
+ enum ctfe_works = (() => { Month x = Month.jan; return toUbyte(x).length > 0; })();
}
-private bool isNonReferenceStruct(T)() if (is(T == struct) || is(T == union))
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0)
{
- foreach (cur; T.init.tupleof)
+ if (__ctfe)
{
- static if (!isNonReference!(typeof(cur))()) return false;
+ if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
+ return ctfe_alloc(T.sizeof);
+ }
+ else
+ {
+ return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
}
-
- return true;
}
-@trusted pure nothrow
-const(ubyte)[] toUbyte(T)(ref T val) if (is(T == struct) || is(T == union))
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == struct) || is(T == union))
{
if (__ctfe)
{
- ubyte[T.sizeof] bytes;
- foreach (key, cur; val.tupleof)
+ ubyte[] bytes = ctfe_alloc(T.sizeof);
+ foreach (key, ref cur; val.tupleof)
{
- alias CUR_TYPE = typeof(cur);
- static if (isNonReference!(CUR_TYPE)())
- {
- bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + cur.sizeof] = toUbyte(cur)[];
- }
- else static if (is(typeof(val.tupleof[key] is null)))
+ static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
+ alias CurType = OriginalType!EType;
+ else
+ alias CurType = typeof(cur);
+ static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
{
- assert(val.tupleof[key] is null, "Unable to compute byte representation of non-null reference field at compile time");
- //skip, because val bytes are zeros
+ bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
}
else
{
- //pragma(msg, "is null: ", typeof(CUR_TYPE).stringof);
- assert(0, "Unable to compute byte representation of "~typeof(CUR_TYPE).stringof~" field at compile time");
+ assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time");
+ //skip, because val bytes are zeros
}
}
- return bytes[].dup;
+ return bytes;
}
else
{
return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
}
}
+
+// Strips off all `enum`s from type `T`.
+// Perhaps move to core.internal.types.
+private template OriginalType(T)
+{
+ static if (is(T EType == enum))
+ alias OriginalType = .OriginalType!EType;
+ else
+ alias OriginalType = T;
+}
module core.internal.hash;
import core.internal.convert;
+import core.internal.traits : allSatisfy;
+
+// If true ensure that positive zero and negative zero have the same hash.
+// Historically typeid(float).getHash did this but hashOf(float) did not.
+private enum floatCoalesceZeroes = true;
+// If true ensure that all NaNs of the same floating point type have the same hash.
+// Historically typeid(float).getHash didn't do this but hashOf(float) did.
+private enum floatCoalesceNaNs = true;
+
+// If either of the above are true then no struct or array that contains the
+// representation of a floating point number may be hashed with `bytesHash`.
+
+@nogc nothrow pure @safe unittest
+{
+ static if (floatCoalesceZeroes)
+ assert(hashOf(+0.0) == hashOf(-0.0)); // Same hash for +0.0 and -0.0.
+ static if (floatCoalesceNaNs)
+ assert(hashOf(double.nan) == hashOf(-double.nan)); // Same hash for different NaN.
+}
+
+private enum hasCallableToHash(T) = __traits(compiles,
+ {
+ size_t hash = ((T* x) => (*x).toHash())(null);
+ });
+
+@nogc nothrow pure @safe unittest
+{
+ static struct S { size_t toHash() { return 4; } }
+ assert(hasCallableToHash!S);
+ assert(!hasCallableToHash!(shared const S));
+}
+
+private enum isFinalClassWithAddressBasedHash(T) = __traits(isFinalClass, T)
+ // Use __traits(compiles, ...) in case there are multiple overloads of `toHash`.
+ && __traits(compiles, {static assert(&Object.toHash is &T.toHash);});
+
+@nogc nothrow pure @safe unittest
+{
+ static class C1 {}
+ final static class C2 : C1 {}
+ final static class C3 : C1 { override size_t toHash() const nothrow { return 1; }}
+ static assert(!isFinalClassWithAddressBasedHash!Object);
+ static assert(!isFinalClassWithAddressBasedHash!C1);
+ static assert(isFinalClassWithAddressBasedHash!C2);
+ static assert(!isFinalClassWithAddressBasedHash!C3);
+}
+
+/+
+Is it valid to calculate a hash code for T based on the bits of its
+representation? Always false for interfaces, dynamic arrays, and
+associative arrays. False for all classes except final classes that do
+not override `toHash`.
+
+Note: according to the spec as of
+https://github.com/dlang/dlang.org/commit/d66eff16491b0664c0fc00ba80a7aa291703f1f2
+the contents of unnamed paddings between fields is undefined. Currently
+this hashing implementation assumes that the padding contents (if any)
+for all instances of `T` are the same. The correctness of this
+assumption is yet to be verified.
++/
+private template canBitwiseHash(T)
+{
+ static if (is(T EType == enum))
+ enum canBitwiseHash = .canBitwiseHash!EType;
+ else static if (__traits(isFloating, T))
+ enum canBitwiseHash = !(floatCoalesceZeroes || floatCoalesceNaNs);
+ else static if (__traits(isScalar, T))
+ enum canBitwiseHash = true;
+ else static if (is(T == class))
+ {
+ enum canBitwiseHash = isFinalClassWithAddressBasedHash!T;
+ }
+ else static if (is(T == interface))
+ {
+ enum canBitwiseHash = false;
+ }
+ else static if (is(T == struct))
+ {
+ static if (hasCallableToHash!T || __traits(isNested, T))
+ enum canBitwiseHash = false;
+ else
+ enum canBitwiseHash = allSatisfy!(.canBitwiseHash, typeof(T.tupleof));
+ }
+ else static if (is(T == union))
+ {
+ // Right now we always bytewise hash unions that lack callable `toHash`.
+ enum canBitwiseHash = !hasCallableToHash!T;
+ }
+ else static if (is(T E : E[]))
+ {
+ static if (__traits(isStaticArray, T))
+ enum canBitwiseHash = (T.length == 0) || .canBitwiseHash!E;
+ else
+ enum canBitwiseHash = false;
+ }
+ else static if (__traits(isAssociativeArray, T))
+ {
+ enum canBitwiseHash = false;
+ }
+ else
+ {
+ static assert(is(T == delegate) || is(T : void) || is(T : typeof(null)),
+ "Internal error: unanticipated type "~T.stringof);
+ enum canBitwiseHash = true;
+ }
+}
+
+// Overly restrictive for simplicity: has false negatives but no false positives.
+private template useScopeConstPassByValue(T)
+{
+ static if (__traits(isScalar, T))
+ enum useScopeConstPassByValue = true;
+ else static if (is(T == class) || is(T == interface))
+ // Overly restrictive for simplicity.
+ enum useScopeConstPassByValue = isFinalClassWithAddressBasedHash!T;
+ else static if (is(T == struct) || is(T == union))
+ {
+ // Overly restrictive for simplicity.
+ enum useScopeConstPassByValue = T.sizeof <= (int[]).sizeof &&
+ __traits(isPOD, T) && // "isPOD" just to check there's no dtor or postblit.
+ canBitwiseHash!T; // We can't verify toHash doesn't leak.
+ }
+ else static if (is(T : E[], E))
+ {
+ static if (!__traits(isStaticArray, T))
+ // Overly restrictive for simplicity.
+ enum useScopeConstPassByValue = .useScopeConstPassByValue!E;
+ else static if (T.length == 0)
+ enum useScopeConstPassByValue = true;
+ else
+ enum useScopeConstPassByValue = T.sizeof <= (uint[]).sizeof
+ && .useScopeConstPassByValue!(typeof(T.init[0]));
+ }
+ else static if (is(T : V[K], K, V))
+ {
+ // Overly restrictive for simplicity.
+ enum useScopeConstPassByValue = .useScopeConstPassByValue!K
+ && .useScopeConstPassByValue!V;
+ }
+ else
+ {
+ static assert(is(T == delegate) || is(T : void) || is(T : typeof(null)),
+ "Internal error: unanticipated type "~T.stringof);
+ enum useScopeConstPassByValue = true;
+ }
+}
+
+@safe unittest
+{
+ static assert(useScopeConstPassByValue!int);
+ static assert(useScopeConstPassByValue!string);
+
+ static int ctr;
+ static struct S1 { ~this() { ctr++; } }
+ static struct S2 { this(this) { ctr++; } }
+ static assert(!useScopeConstPassByValue!S1,
+ "Don't default pass by value a struct with a non-vacuous destructor.");
+ static assert(!useScopeConstPassByValue!S2,
+ "Don't default pass by value a struct with a non-vacuous postblit.");
+}
+
+//enum hash. CTFE depends on base type
+size_t hashOf(T)(scope const T val)
+if (is(T EType == enum) && useScopeConstPassByValue!EType)
+{
+ static if (is(T EType == enum)) //for EType
+ {
+ return hashOf(cast(const EType) val);
+ }
+ else
+ {
+ static assert(0);
+ }
+}
//enum hash. CTFE depends on base type
-size_t hashOf(T)(auto ref T val, size_t seed = 0) if (is(T == enum))
+size_t hashOf(T)(scope const T val, size_t seed)
+if (is(T EType == enum) && useScopeConstPassByValue!EType)
+{
+ static if (is(T EType == enum)) //for EType
+ {
+ return hashOf(cast(const EType) val, seed);
+ }
+ else
+ {
+ static assert(0);
+ }
+}
+
+//enum hash. CTFE depends on base type
+size_t hashOf(T)(auto ref T val, size_t seed = 0)
+if (is(T EType == enum) && !useScopeConstPassByValue!EType)
{
static if (is(T EType == enum)) //for EType
{
}
}
-//CTFE ready (depends on base type). Can be merged with dynamic array hash
-size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && __traits(isStaticArray, T))
+//CTFE ready (depends on base type).
+size_t hashOf(T)(scope const auto ref T val, size_t seed = 0)
+if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T)
{
- size_t cur_hash = seed;
- foreach (ref cur; val)
+ // FIXME:
+ // We would like to to do this:
+ //
+ //static if (T.length == 0)
+ // return seed;
+ //else static if (T.length == 1)
+ // return hashOf(val[0], seed);
+ //else
+ // return bytesHashWithExactSizeAndAlignment!T(toUbyte(val), seed);
+ //
+ // ... but that's inefficient when using a runtime TypeInfo (introduces a branch)
+ // and PR #2243 wants typeid(T).getHash(&val) to produce the same result as
+ // hashOf(val).
+ static if (T.length == 0)
+ {
+ return bytesHashAlignedBy!size_t((ubyte[]).init, seed);
+ }
+ static if (is(typeof(toUbyte(val)) == const(ubyte)[]))
{
- cur_hash = hashOf(cur, cur_hash);
+ return bytesHashAlignedBy!T(toUbyte(val), seed);
+ }
+ else //Other types. CTFE unsupported
+ {
+ assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time");
+ return bytesHashAlignedBy!T((cast(const(ubyte)*) &val)[0 .. T.sizeof], seed);
}
- return cur_hash;
}
-//dynamic array hash
+//CTFE ready (depends on base type).
size_t hashOf(T)(auto ref T val, size_t seed = 0)
+if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T)
+{
+ // FIXME:
+ // We would like to to do this:
+ //
+ //static if (T.length == 0)
+ // return seed;
+ //else static if (T.length == 1)
+ // return hashOf(val[0], seed);
+ //else
+ // /+ hash like a dynamic array +/
+ //
+ // ... but that's inefficient when using a runtime TypeInfo (introduces a branch)
+ // and PR #2243 wants typeid(T).getHash(&val) to produce the same result as
+ // hashOf(val).
+ return hashOf(val[], seed);
+}
+
+//dynamic array hash
+size_t hashOf(T)(scope const T val, size_t seed = 0)
if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T)
- && !is(T == struct) && !is(T == class) && !is(T == union))
+ && !is(T == struct) && !is(T == class) && !is(T == union)
+ && (__traits(isScalar, S) || canBitwiseHash!S))
{
alias ElementType = typeof(val[0]);
- static if (is(ElementType == interface) || is(ElementType == class) ||
- ((is(ElementType == struct) || is(ElementType == union))
- && is(typeof(val[0].toHash()) == size_t)))
- //class or interface array or struct array with toHash(); CTFE depend on toHash() method
+ static if (!canBitwiseHash!ElementType)
{
size_t hash = seed;
- foreach (o; val)
+ foreach (ref o; val)
{
- hash = hashOf(o, hash);
+ hash = hashOf(hashOf(o), hash); // double hashing to match TypeInfo.getHash
}
return hash;
}
else static if (is(typeof(toUbyte(val)) == const(ubyte)[]))
//ubyteble array (arithmetic types and structs without toHash) CTFE ready for arithmetic types and structs without reference fields
{
- auto bytes = toUbyte(val);
- return bytesHash(bytes.ptr, bytes.length, seed);
+ return bytesHashAlignedBy!ElementType(toUbyte(val), seed);
}
else //Other types. CTFE unsupported
{
- assert(!__ctfe, "unable to compute hash of "~T.stringof);
- return bytesHash(val.ptr, ElementType.sizeof*val.length, seed);
+ assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time");
+ return bytesHashAlignedBy!ElementType((cast(const(ubyte)*) val.ptr)[0 .. ElementType.sizeof*val.length], seed);
+ }
+}
+
+//dynamic array hash
+size_t hashOf(T)(T val, size_t seed = 0)
+if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T)
+ && !is(T == struct) && !is(T == class) && !is(T == union)
+ && !(__traits(isScalar, S) || canBitwiseHash!S))
+{
+ size_t hash = seed;
+ foreach (ref o; val)
+ {
+ hash = hashOf(hashOf(o), hash); // double hashing because TypeInfo.getHash doesn't allow to pass seed value
}
+ return hash;
+}
+
+//arithmetic type hash
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val) if (!is(T == enum) && __traits(isArithmetic, T)
+ && __traits(isIntegral, T) && T.sizeof <= size_t.sizeof && !is(T == __vector))
+{
+ return val;
}
//arithmetic type hash
-@trusted nothrow pure
-size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && __traits(isArithmetic, T))
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && __traits(isArithmetic, T)
+ && __traits(isIntegral, T) && T.sizeof <= size_t.sizeof && !is(T == __vector))
+{
+ static if (size_t.sizeof < ulong.sizeof)
+ {
+ //MurmurHash3 32-bit single round
+ enum uint c1 = 0xcc9e2d51;
+ enum uint c2 = 0x1b873593;
+ enum uint c3 = 0xe6546b64;
+ enum uint r1 = 15;
+ enum uint r2 = 13;
+ }
+ else
+ {
+ //Half of MurmurHash3 64-bit single round
+ //(omits second interleaved update)
+ enum ulong c1 = 0x87c37b91114253d5;
+ enum ulong c2 = 0x4cf5ad432745937f;
+ enum ulong c3 = 0x52dce729;
+ enum uint r1 = 31;
+ enum uint r2 = 27;
+ }
+ size_t h = c1 * val;
+ h = (h << r1) | (h >>> (size_t.sizeof * 8 - r1));
+ h = (h * c2) ^ seed;
+ h = (h << r2) | (h >>> (size_t.sizeof * 8 - r2));
+ return h * 5 + c3;
+}
+
+//arithmetic type hash
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && __traits(isArithmetic, T)
+ && (!__traits(isIntegral, T) || T.sizeof > size_t.sizeof) && !is(T == __vector))
{
static if (__traits(isFloating, val))
{
- T data = (val != val) ? T.nan : val;
- auto bytes = toUbyte(data);
- return bytesHash(bytes.ptr, bytes.length, seed);
+ static if (floatCoalesceZeroes || floatCoalesceNaNs)
+ {
+ import core.internal.traits : Unqual;
+ Unqual!T data = val;
+ // +0.0 and -0.0 become the same.
+ static if (floatCoalesceZeroes && is(typeof(data = 0)))
+ if (data == 0) data = 0;
+ static if (floatCoalesceZeroes && is(typeof(data = 0.0i)))
+ if (data == 0.0i) data = 0.0i;
+ static if (floatCoalesceZeroes && is(typeof(data = 0.0 + 0.0i)))
+ {
+ if (data.re == 0.0) data = 0.0 + (data.im * 1.0i);
+ if (data.im == 0.0i) data = data.re + 0.0i;
+ }
+ static if (floatCoalesceNaNs)
+ if (data != data) data = T.nan; // All NaN patterns become the same.
+ }
+ else
+ {
+ alias data = val;
+ }
+
+ static if (T.mant_dig == float.mant_dig && T.sizeof == uint.sizeof)
+ return hashOf(*cast(const uint*) &data, seed);
+ else static if (T.mant_dig == double.mant_dig && T.sizeof == ulong.sizeof)
+ return hashOf(*cast(const ulong*) &data, seed);
+ else
+ return bytesHashWithExactSizeAndAlignment!T(toUbyte(data), seed);
}
else
{
- auto bytes = toUbyte(val);
- return bytesHash(bytes.ptr, bytes.length, seed);
+ static assert(T.sizeof > size_t.sizeof && __traits(isIntegral, T));
+ foreach (i; 0 .. T.sizeof / size_t.sizeof)
+ seed = hashOf(cast(size_t) (val >>> (size_t.sizeof * 8 * i)), seed);
+ return seed;
}
}
+size_t hashOf(T)(scope const auto ref T val, size_t seed = 0) @safe @nogc nothrow pure
+if (is(T == __vector) && !is(T == enum))
+{
+ static if (__traits(isFloating, T) && (floatCoalesceZeroes || floatCoalesceNaNs))
+ {
+ if (__ctfe)
+ {
+ // Workaround for CTFE bug.
+ alias E = Unqual!(typeof(val[0]));
+ E[T.sizeof / E.sizeof] array;
+ foreach (i; 0 .. T.sizeof / E.sizeof)
+ array[i] = val[i];
+ return hashOf(array, seed);
+ }
+ return hashOf(val.array, seed);
+ }
+ else
+ {
+ return bytesHashAlignedBy!T(toUbyte(val), seed);
+ }
+}
+
+//typeof(null) hash. CTFE supported
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val) if (!is(T == enum) && is(T : typeof(null)))
+{
+ return 0;
+}
+
//typeof(null) hash. CTFE supported
-@trusted nothrow pure
-size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && is(T : typeof(null)))
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && is(T : typeof(null)))
{
- return hashOf(cast(void*)null);
+ return hashOf(size_t(0), seed);
}
//Pointers hash. CTFE unsupported if not null
-@trusted nothrow pure
-size_t hashOf(T)(auto ref T val, size_t seed = 0)
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val)
if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null))
&& !is(T == struct) && !is(T == class) && !is(T == union))
{
{
if (val is null)
{
- return hashOf(cast(size_t)0);
+ return 0;
}
else
{
}
}
- return hashOf(cast(size_t)val);
+ auto addr = cast(size_t) val;
+ return addr ^ (addr >>> 4);
}
-//struct or union hash
-size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && (is(T == struct) || is(T == union)))
+//Pointers hash. CTFE unsupported if not null
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val, size_t seed)
+if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null))
+ && !is(T == struct) && !is(T == class) && !is(T == union))
{
- static if (is(typeof(val.toHash()) == size_t)) //CTFE depends on toHash()
+ if (__ctfe)
+ {
+ if (val is null)
+ {
+ return hashOf(cast(size_t)0, seed);
+ }
+ else
+ {
+ assert(0, "Unable to calculate hash of non-null pointer at compile time");
+ }
+
+ }
+ return hashOf(cast(size_t)val, seed);
+}
+
+private enum _hashOfStruct =
+q{
+ enum bool isChained = is(typeof(seed) : size_t);
+ static if (!isChained) enum size_t seed = 0;
+ static if (hasCallableToHash!T) //CTFE depends on toHash()
{
- return hashOf(val.toHash(), seed);
+ static if (isChained)
+ return hashOf(cast(size_t) val.toHash(), seed);
+ else
+ return val.toHash();
}
else
{
pragma(msg, "Warning: struct "~__traits(identifier, T)~" has method toHash, however it cannot be called with "~T.stringof~" this.");
}
- static if (is(typeof(toUbyte(val)) == const(ubyte)[]))//CTFE ready for structs without reference fields
+ static if (T.tupleof.length == 0)
+ {
+ return seed;
+ }
+ else static if ((is(T == struct) && !canBitwiseHash!T) || T.tupleof.length == 1)
{
- auto bytes = toUbyte(val);
- return bytesHash(bytes.ptr, bytes.length, seed);
+ size_t h = void;
+ static if (isChained) h = seed;
+ foreach (i, F; typeof(val.tupleof))
+ {
+ static if (__traits(isStaticArray, F))
+ {
+ static if (i == 0 && !isChained) h = 0;
+ static if (F.sizeof > 0 && canBitwiseHash!F)
+ // May use smallBytesHash instead of bytesHash.
+ h = bytesHashWithExactSizeAndAlignment!F(toUbyte(val.tupleof[i]), h);
+ else
+ // We can avoid the "double hashing" the top-level version uses
+ // for consistency with TypeInfo.getHash.
+ foreach (ref e; val.tupleof[i])
+ h = hashOf(e, h);
+ }
+ else static if (is(F == struct) || is(F == union))
+ {
+ static if (hasCallableToHash!F)
+ {
+ static if (i == 0 && !isChained)
+ h = val.tupleof[i].toHash();
+ else
+ h = hashOf(cast(size_t) val.tupleof[i].toHash(), h);
+ }
+ else static if (F.tupleof.length == 1)
+ {
+ // Handle the single member case separately to avoid unnecessarily using bytesHash.
+ static if (i == 0 && !isChained)
+ h = hashOf(val.tupleof[i].tupleof[0]);
+ else
+ h = hashOf(val.tupleof[i].tupleof[0], h);
+ }
+ else static if (canBitwiseHash!F)
+ {
+ // May use smallBytesHash instead of bytesHash.
+ static if (i == 0 && !isChained) h = 0;
+ h = bytesHashWithExactSizeAndAlignment!F(toUbyte(val.tupleof[i]), h);
+ }
+ else
+ {
+ // Nothing special happening.
+ static if (i == 0 && !isChained)
+ h = hashOf(val.tupleof[i]);
+ else
+ h = hashOf(val.tupleof[i], h);
+ }
+ }
+ else
+ {
+ // Nothing special happening.
+ static if (i == 0 && !isChained)
+ h = hashOf(val.tupleof[i]);
+ else
+ h = hashOf(val.tupleof[i], h);
+ }
+ }
+ return h;
}
- else // CTFE unsupproreted for structs with reference fields
+ else static if (is(typeof(toUbyte(val)) == const(ubyte)[]))//CTFE ready for structs without reference fields
{
- assert(!__ctfe, "unable to compute hash of "~T.stringof);
- const(ubyte)[] bytes = (cast(const(ubyte)*)&val)[0 .. T.sizeof];
- return bytesHash(bytes.ptr, bytes.length, seed);
+ // Not using bytesHashWithExactSizeAndAlignment here because
+ // the result may differ from typeid(T).hashOf(&val).
+ return bytesHashAlignedBy!T(toUbyte(val), seed);
+ }
+ else // CTFE unsupported
+ {
+ assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time");
+ const(ubyte)[] bytes = (() @trusted => (cast(const(ubyte)*)&val)[0 .. T.sizeof])();
+ // Not using bytesHashWithExactSizeAndAlignment here because
+ // the result may differ from typeid(T).hashOf(&val).
+ return bytesHashAlignedBy!T(bytes, seed);
}
}
+};
+
+//struct or union hash
+size_t hashOf(T)(scope const auto ref T val, size_t seed = 0)
+if (!is(T == enum) && (is(T == struct) || is(T == union))
+ && canBitwiseHash!T)
+{
+ mixin(_hashOfStruct);
+}
+
+//struct or union hash
+size_t hashOf(T)(auto ref T val)
+if (!is(T == enum) && (is(T == struct) || is(T == union))
+ && !canBitwiseHash!T)
+{
+ mixin(_hashOfStruct);
+}
+
+//struct or union hash
+size_t hashOf(T)(auto ref T val, size_t seed)
+if (!is(T == enum) && (is(T == struct) || is(T == union))
+ && !canBitwiseHash!T)
+{
+ mixin(_hashOfStruct);
}
//delegate hash. CTFE unsupported
-@trusted nothrow pure
-size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate))
+@trusted @nogc nothrow pure
+size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate))
{
- assert(!__ctfe, "unable to compute hash of "~T.stringof);
+ assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time");
const(ubyte)[] bytes = (cast(const(ubyte)*)&val)[0 .. T.sizeof];
- return bytesHash(bytes.ptr, bytes.length, seed);
+ return bytesHashWithExactSizeAndAlignment!T(bytes, seed);
+}
+
+//address-based class hash. CTFE only if null.
+@nogc nothrow pure @trusted
+size_t hashOf(T)(scope const T val)
+if (!is(T == enum) && (is(T == interface) || is(T == class))
+ && canBitwiseHash!T)
+{
+ if (__ctfe) if (val is null) return 0;
+ return hashOf(cast(const void*) val);
+}
+
+//address-based class hash. CTFE only if null.
+@nogc nothrow pure @trusted
+size_t hashOf(T)(scope const T val, size_t seed)
+if (!is(T == enum) && (is(T == interface) || is(T == class))
+ && canBitwiseHash!T)
+{
+ if (__ctfe) if (val is null) return hashOf(size_t(0), seed);
+ return hashOf(cast(const void*) val, seed);
}
//class or interface hash. CTFE depends on toHash
-size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && is(T == interface) || is(T == class))
+size_t hashOf(T)(T val)
+if (!is(T == enum) && (is(T == interface) || is(T == class))
+ && !canBitwiseHash!T)
{
- return hashOf(val ? (cast(Object)val).toHash() : 0, seed);
+ static if (__traits(compiles, {size_t h = val.toHash();}))
+ return val ? val.toHash() : 0;
+ else
+ return val ? (cast(Object)val).toHash() : 0;
+}
+
+//class or interface hash. CTFE depends on toHash
+size_t hashOf(T)(T val, size_t seed)
+if (!is(T == enum) && (is(T == interface) || is(T == class))
+ && !canBitwiseHash!T)
+{
+ static if (__traits(compiles, {size_t h = val.toHash();}))
+ return hashOf(val ? cast(size_t) val.toHash() : size_t(0), seed);
+ else
+ return hashOf(val ? (cast(Object)val).toHash() : 0, seed);
}
//associative array hash. CTFE depends on base types
-size_t hashOf(T)(auto ref T aa, size_t seed = 0) if (!is(T == enum) && __traits(isAssociativeArray, T))
+size_t hashOf(T)(T aa) if (!is(T == enum) && __traits(isAssociativeArray, T))
{
- if (!aa.length) return hashOf(0, seed);
+ static if (is(typeof(aa) : V[K], K, V)) {} // Put K & V in scope.
+ static if (__traits(compiles, (ref K k, ref V v) nothrow => .hashOf(k) + .hashOf(v)))
+ scope (failure) assert(0); // Allow compiler to infer nothrow.
+
+ if (!aa.length) return 0;
size_t h = 0;
// The computed hash is independent of the foreach traversal order.
size_t[2] hpair;
hpair[0] = key.hashOf();
hpair[1] = val.hashOf();
- h ^= hpair.hashOf();
+ h += hpair.hashOf();
}
- return h.hashOf(seed);
+ return h;
}
-unittest
+//associative array hash. CTFE depends on base types
+size_t hashOf(T)(T aa, size_t seed) if (!is(T == enum) && __traits(isAssociativeArray, T))
{
- static struct Foo
- {
- int a = 99;
- float b = 4.0;
- size_t toHash() const pure @safe nothrow
- {
- return a;
- }
- }
-
- static struct Bar
- {
- char c = 'x';
- int a = 99;
- float b = 4.0;
- void* d = null;
- }
-
- static struct Boom
- {
- char c = 'M';
- int* a = null;
- }
-
- interface IBoo
- {
- void boo();
- }
-
- static class Boo: IBoo
- {
- override void boo()
- {
- }
-
- override size_t toHash()
- {
- return 1;
- }
- }
-
- static struct Goo
- {
- size_t toHash() pure @safe nothrow
- {
- return 1;
- }
- }
-
- enum Gun: long
- {
- A = 99,
- B = 17
- }
-
- enum double dexpr = 3.14;
- enum float fexpr = 2.71;
- enum wstring wsexpr = "abcdef"w;
- enum string csexpr = "abcdef";
- enum int iexpr = 7;
- enum long lexpr = 42;
- enum int[2][3] saexpr = [[1, 2], [3, 4], [5, 6]];
- enum int[] daexpr = [7,8,9];
- enum Foo thsexpr = Foo();
- enum Bar vsexpr = Bar();
- enum int[int] aaexpr = [99:2, 12:6, 45:4];
- enum Gun eexpr = Gun.A;
- enum cdouble cexpr = 7+4i;
- enum Foo[] staexpr = [Foo(), Foo(), Foo()];
- enum Bar[] vsaexpr = [Bar(), Bar(), Bar()];
- enum realexpr = 7.88;
- enum raexpr = [8.99L+86i, 3.12L+99i, 5.66L+12i];
- enum nullexpr = null;
-
- //No CTFE:
- Boom rstructexpr = Boom();
- Boom[] rstrarrexpr = [Boom(), Boom(), Boom()];
- int delegate() dgexpr = (){return 78;};
- void* ptrexpr = &dgexpr;
-
-
- //CTFE hashes
- enum h1 = dexpr.hashOf();
- enum h2 = fexpr.hashOf();
- enum h3 = wsexpr.hashOf();
- enum h4 = csexpr.hashOf();
- enum h5 = iexpr.hashOf();
- enum h6 = lexpr.hashOf();
- enum h7 = saexpr.hashOf();
- enum h8 = daexpr.hashOf();
- enum h9 = thsexpr.hashOf();
- enum h10 = vsexpr.hashOf();
- enum h11 = aaexpr.hashOf();
- enum h12 = eexpr.hashOf();
- enum h13 = cexpr.hashOf();
- enum h14 = hashOf(new Boo);
- enum h15 = staexpr.hashOf();
- enum h16 = hashOf([new Boo, new Boo, new Boo]);
- enum h17 = hashOf([cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo]);
- enum h18 = hashOf(cast(IBoo)new Boo);
- enum h19 = vsaexpr.hashOf();
- enum h20 = hashOf(cast(Foo[3])staexpr);
-
- //BUG: cannot cast [Boo(), Boo(), Boo()][0] to object.Object at compile time
- auto h21 = hashOf(cast(Boo[3])[new Boo, new Boo, new Boo]);
- auto h22 = hashOf(cast(IBoo[3])[cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo]);
- enum h23 = hashOf(cast(Bar[3])vsaexpr);
-
- //NO CTFE (Compute, but don't check correctness):
- auto h24 = rstructexpr.hashOf();
- auto h25 = rstrarrexpr.hashOf();
- auto h26 = dgexpr.hashOf();
- auto h27 = ptrexpr.hashOf();
-
- enum h28 = realexpr.hashOf();
- enum h29 = raexpr.hashOf();
- enum h30 = nullexpr.hashOf();
-
- auto v1 = dexpr;
- auto v2 = fexpr;
- auto v3 = wsexpr;
- auto v4 = csexpr;
- auto v5 = iexpr;
- auto v6 = lexpr;
- auto v7 = saexpr;
- auto v8 = daexpr;
- auto v9 = thsexpr;
- auto v10 = vsexpr;
- auto v11 = aaexpr;
- auto v12 = eexpr;
- auto v13 = cexpr;
- auto v14 = new Boo;
- auto v15 = staexpr;
- auto v16 = [new Boo, new Boo, new Boo];
- auto v17 = [cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo];
- auto v18 = cast(IBoo)new Boo;
- auto v19 = vsaexpr;
- auto v20 = cast(Foo[3])staexpr;
- auto v21 = cast(Boo[3])[new Boo, new Boo, new Boo];
- auto v22 = cast(IBoo[3])[cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo];
- auto v23 = cast(Bar[3])vsaexpr;
- auto v30 = null;
-
- //NO CTFE:
- /*auto v24 = rstructexpr;
- auto v25 = rstrarrexpr;
- auto v26 = dgexpr;
- auto v27 = ptrexpr;
- auto v28 = realexpr;
- auto v29 = raexpr;*/
-
- //runtime hashes
- auto rth1 = hashOf(v1);
- auto rth2 = hashOf(v2);
- auto rth3 = hashOf(v3);
- auto rth4 = hashOf(v4);
- auto rth5 = hashOf(v5);
- auto rth6 = hashOf(v6);
- auto rth7 = hashOf(v7);
- auto rth8 = hashOf(v8);
- auto rth9 = hashOf(v9);
- auto rth10 = hashOf(v10);
- auto rth11 = hashOf(v11);
- auto rth12 = hashOf(v12);
- auto rth13 = hashOf(v13);
- auto rth14 = hashOf(v14);
- auto rth15 = hashOf(v15);
- auto rth16 = hashOf(v16);
- auto rth17 = hashOf(v17);
- auto rth18 = hashOf(v18);
- auto rth19 = hashOf(v19);
- auto rth20 = hashOf(v20);
- auto rth21 = hashOf(v21);
- auto rth22 = hashOf(v22);
- auto rth23 = hashOf(v23);
- auto rth30 = hashOf(v30);
- /*//NO CTFE:
- auto rth24 = hashOf(v24);
- auto rth25 = hashOf(v25);
- auto rth26 = hashOf(v26);
- auto rth27 = hashOf(v27);
- auto rth28 = hashOf(v28);
- auto rth29 = hashOf(v29);*/
-
- assert(h1 == rth1);
- assert(h2 == rth2);
- assert(h3 == rth3);
- assert(h4 == rth4);
- assert(h5 == rth5);
- assert(h6 == rth6);
- assert(h7 == rth7);
- assert(h8 == rth8);
- assert(h9 == rth9);
- assert(h10 == rth10);
- assert(h11 == rth11);
- assert(h12 == rth12);
- assert(h13 == rth13);
- assert(h14 == rth14);
- assert(h15 == rth15);
- assert(h16 == rth16);
- assert(h17 == rth17);
- assert(h18 == rth18);
- assert(h19 == rth19);
- assert(h20 == rth20);
- assert(h21 == rth21);
- assert(h22 == rth22);
- assert(h23 == rth23);
- /*assert(h24 == rth24);
- assert(h25 == rth25);
- assert(h26 == rth26);
- assert(h27 == rth27);
- assert(h28 == rth28);
- assert(h29 == rth29);*/
- assert(h30 == rth30);
-}
-
-
-unittest // issue 15111
-{
- void testAlias(T)()
- {
- static struct Foo
- {
- T t;
- alias t this;
- }
- Foo foo;
- static assert(is(typeof(hashOf(foo))));
- }
- // was fixed
- testAlias!(int[]);
- testAlias!(int*);
- // was not affected
- testAlias!int;
- testAlias!(void delegate());
- testAlias!(string[string]);
- testAlias!(int[8]);
+ return hashOf(hashOf(aa), seed);
}
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
-version (X86)
- version = AnyX86;
-version (X86_64)
- version = AnyX86;
+// This overload is for backwards compatibility.
+@system pure nothrow @nogc
+size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
+{
+ return bytesHashAlignedBy!ubyte((cast(const(ubyte)*) buf)[0 .. len], seed);
+}
-version (AnyX86)
+private template bytesHashAlignedBy(AlignType)
{
- version (DigitalMars)
- {
- }
- else
- {
- version = HasUnalignedOps;
- }
+ alias bytesHashAlignedBy = bytesHash!(AlignType.alignof >= uint.alignof);
}
+private template bytesHashWithExactSizeAndAlignment(SizeAndAlignType)
+{
+ static if (SizeAndAlignType.alignof < uint.alignof
+ ? SizeAndAlignType.sizeof <= 12
+ : SizeAndAlignType.sizeof <= 10)
+ alias bytesHashWithExactSizeAndAlignment = smallBytesHash;
+ else
+ alias bytesHashWithExactSizeAndAlignment = bytesHashAlignedBy!SizeAndAlignType;
+}
-@system pure nothrow @nogc
-size_t bytesHash(const(void)* buf, size_t len, size_t seed)
+// Fowler/Noll/Vo hash. http://www.isthe.com/chongo/tech/comp/fnv/
+private size_t fnv()(scope const(ubyte)[] bytes, size_t seed) @nogc nothrow pure @safe
{
- static uint rotl32(uint n)(in uint x) pure nothrow @safe @nogc
- {
- return (x << n) | (x >> (32 - n));
- }
+ static if (size_t.max <= uint.max)
+ enum prime = (1U << 24) + (1U << 8) + 0x93U;
+ else static if (size_t.max <= ulong.max)
+ enum prime = (1UL << 40) + (1UL << 8) + 0xb3UL;
+ else
+ enum prime = (size_t(1) << 88) + (size_t(1) << 8) + size_t(0x3b);
+ foreach (b; bytes)
+ seed = (seed ^ b) * prime;
+ return seed;
+}
+private alias smallBytesHash = fnv;
- //-----------------------------------------------------------------------------
- // Block read - if your platform needs to do endian-swapping or can only
- // handle aligned reads, do the conversion here
- static uint get32bits(const (ubyte)* x) pure nothrow @nogc
+//-----------------------------------------------------------------------------
+// Block read - if your platform needs to do endian-swapping or can only
+// handle aligned reads, do the conversion here
+private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
+{
+ version (BigEndian)
{
- //Compiler can optimize this code to simple *cast(uint*)x if it possible.
- version (HasUnalignedOps)
- {
- if (!__ctfe)
- return *cast(uint*)x; //BUG: Can't be inlined by DMD
- }
- version (BigEndian)
- {
- return ((cast(uint) x[0]) << 24) | ((cast(uint) x[1]) << 16) | ((cast(uint) x[2]) << 8) | (cast(uint) x[3]);
- }
- else
- {
- return ((cast(uint) x[3]) << 24) | ((cast(uint) x[2]) << 16) | ((cast(uint) x[1]) << 8) | (cast(uint) x[0]);
- }
+ return ((cast(uint) x[0]) << 24) | ((cast(uint) x[1]) << 16) | ((cast(uint) x[2]) << 8) | (cast(uint) x[3]);
}
-
- //-----------------------------------------------------------------------------
- // Finalization mix - force all bits of a hash block to avalanche
- static uint fmix32(uint h) pure nothrow @safe @nogc
+ else
{
- h ^= h >> 16;
- h *= 0x85ebca6b;
- h ^= h >> 13;
- h *= 0xc2b2ae35;
- h ^= h >> 16;
-
- return h;
+ return ((cast(uint) x[3]) << 24) | ((cast(uint) x[2]) << 16) | ((cast(uint) x[1]) << 8) | (cast(uint) x[0]);
}
+}
- auto data = cast(const(ubyte)*)buf;
+/+
+Params:
+ dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
++/
+@nogc nothrow pure @trusted
+private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
+{
+ auto len = bytes.length;
+ auto data = bytes.ptr;
auto nblocks = len / 4;
uint h1 = cast(uint)seed;
auto end_data = data+nblocks*uint.sizeof;
for (; data!=end_data; data += uint.sizeof)
{
- uint k1 = get32bits(data);
+ static if (dataKnownToBeAligned)
+ uint k1 = __ctfe ? get32bits(data) : *(cast(const uint*) data);
+ else
+ uint k1 = get32bits(data);
k1 *= c1;
- k1 = rotl32!15(k1);
+ k1 = (k1 << 15) | (k1 >> (32 - 15));
k1 *= c2;
h1 ^= k1;
- h1 = rotl32!13(h1);
+ h1 = (h1 << 13) | (h1 >> (32 - 13));
h1 = h1*5+c3;
}
case 3: k1 ^= data[2] << 16; goto case;
case 2: k1 ^= data[1] << 8; goto case;
case 1: k1 ^= data[0];
- k1 *= c1; k1 = rotl32!15(k1); k1 *= c2; h1 ^= k1;
+ k1 *= c1; k1 = (k1 << 15) | (k1 >> (32 - 15)); k1 *= c2; h1 ^= k1;
goto default;
default:
}
//----------
// finalization
h1 ^= len;
- h1 = fmix32(h1);
+ // Force all bits of the hash block to avalanche.
+ h1 = (h1 ^ (h1 >> 16)) * 0x85ebca6b;
+ h1 = (h1 ^ (h1 >> 13)) * 0xc2b2ae35;
+ h1 ^= h1 >> 16;
return h1;
}
enum test_str = "Sample string";
enum size_t hashVal = ctfeHash(test_str);
assert(hashVal == bytesHash(&test_str[0], test_str.length, 0));
+
+ // Detect unintended changes to bytesHash on unaligned and aligned inputs.
+ version (BigEndian)
+ {
+ const ubyte[7] a = [99, 4, 3, 2, 1, 5, 88];
+ const uint[2] b = [0x04_03_02_01, 0x05_ff_ff_ff];
+ }
+ else
+ {
+ const ubyte[7] a = [99, 1, 2, 3, 4, 5, 88];
+ const uint[2] b = [0x04_03_02_01, 0xff_ff_ff_05];
+ }
+ // It is okay to change the below values if you make a change
+ // that you expect to change the result of bytesHash.
+ assert(bytesHash(&a[1], a.length - 2, 0) == 2727459272);
+ assert(bytesHash(&b, 5, 0) == 2727459272);
+ assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == 2727459272);
}
enum dtorIsNothrow = is(typeof(function{T t=void;}) : void function() nothrow);
}
+/*
+Tests whether all given items satisfy a template predicate, i.e. evaluates to
+$(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])).
+*/
+package(core.internal)
+template allSatisfy(alias F, T...)
+{
+ static if (T.length == 0)
+ {
+ enum allSatisfy = true;
+ }
+ else static if (T.length == 1)
+ {
+ enum allSatisfy = F!(T[0]);
+ }
+ else
+ {
+ static if (allSatisfy!(F, T[0 .. $/2]))
+ enum allSatisfy = allSatisfy!(F, T[$/2 .. $]);
+ else
+ enum allSatisfy = false;
+ }
+}
+
template anySatisfy(alias F, T...)
{
static if (T.length == 0)
size_t toHash() @trusted nothrow
{
// BUG: this prevents a compacting GC from working, needs to be fixed
- return cast(size_t)cast(void*)this;
+ size_t addr = cast(size_t) cast(void*) this;
+ // The bottom log2((void*).alignof) bits of the address will always
+ // be 0. Moreover it is likely that each Object is allocated with a
+ // separate call to malloc. The alignment of malloc differs from
+ // platform to platform, but rather than having special cases for
+ // each platform it is safe to use a shift of 4. To minimize
+ // collisions in the low bits it is more important for the shift to
+ // not be too small than for the shift to not be too big.
+ return addr ^ (addr >>> 4);
}
/**
override size_t toHash() @trusted const nothrow
{
- import core.internal.traits : externDFunc;
- alias hashOf = externDFunc!("rt.util.hash.hashOf",
- size_t function(const(void)[], size_t) @trusted pure nothrow @nogc);
- return hashOf(this.toString(), 0);
+ return hashOf(this.toString());
}
override int opCmp(Object o)
* Bugs:
* fix https://issues.dlang.org/show_bug.cgi?id=12516 e.g. by changing this to a truly safe interface.
*/
- size_t getHash(in void* p) @trusted nothrow const { return cast(size_t)p; }
+ size_t getHash(scope const void* p) @trusted nothrow const
+ {
+ return hashOf(p);
+ }
/// Compares two instances for equality.
bool equals(in void* p1, in void* p2) const { return p1 == p2; }
this.base == c.base;
}
- override size_t getHash(in void* p) const { return base.getHash(p); }
+ override size_t getHash(scope const void* p) const { return base.getHash(p); }
override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); }
override @property size_t tsize() nothrow pure const { return base.tsize; }
return c && this.m_next == c.m_next;
}
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
- return cast(size_t)*cast(void**)p;
+ size_t addr = cast(size_t) *cast(const void**)p;
+ return addr ^ (addr >> 4);
}
override bool equals(in void* p1, in void* p2) const
return c && this.value == c.value;
}
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
void[] a = *cast(void[]*)p;
return getArrayHash(value, a.ptr, a.length);
this.value == c.value;
}
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return getArrayHash(value, p, len);
}
return !!_aaEqual(this, *cast(const void**) p1, *cast(const void**) p2);
}
- override hash_t getHash(in void* p) nothrow @trusted const
+ override hash_t getHash(scope const void* p) nothrow @trusted const
{
return _aaGetHash(cast(void*)p, this);
}
return c && this.base == c.base;
}
- override size_t getHash(in void* p) const { return base.getHash(p); }
+ override size_t getHash(scope const void* p) const { return base.getHash(p); }
override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); }
override @property size_t tsize() nothrow pure const { return base.tsize; }
return c && this.deco == c.deco;
}
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return hashOf(*cast(void delegate()*)p);
}
}
}
-unittest
-{
- // Bugzilla 15367
- void f1() {}
- void f2() {}
-
- // TypeInfo_Delegate.getHash
- int[void delegate()] aa;
- assert(aa.length == 0);
- aa[&f1] = 1;
- assert(aa.length == 1);
- aa[&f1] = 1;
- assert(aa.length == 1);
-
- auto a1 = [&f2, &f1];
- auto a2 = [&f2, &f1];
-
- // TypeInfo_Delegate.equals
- for (auto i = 0; i < 2; i++)
- assert(a1[i] == a2[i]);
- assert(a1 == a2);
-
- // TypeInfo_Delegate.compare
- for (auto i = 0; i < 2; i++)
- assert(a1[i] <= a2[i]);
- assert(a1 <= a2);
-}
-
/**
* Runtime type information about a class.
* Can be retrieved from an object instance by using the
return c && this.info.name == c.info.name;
}
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
auto o = *cast(Object*)p;
return o ? o.toHash() : 0;
return c && this.info.name == typeid(c).name;
}
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
+ if (!*cast(void**)p)
+ {
+ return 0;
+ }
Interface* pi = **cast(Interface ***)*cast(void**)p;
Object o = cast(Object)(*cast(void**)p - pi.offset);
assert(o);
this.initializer().length == s.initializer().length;
}
- override size_t getHash(in void* p) @trusted pure nothrow const
+ override size_t getHash(scope const void* p) @trusted pure nothrow const
{
assert(p);
if (xtoHash)
}
else
{
- import core.internal.traits : externDFunc;
- alias hashOf = externDFunc!("rt.util.hash.hashOf",
- size_t function(const(void)[], size_t) @trusted pure nothrow @nogc);
- return hashOf(p[0 .. initializer().length], 0);
+ return hashOf(p[0 .. initializer().length]);
}
}
return false;
}
- override size_t getHash(in void* p) const
+ override size_t getHash(scope const void* p) const
{
assert(0);
}
return base.opEquals(t.base);
}
- override size_t getHash(in void *p) const { return base.getHash(p); }
+ override size_t getHash(scope const void *p) const { return base.getHash(p); }
override bool equals(in void *p1, in void *p2) const { return base.equals(p1, p2); }
override int compare(in void *p1, in void *p2) const { return base.compare(p1, p2); }
override @property size_t tsize() nothrow pure const { return base.tsize; }
// size_t _aaLen(in void* p) pure nothrow @nogc;
private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow;
+ private void* _aaGetX(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey, out bool found) pure nothrow;
// inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey);
inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow;
inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow;
// int _aaApply2(void* aa, size_t keysize, _dg2_t dg);
private struct AARange { void* impl; size_t idx; }
- AARange _aaRange(void* aa) pure nothrow @nogc;
- bool _aaRangeEmpty(AARange r) pure nothrow @nogc;
- void* _aaRangeFrontKey(AARange r) pure nothrow @nogc;
- void* _aaRangeFrontValue(AARange r) pure nothrow @nogc;
- void _aaRangePopFront(ref AARange r) pure nothrow @nogc;
+ AARange _aaRange(void* aa) pure nothrow @nogc @safe;
+ bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe;
+ void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe;
+ void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe;
+ void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe;
int _aaEqual(in TypeInfo tiRaw, in void* e1, in void* e2);
hash_t _aaGetHash(in void* aa, in TypeInfo tiRaw) nothrow;
alias AssociativeArray(Key, Value) = Value[Key];
+/***********************************
+ * Removes all remaining keys and values from an associative array.
+ * Params:
+ * aa = The associative array.
+ */
void clear(T : Value[Key], Value, Key)(T aa)
{
_aaClear(*cast(void **) &aa);
}
+/* ditto */
void clear(T : Value[Key], Value, Key)(T* aa)
{
_aaClear(*cast(void **) aa);
}
+/***********************************
+ * Reorganizes the associative array in place so that lookups are more
+ * efficient.
+ * Params:
+ * aa = The associative array.
+ * Returns:
+ * The rehashed associative array.
+ */
T rehash(T : Value[Key], Value, Key)(T aa)
{
_aaRehash(cast(void**)&aa, typeid(Value[Key]));
return aa;
}
+/* ditto */
T rehash(T : Value[Key], Value, Key)(T* aa)
{
_aaRehash(cast(void**)aa, typeid(Value[Key]));
return *aa;
}
+/* ditto */
T rehash(T : shared Value[Key], Value, Key)(T aa)
{
_aaRehash(cast(void**)&aa, typeid(Value[Key]));
return aa;
}
+/* ditto */
T rehash(T : shared Value[Key], Value, Key)(T* aa)
{
_aaRehash(cast(void**)aa, typeid(Value[Key]));
return *aa;
}
+/***********************************
+ * Create a new associative array of the same size and copy the contents of the
+ * associative array into it.
+ * Params:
+ * aa = The associative array.
+ */
V[K] dup(T : V[K], K, V)(T aa)
{
//pragma(msg, "K = ", K, ", V = ", V);
return result;
}
+/* ditto */
V[K] dup(T : V[K], K, V)(T* aa)
{
return (*aa).dup;
}
-auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc
+// this should never be made public.
+private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
+{
+ // ensure we are dealing with a genuine AA.
+ static if (is(const(V[K]) == const(T)))
+ alias realAA = aa;
+ else
+ const(V[K]) realAA = aa;
+ return _aaRange(() @trusted { return cast(void*)realAA; } ());
+}
+
+/***********************************
+ * Returns a forward range over the keys of the associative array.
+ * Params:
+ * aa = The associative array.
+ * Returns:
+ * A forward range.
+ */
+auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
{
import core.internal.traits : substInout;
AARange r;
pure nothrow @nogc:
- @property bool empty() { return _aaRangeEmpty(r); }
- @property ref front() { return *cast(substInout!K*)_aaRangeFrontKey(r); }
- void popFront() { _aaRangePopFront(r); }
+ @property bool empty() @safe { return _aaRangeEmpty(r); }
+ @property ref front()
+ {
+ auto p = (() @trusted => cast(substInout!K*) _aaRangeFrontKey(r)) ();
+ return *p;
+ }
+ void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
}
- return Result(_aaRange(cast(void*)aa));
+ return Result(_aaToRange(aa));
}
+/* ditto */
auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
{
return (*aa).byKey();
}
-auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc
+/***********************************
+ * Returns a forward range over the values of the associative array.
+ * Params:
+ * aa = The associative array.
+ * Returns:
+ * A forward range.
+ */
+auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
{
import core.internal.traits : substInout;
AARange r;
pure nothrow @nogc:
- @property bool empty() { return _aaRangeEmpty(r); }
- @property ref front() { return *cast(substInout!V*)_aaRangeFrontValue(r); }
- void popFront() { _aaRangePopFront(r); }
+ @property bool empty() @safe { return _aaRangeEmpty(r); }
+ @property ref front()
+ {
+ auto p = (() @trusted => cast(substInout!V*) _aaRangeFrontValue(r)) ();
+ return *p;
+ }
+ void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
}
- return Result(_aaRange(cast(void*)aa));
+ return Result(_aaToRange(aa));
}
+/* ditto */
auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
{
return (*aa).byValue();
}
-auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc
+/***********************************
+ * Returns a forward range over the key value pairs of the associative array.
+ * Params:
+ * aa = The associative array.
+ * Returns:
+ * A forward range.
+ */
+auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
{
import core.internal.traits : substInout;
AARange r;
pure nothrow @nogc:
- @property bool empty() { return _aaRangeEmpty(r); }
- @property auto front() @trusted
+ @property bool empty() @safe { return _aaRangeEmpty(r); }
+ @property auto front()
{
static struct Pair
{
private void* keyp;
private void* valp;
- @property ref key() inout { return *cast(substInout!K*)keyp; }
- @property ref value() inout { return *cast(substInout!V*)valp; }
+ @property ref key() inout
+ {
+ auto p = (() @trusted => cast(substInout!K*) keyp) ();
+ return *p;
+ }
+ @property ref value() inout
+ {
+ auto p = (() @trusted => cast(substInout!V*) valp) ();
+ return *p;
+ }
}
return Pair(_aaRangeFrontKey(r),
_aaRangeFrontValue(r));
}
- void popFront() { _aaRangePopFront(r); }
+ void popFront() @safe { return _aaRangePopFront(r); }
@property Result save() { return this; }
}
- return Result(_aaRange(cast(void*)aa));
+ return Result(_aaToRange(aa));
}
+/* ditto */
auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
{
return (*aa).byKeyValue();
}
+/***********************************
+ * Returns a dynamic array, the elements of which are the keys in the
+ * associative array.
+ * Params:
+ * aa = The associative array.
+ * Returns:
+ * A dynamic array.
+ */
Key[] keys(T : Value[Key], Value, Key)(T aa) @property
{
auto a = cast(void[])_aaKeys(cast(inout(void)*)aa, Key.sizeof, typeid(Key[]));
return res;
}
+/* ditto */
Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
{
return (*aa).keys;
}
+/***********************************
+ * Returns a dynamic array, the elements of which are the values in the
+ * associative array.
+ * Params:
+ * aa = The associative array.
+ * Returns:
+ * A dynamic array.
+ */
Value[] values(T : Value[Key], Value, Key)(T aa) @property
{
auto a = cast(void[])_aaValues(cast(inout(void)*)aa, Key.sizeof, Value.sizeof, typeid(Value[]));
return res;
}
+/* ditto */
Value[] values(T : Value[Key], Value, Key)(T *aa) @property
{
return (*aa).values;
}
-unittest
-{
- static struct T
- {
- static size_t count;
- this(this) { ++count; }
- }
- T[int] aa;
- T t;
- aa[0] = t;
- aa[1] = t;
- assert(T.count == 2);
- auto vals = aa.values;
- assert(vals.length == 2);
- assert(T.count == 4);
-
- T.count = 0;
- int[T] aa2;
- aa2[t] = 0;
- assert(T.count == 1);
- aa2[t] = 1;
- assert(T.count == 1);
- auto keys = aa2.keys;
- assert(keys.length == 1);
- assert(T.count == 2);
-}
-
+/***********************************
+ * Looks up key; if it exists returns corresponding value else evaluates and
+ * returns defaultValue.
+ * Params:
+ * aa = The associative array.
+ * key = The key.
+ * defaultValue = The default value.
+ * Returns:
+ * The value.
+ */
inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue)
{
auto p = key in aa;
return p ? *p : defaultValue;
}
+/* ditto */
inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
{
return (*aa).get(key, defaultValue);
}
-pure nothrow unittest
+/***********************************
+ * Looks up key; if it exists returns corresponding value else evaluates
+ * value, adds it to the associative array and returns it.
+ * Params:
+ * aa = The associative array.
+ * key = The key.
+ * value = The required value.
+ * Returns:
+ * The value.
+ */
+ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
{
- int[int] a;
- foreach (i; a.byKey)
+ bool found;
+ // if key is @safe-ly copyable, `require` can infer @safe
+ static if (isSafeCopyable!K)
{
- assert(false);
+ auto p = () @trusted
+ {
+ return cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
+ } ();
}
- foreach (i; a.byValue)
+ else
{
- assert(false);
+ auto p = cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
}
+ return found ? *p : (*p = value);
}
-pure /*nothrow */ unittest
+// Constraints for aa update. Delegates, Functions or Functors (classes that
+// provide opCall) are allowed. See unittest for an example.
+private
{
- auto a = [ 1:"one", 2:"two", 3:"three" ];
- auto b = a.dup;
- assert(b == [ 1:"one", 2:"two", 3:"three" ]);
-
- int[] c;
- foreach (k; a.byKey)
+ template isCreateOperation(C, V)
{
- c ~= k;
- }
-
- assert(c.length == 3);
- assert(c[0] == 1 || c[1] == 1 || c[2] == 1);
- assert(c[0] == 2 || c[1] == 2 || c[2] == 2);
- assert(c[0] == 3 || c[1] == 3 || c[2] == 3);
-}
-
-pure nothrow unittest
-{
- // test for bug 5925
- const a = [4:0];
- const b = [4:0];
- assert(a == b);
-}
-
-pure nothrow unittest
-{
- // test for bug 9052
- static struct Json {
- Json[string] aa;
- void opAssign(Json) {}
- size_t length() const { return aa.length; }
- // This length() instantiates AssociativeArray!(string, const(Json)) to call AA.length(), and
- // inside ref Slot opAssign(Slot p); (which is automatically generated by compiler in Slot),
- // this.value = p.value would actually fail, because both side types of the assignment
- // are const(Json).
+ static if (is(C : V delegate()) || is(C : V function()))
+ enum bool isCreateOperation = true;
+ else static if (isCreateOperation!(typeof(&C.opCall), V))
+ enum bool isCreateOperation = true;
+ else
+ enum bool isCreateOperation = false;
}
-}
-
-pure nothrow unittest
-{
- // test for bug 8583: ensure Slot and aaA are on the same page wrt value alignment
- string[byte] aa0 = [0: "zero"];
- string[uint[3]] aa1 = [[1,2,3]: "onetwothree"];
- ushort[uint[3]] aa2 = [[9,8,7]: 987];
- ushort[uint[4]] aa3 = [[1,2,3,4]: 1234];
- string[uint[5]] aa4 = [[1,2,3,4,5]: "onetwothreefourfive"];
-
- assert(aa0.byValue.front == "zero");
- assert(aa1.byValue.front == "onetwothree");
- assert(aa2.byValue.front == 987);
- assert(aa3.byValue.front == 1234);
- assert(aa4.byValue.front == "onetwothreefourfive");
-}
-pure nothrow unittest
-{
- // test for bug 10720
- static struct NC
+ template isUpdateOperation(U, V)
{
- @disable this(this) { }
+ static if (is(U : V delegate(ref V)) || is(U : V function(ref V)))
+ enum bool isUpdateOperation = true;
+ else static if (isUpdateOperation!(typeof(&U.opCall), V))
+ enum bool isUpdateOperation = true;
+ else
+ enum bool isUpdateOperation = false;
}
-
- NC[string] aa;
- static assert(!is(aa.nonExistingField));
}
-pure nothrow unittest
-{
- // bug 5842
- string[string] test = null;
- test["test1"] = "test1";
- test.remove("test1");
- test.rehash;
- test["test3"] = "test3"; // causes divide by zero if rehash broke the AA
-}
+// Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
+private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
-pure nothrow unittest
+/***********************************
+ * Looks up key; if it exists applies the update delegate else evaluates the
+ * create delegate and adds it to the associative array
+ * Params:
+ * aa = The associative array.
+ * key = The key.
+ * create = The delegate to apply on create.
+ * update = The delegate to apply on update.
+ */
+void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
+if (isCreateOperation!(C, V) && isUpdateOperation!(U, V))
{
- string[] keys = ["a", "b", "c", "d", "e", "f"];
-
- // Test forward range capabilities of byKey
+ bool found;
+ // if key is @safe-ly copyable, `update` may infer @safe
+ static if (isSafeCopyable!K)
{
- int[string] aa;
- foreach (key; keys)
- aa[key] = 0;
-
- auto keyRange = aa.byKey();
- auto savedKeyRange = keyRange.save;
-
- // Consume key range once
- size_t keyCount = 0;
- while (!keyRange.empty)
- {
- aa[keyRange.front]++;
- keyCount++;
- keyRange.popFront();
- }
-
- foreach (key; keys)
+ auto p = () @trusted
{
- assert(aa[key] == 1);
- }
- assert(keyCount == keys.length);
-
- // Verify it's possible to iterate the range the second time
- keyCount = 0;
- while (!savedKeyRange.empty)
- {
- aa[savedKeyRange.front]++;
- keyCount++;
- savedKeyRange.popFront();
- }
-
- foreach (key; keys)
- {
- assert(aa[key] == 2);
- }
- assert(keyCount == keys.length);
+ return cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
+ } ();
}
-
- // Test forward range capabilities of byValue
- {
- size_t[string] aa;
- foreach (i; 0 .. keys.length)
- {
- aa[keys[i]] = i;
- }
-
- auto valRange = aa.byValue();
- auto savedValRange = valRange.save;
-
- // Consume value range once
- int[] hasSeen;
- hasSeen.length = keys.length;
- while (!valRange.empty)
- {
- assert(hasSeen[valRange.front] == 0);
- hasSeen[valRange.front]++;
- valRange.popFront();
- }
-
- foreach (sawValue; hasSeen) { assert(sawValue == 1); }
-
- // Verify it's possible to iterate the range the second time
- hasSeen = null;
- hasSeen.length = keys.length;
- while (!savedValRange.empty)
- {
- assert(!hasSeen[savedValRange.front]);
- hasSeen[savedValRange.front] = true;
- savedValRange.popFront();
- }
-
- foreach (sawValue; hasSeen) { assert(sawValue); }
- }
-}
-
-pure nothrow unittest
-{
- // expanded test for 5842: increase AA size past the point where the AA
- // stops using binit, in order to test another code path in rehash.
- int[int] aa;
- foreach (int i; 0 .. 32)
- aa[i] = i;
- foreach (int i; 0 .. 32)
- aa.remove(i);
- aa.rehash;
- aa[1] = 1;
-}
-
-pure nothrow unittest
-{
- // bug 13078
- shared string[][string] map;
- map.rehash;
-}
-
-pure nothrow unittest
-{
- // bug 11761: test forward range functionality
- auto aa = ["a": 1];
-
- void testFwdRange(R, T)(R fwdRange, T testValue)
- {
- assert(!fwdRange.empty);
- assert(fwdRange.front == testValue);
- static assert(is(typeof(fwdRange.save) == typeof(fwdRange)));
-
- auto saved = fwdRange.save;
- fwdRange.popFront();
- assert(fwdRange.empty);
-
- assert(!saved.empty);
- assert(saved.front == testValue);
- saved.popFront();
- assert(saved.empty);
- }
-
- testFwdRange(aa.byKey, "a");
- testFwdRange(aa.byValue, 1);
- //testFwdRange(aa.byPair, tuple("a", 1));
-}
-
-unittest
-{
- // Issue 9119
- int[string] aa;
- assert(aa.byKeyValue.empty);
-
- aa["a"] = 1;
- aa["b"] = 2;
- aa["c"] = 3;
-
- auto pairs = aa.byKeyValue;
-
- auto savedPairs = pairs.save;
- size_t count = 0;
- while (!pairs.empty)
- {
- assert(pairs.front.key in aa);
- assert(pairs.front.value == aa[pairs.front.key]);
- count++;
- pairs.popFront();
- }
- assert(count == aa.length);
-
- // Verify that saved range can iterate over the AA again
- count = 0;
- while (!savedPairs.empty)
- {
- assert(savedPairs.front.key in aa);
- assert(savedPairs.front.value == aa[savedPairs.front.key]);
- count++;
- savedPairs.popFront();
- }
- assert(count == aa.length);
-}
-
-unittest
-{
- // Verify iteration with const.
- auto aa = [1:2, 3:4];
- foreach (const t; aa.byKeyValue)
+ else
{
- auto k = t.key;
- auto v = t.value;
+ auto p = cast(V*) _aaGetX(cast(void**) &aa, typeid(V[K]), V.sizeof, &key, found);
}
+ if (!found)
+ *p = create();
+ else
+ *p = update(*p);
}
unittest
{
- // test for bug 14626
static struct S
{
- string[string] aa;
- inout(string) key() inout { return aa.byKey().front; }
- inout(string) val() inout { return aa.byValue().front; }
- auto keyval() inout { return aa.byKeyValue().front; }
+ int x;
+ @nogc nothrow pure:
+ this(this) @system {}
+
+ @safe const:
+ // stubs
+ bool opEquals(S rhs) { assert(0); }
+ size_t toHash() { assert(0); }
}
- S s = S(["a":"b"]);
- assert(s.key() == "a");
- assert(s.val() == "b");
- assert(s.keyval().key == "a");
- assert(s.keyval().value == "b");
+ int[string] aai;
+ static assert(is(typeof(() @safe { aai.require("a", 1234); })));
+ static assert(is(typeof(() @safe { aai.update("a", { return 1234; }, (ref int x) { x++; return x; }); })));
- void testInoutKeyVal(inout(string) key)
- {
- inout(string)[typeof(key)] aa;
+ S[string] aas;
+ static assert(is(typeof(() { aas.require("a", S(1234)); })));
+ static assert(is(typeof(() { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); })));
+ static assert(!is(typeof(() @safe { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); })));
- foreach (i; aa.byKey()) {}
- foreach (i; aa.byValue()) {}
- foreach (i; aa.byKeyValue()) {}
- }
-
- const int[int] caa;
- static assert(is(typeof(caa.byValue().front) == const int));
+ int[S] aais;
+ static assert(is(typeof(() { aais.require(S(1234), 1234); })));
+ static assert(is(typeof(() { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
+ static assert(!is(typeof(() @safe { aais.require(S(1234), 1234); })));
+ static assert(!is(typeof(() @safe { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
}
private void _destructRecurse(S)(ref S s)
return true;
}
-/**
-Calculates the hash value of $(D arg) with $(D seed) initial value.
-The result may not be equal to `typeid(T).getHash(&arg)`.
-The $(D seed) value may be used for hash chaining:
-----
-struct Test
+version (D_Ddoc)
{
- int a;
- string b;
- MyObject c;
+ // This lets DDoc produce better documentation.
+
+ /**
+ Calculates the hash value of `arg` with an optional `seed` initial value.
+ The result might not be equal to `typeid(T).getHash(&arg)`.
- size_t toHash() const @safe pure nothrow
+ Params:
+ arg = argument to calculate the hash value of
+ seed = optional `seed` value (may be used for hash chaining)
+
+ Return: calculated hash value of `arg`
+ */
+ size_t hashOf(T)(auto ref T arg, size_t seed)
+ {
+ static import core.internal.hash;
+ return core.internal.hash.hashOf(arg, seed);
+ }
+ /// ditto
+ size_t hashOf(T)(auto ref T arg)
{
- size_t hash = a.hashOf();
- hash = b.hashOf(hash);
- size_t h1 = c.myMegaHash();
- hash = h1.hashOf(hash); //Mix two hash values
- return hash;
+ static import core.internal.hash;
+ return core.internal.hash.hashOf(arg);
}
}
-----
-*/
-size_t hashOf(T)(auto ref T arg, size_t seed = 0)
+else
{
- import core.internal.hash;
- return core.internal.hash.hashOf(arg, seed);
+ public import core.internal.hash : hashOf;
}
unittest
}
import core.internal.traits : externDFunc;
- alias hashOf = externDFunc!("rt.util.hash.hashOf",
- size_t function(const(void)[], size_t) @trusted pure nothrow @nogc);
if (!hasCustomToHash(element))
- return hashOf(ptr[0 .. elementSize * count], 0);
+ return hashOf(ptr[0 .. elementSize * count]);
size_t hash = 0;
foreach (size_t i; 0 .. count)
- hash += element.getHash(ptr + i * elementSize);
+ hash = hashOf(element.getHash(ptr + i * elementSize), hash);
return hash;
}
-
-// Tests ensure TypeInfo_Array.getHash uses element hash functions instead of hashing array data
-
-unittest
-{
- class C
- {
- int i;
- this(in int i) { this.i = i; }
- override hash_t toHash() { return 0; }
- }
- C[] a1 = [new C(11)], a2 = [new C(12)];
- assert(typeid(C[]).getHash(&a1) == typeid(C[]).getHash(&a2));
-}
-
-unittest
-{
- struct S
- {
- int i;
- hash_t toHash() const @safe nothrow { return 0; }
- }
- S[] a1 = [S(11)], a2 = [S(12)];
- assert(typeid(S[]).getHash(&a1) == typeid(S[]).getHash(&a2));
-}
-
-@safe unittest
-{
- struct S
- {
- int i;
- const @safe nothrow:
- hash_t toHash() { return 0; }
- bool opEquals(const S) { return true; }
- int opCmp(const S) { return 0; }
- }
-
- int[S[]] aa = [[S(11)] : 13];
- assert(aa[[S(12)]] == 13);
-}
-
/// Provide the .dup array property.
@property auto dup(T)(T[] a)
if (!is(const(T) : T))
return used - deleted;
}
- @property size_t dim() const pure nothrow @nogc
+ @property size_t dim() const pure nothrow @nogc @safe
{
return buckets.length;
}
return hash == HASH_DELETED;
}
- @property bool filled() const
+ @property bool filled() const @safe
{
return cast(ptrdiff_t) hash < 0;
}
* If key was not in the aa, a mutable pointer to newly inserted value which
* is set to all zeros
*/
-extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, in size_t valsz,
- in void* pkey)
+extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti,
+ in size_t valsz, in void* pkey)
+{
+ bool found;
+ return _aaGetX(aa, ti, valsz, pkey, found);
+}
+
+/******************************
+ * Lookup *pkey in aa.
+ * Called only from implementation of require
+ * Params:
+ * aa = associative array opaque pointer
+ * ti = TypeInfo for the associative array
+ * valsz = ignored
+ * pkey = pointer to the key value
+ * found = true if the value was found
+ * Returns:
+ * if key was in the aa, a mutable pointer to the existing value.
+ * If key was not in the aa, a mutable pointer to newly inserted value which
+ * is set to all zeros
+ */
+extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti,
+ in size_t valsz, in void* pkey, out bool found)
{
// lazily alloc implementation
if (aa.impl is null)
// found a value => return it
if (auto p = aa.findSlotLookup(hash, pkey, ti.key))
+ {
+ found = true;
return p.entry + aa.valoff;
+ }
auto p = aa.findSlotInsert(hash);
if (p.deleted)
void* pkey = keys.ptr;
void* pval = vals.ptr;
immutable off = aa.valoff;
+ uint actualLength = 0;
foreach (_; 0 .. length)
{
immutable hash = calcHash(pkey, ti.key);
p.hash = hash;
p.entry = allocEntry(aa, pkey); // move key, no postblit
aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
+ actualLength++;
}
else if (aa.entryTI && hasDtor(ti.value))
{
pkey += keysz;
pval += valsz;
}
- aa.used = cast(uint) length;
+ aa.used = actualLength;
return aa;
}
auto uti = unqualify(tiRaw);
auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
immutable off = aa.valoff;
+ auto keyHash = &ti.key.getHash;
auto valHash = &ti.value.getHash;
size_t h;
{
if (!b.filled)
continue;
- size_t[2] h2 = [b.hash, valHash(b.entry + off)];
- // use XOR here, so that hash is independent of element order
- h ^= hashOf(h2);
+ size_t[2] h2 = [keyHash(b.entry), valHash(b.entry + off)];
+ // use addition here, so that hash is independent of element order
+ h += hashOf(h2);
}
+
return h;
}
alias impl this;
}
-extern (C) pure nothrow @nogc
+extern (C) pure nothrow @nogc @safe
{
Range _aaRange(AA aa)
{
bool _aaRangeEmpty(Range r)
{
- return r.impl is null || r.idx == r.dim;
+ return r.impl is null || r.idx >= r.dim;
}
void* _aaRangeFrontKey(Range r)
{
+ assert(!_aaRangeEmpty(r));
+ if (r.idx >= r.dim)
+ return null;
return r.buckets[r.idx].entry;
}
void* _aaRangeFrontValue(Range r)
{
- return r.buckets[r.idx].entry + r.valoff;
+ assert(!_aaRangeEmpty(r));
+ if (r.idx >= r.dim)
+ return null;
+
+ auto entry = r.buckets[r.idx].entry;
+ return entry is null ?
+ null :
+ (() @trusted { return entry + r.valoff; } ());
}
void _aaRangePopFront(ref Range r)
{
+ if (r.idx >= r.dim) return;
for (++r.idx; r.idx < r.dim; ++r.idx)
{
if (r.buckets[r.idx].filled)
}
}
-//==============================================================================
-// Unittests
-//------------------------------------------------------------------------------
-
-pure nothrow unittest
-{
- int[string] aa;
-
- assert(aa.keys.length == 0);
- assert(aa.values.length == 0);
-
- aa["hello"] = 3;
- assert(aa["hello"] == 3);
- aa["hello"]++;
- assert(aa["hello"] == 4);
-
- assert(aa.length == 1);
-
- string[] keys = aa.keys;
- assert(keys.length == 1);
- assert(keys[0] == "hello");
-
- int[] values = aa.values;
- assert(values.length == 1);
- assert(values[0] == 4);
-
- aa.rehash;
- assert(aa.length == 1);
- assert(aa["hello"] == 4);
-
- aa["foo"] = 1;
- aa["bar"] = 2;
- aa["batz"] = 3;
-
- assert(aa.keys.length == 4);
- assert(aa.values.length == 4);
-
- foreach (a; aa.keys)
- {
- assert(a.length != 0);
- assert(a.ptr != null);
- }
-
- foreach (v; aa.values)
- {
- assert(v != 0);
- }
-}
-
-unittest // Test for Issue 10381
-{
- alias II = int[int];
- II aa1 = [0 : 1];
- II aa2 = [0 : 1];
- II aa3 = [0 : 2];
- assert(aa1 == aa2); // Passes
- assert(typeid(II).equals(&aa1, &aa2));
- assert(!typeid(II).equals(&aa1, &aa3));
-}
-
-pure nothrow unittest
-{
- string[int] key1 = [1 : "true", 2 : "false"];
- string[int] key2 = [1 : "false", 2 : "true"];
- string[int] key3;
-
- // AA lits create a larger hashtable
- int[string[int]] aa1 = [key1 : 100, key2 : 200, key3 : 300];
-
- // Ensure consistent hash values are computed for key1
- assert((key1 in aa1) !is null);
-
- // Manually assigning to an empty AA creates a smaller hashtable
- int[string[int]] aa2;
- aa2[key1] = 100;
- aa2[key2] = 200;
- aa2[key3] = 300;
-
- assert(aa1 == aa2);
-
- // Ensure binary-independence of equal hash keys
- string[int] key2a;
- key2a[1] = "false";
- key2a[2] = "true";
-
- assert(aa1[key2a] == 200);
-}
-
-// Issue 9852
-pure nothrow unittest
-{
- // Original test case (revised, original assert was wrong)
- int[string] a;
- a["foo"] = 0;
- a.remove("foo");
- assert(a == null); // should not crash
-
- int[string] b;
- assert(b is null);
- assert(a == b); // should not deref null
- assert(b == a); // ditto
-
- int[string] c;
- c["a"] = 1;
- assert(a != c); // comparison with empty non-null AA
- assert(c != a);
- assert(b != c); // comparison with null AA
- assert(c != b);
-}
-
-// Bugzilla 14104
-unittest
-{
- import core.stdc.stdio;
-
- alias K = const(ubyte)*;
- size_t[K] aa;
- immutable key = cast(K)(cast(size_t) uint.max + 1);
- aa[key] = 12;
- assert(key in aa);
-}
-
-unittest
-{
- int[int] aa;
- foreach (k, v; aa)
- assert(false);
- foreach (v; aa)
- assert(false);
- assert(aa.byKey.empty);
- assert(aa.byValue.empty);
- assert(aa.byKeyValue.empty);
-
- size_t n;
- aa = [0 : 3, 1 : 4, 2 : 5];
- foreach (k, v; aa)
- {
- n += k;
- assert(k >= 0 && k < 3);
- assert(v >= 3 && v < 6);
- }
- assert(n == 3);
- n = 0;
-
- foreach (v; aa)
- {
- n += v;
- assert(v >= 3 && v < 6);
- }
- assert(n == 12);
-
- n = 0;
- foreach (k, v; aa)
- {
- ++n;
- break;
- }
- assert(n == 1);
-
- n = 0;
- foreach (v; aa)
- {
- ++n;
- break;
- }
- assert(n == 1);
-}
-
-unittest
-{
- int[int] aa;
- assert(!aa.remove(0));
- aa = [0 : 1];
- assert(aa.remove(0));
- assert(!aa.remove(0));
- aa[1] = 2;
- assert(!aa.remove(0));
- assert(aa.remove(1));
-
- assert(aa.length == 0);
- assert(aa.byKey.empty);
-}
-
-// test zero sized value (hashset)
-unittest
-{
- alias V = void[0];
- auto aa = [0 : V.init];
- assert(aa.length == 1);
- assert(aa.byKey.front == 0);
- assert(aa.byValue.front == V.init);
- aa[1] = V.init;
- assert(aa.length == 2);
- aa[0] = V.init;
- assert(aa.length == 2);
- assert(aa.remove(0));
- aa[0] = V.init;
- assert(aa.length == 2);
- assert(aa == [0 : V.init, 1 : V.init]);
-}
-
-// test tombstone purging
-unittest
-{
- int[int] aa;
- foreach (i; 0 .. 6)
- aa[i] = i;
- foreach (i; 0 .. 6)
- assert(aa.remove(i));
- foreach (i; 6 .. 10)
- aa[i] = i;
- assert(aa.length == 4);
- foreach (i; 6 .. 10)
- assert(i in aa);
-}
+// Most tests are now in in test_aa.d
// test postblit for AA literals
unittest
GC.runFinalizers((cast(char*)(&entryDtor))[0 .. 1]);
assert(T.dtor == 6 && T.postblit == 2);
}
-
-// for aa.clear
-pure nothrow unittest
-{
- int[int] aa;
- assert(aa.length == 0);
- foreach (i; 0 .. 100)
- aa[i] = i * 2;
- assert(aa.length == 100);
- auto aa2 = aa;
- assert(aa2.length == 100);
- aa.clear();
- assert(aa.length == 0);
- assert(aa2.length == 0);
-
- aa2[5] = 6;
- assert(aa.length == 1);
- assert(aa[5] == 6);
-}
-
-// test AA as key (Issue 16974)
-unittest
-{
- int[int] a = [1 : 2], a2 = [1 : 2];
-
- assert([a : 3] == [a : 3]);
- assert([a : 3] == [a2 : 3]);
-
- assert(typeid(a).getHash(&a) == typeid(a).getHash(&a));
- assert(typeid(a).getHash(&a) == typeid(a).getHash(&a2));
-}
override string toString() const { return (F[]).stringof; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return Array!F.hashOf(*cast(F[]*)p);
}
override string toString() const { return (F[]).stringof; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return Array!F.hashOf(*cast(F[]*)p);
}
override string toString() const { return (F[]).stringof; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return Array!F.hashOf(*cast(F[]*)p);
}
override string toString() const { return (F[]).stringof; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return Array!F.hashOf(*cast(F[]*)p);
}
override string toString() const { return (F[]).stringof; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return Array!F.hashOf(*cast(F[]*)p);
}
module rt.typeinfo.ti_Ag;
private import core.stdc.string;
-private import rt.util.hash;
private import core.internal.string;
// byte[]
override string toString() const { return "byte[]"; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
const s = *cast(const void[]*)p;
- return rt.util.hash.hashOf(s, 0);
+ return hashOf(s);
}
override bool equals(in void* p1, in void* p2) const
{
override string toString() const { return "char[]"; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
char[] s = *cast(char[]*)p;
- size_t hash = 0;
-
-version (all)
-{
- foreach (char c; s)
- hash = hash * 11 + c;
-}
-else
-{
- size_t len = s.length;
- char *str = s;
-
- while (1)
- {
- switch (len)
- {
- case 0:
- return hash;
-
- case 1:
- hash *= 9;
- hash += *cast(ubyte *)str;
- return hash;
-
- case 2:
- hash *= 9;
- hash += *cast(ushort *)str;
- return hash;
-
- case 3:
- hash *= 9;
- hash += (*cast(ushort *)str << 8) +
- (cast(ubyte *)str)[2];
- return hash;
-
- default:
- hash *= 9;
- hash += *cast(uint *)str;
- str += 4;
- len -= 4;
- break;
- }
- }
-}
- return hash;
+ return hashOf(s);
}
override @property inout(TypeInfo) next() inout
module rt.typeinfo.ti_Aint;
private import core.stdc.string;
-private import rt.util.hash;
extern (C) void[] _adSort(void[] a, TypeInfo ti);
override string toString() const { return "int[]"; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
- const s = *cast(const int[]*)p;
- return rt.util.hash.hashOf(s, 0);
+ // Hash as if unsigned.
+ const s = *cast(const uint[]*)p;
+ return hashOf(s);
}
override bool equals(in void* p1, in void* p2) const
module rt.typeinfo.ti_Along;
private import core.stdc.string;
-private import rt.util.hash;
// long[]
override string toString() const { return "long[]"; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
- const s = *cast(const long[]*)p;
- return rt.util.hash.hashOf(s, 0);
+ // Hash as if unsigned.
+ const s = *cast(const ulong[]*)p;
+ return hashOf(s);
}
override bool equals(in void* p1, in void* p2) const
override string toString() const { return (F[]).stringof; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
return Array!F.hashOf(*cast(F[]*)p);
}
module rt.typeinfo.ti_Ashort;
private import core.stdc.string;
-private import rt.util.hash;
// short[]
override string toString() const { return "short[]"; }
- override size_t getHash(in void* p) @trusted const
+ override size_t getHash(scope const void* p) @trusted const
{
- const s = *cast(const short[]*)p;
- return rt.util.hash.hashOf(s, 0);
+ // Hash as if unsigned.
+ const s = *cast(const ushort[]*)p;
+ return hashOf(s);
}
override bool equals(in void* p1, in void* p2) const
//pure:
//nothrow:
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
Object o = *cast(Object*)p;
return o ? o.toHash() : 0;
override string toString() const pure nothrow @safe { return "byte"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(byte *)p;
+ return *cast(const byte *)p;
}
override bool equals(in void* p1, in void* p2)
override string toString() const { return F.stringof; }
- override size_t getHash(in void* p) const @trusted
+ override size_t getHash(scope const void* p) const @trusted
{
return Floating!F.hashOf(*cast(F*)p);
}
*/
module rt.typeinfo.ti_cent;
-private import rt.util.hash;
-
static if (is(cent)):
// cent
override string toString() const pure nothrow @safe { return "cent"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return rt.util.hash.hashOf(p[0 .. cent.sizeof], 0);
+ // cent & ucent hash the same if ucent.sizeof >= size_t.sizeof.
+ return hashOf(*cast(const ucent*) p);
}
override bool equals(in void* p1, in void* p2)
override string toString() const { return F.stringof; }
- override size_t getHash(in void* p) const @trusted
+ override size_t getHash(scope const void* p) const @trusted
{
return Floating!F.hashOf(*cast(F*)p);
}
override string toString() const pure nothrow @safe { return "char"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(char *)p;
+ return *cast(const char *)p;
}
override bool equals(in void* p1, in void* p2)
override string toString() const { return F.stringof; }
- override size_t getHash(in void* p) const @trusted
+ override size_t getHash(scope const void* p) const @trusted
{
return Floating!F.hashOf(*cast(F*)p);
}
override string toString() const pure nothrow @safe { return "dchar"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(dchar *)p;
+ return *cast(const dchar *)p;
}
override bool equals(in void* p1, in void* p2)
*/
module rt.typeinfo.ti_delegate;
-private import rt.util.hash;
// delegate
pure:
nothrow:
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return rt.util.hash.hashOf(p[0 .. dg.sizeof], 0);
+ return hashOf(*cast(dg*)p);
}
override bool equals(in void* p1, in void* p2)
override string toString() const { return F.stringof; }
- override size_t getHash(in void* p) const @trusted
+ override size_t getHash(scope const void* p) const @trusted
{
return Floating!F.hashOf(*cast(F*)p);
}
override string toString() const { return F.stringof; }
- override size_t getHash(in void* p) const @trusted
+ override size_t getHash(scope const void* p) const @trusted
{
return Floating!F.hashOf(*cast(F*)p);
}
override string toString() const pure nothrow @safe { return "int"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(uint *)p;
+ return *cast(const int *)p;
}
override bool equals(in void* p1, in void* p2)
*/
module rt.typeinfo.ti_long;
-private import rt.util.hash;
-
// long
class TypeInfo_l : TypeInfo
override string toString() const pure nothrow @safe { return "long"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return rt.util.hash.hashOf(p[0 .. long.sizeof], 0);
+ static if (ulong.sizeof <= size_t.sizeof)
+ return *cast(const long*)p;
+ else
+ // long & ulong hash the same if ulong.sizeof > size_t.sizeof.
+ return hashOf(*cast(const ulong*)p);
}
override bool equals(in void* p1, in void* p2)
{
override string toString() const @safe { return "typeof(null)"; }
- override size_t getHash(in void* p) const
+ override size_t getHash(scope const void* p) const
{
return 0;
}
pure:
nothrow:
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return cast(size_t)*cast(void**)p;
+ size_t addr = cast(size_t) *cast(const void**)p;
+ return addr ^ (addr >> 4);
}
override bool equals(in void* p1, in void* p2)
override string toString() const { return F.stringof; }
- override size_t getHash(in void* p) const @trusted
+ override size_t getHash(scope const void* p) const @trusted
{
return Floating!F.hashOf(*cast(F*)p);
}
override string toString() const pure nothrow @safe { return "short"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(short *)p;
+ return *cast(const short *)p;
}
override bool equals(in void* p1, in void* p2)
override string toString() const pure nothrow @safe { return "ubyte"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(ubyte *)p;
+ return *cast(const ubyte *)p;
}
override bool equals(in void* p1, in void* p2)
*/
module rt.typeinfo.ti_ucent;
-private import rt.util.hash;
-
static if (is(ucent)):
// ucent
override string toString() const pure nothrow @safe { return "ucent"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return rt.util.hash.hashOf(p[0 .. ucent.sizeof], 0);
+ return hashOf(*cast(const ucent*) p);
}
override bool equals(in void* p1, in void* p2)
override string toString() const pure nothrow @safe { return "uint"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(uint *)p;
+ return *cast(const uint *)p;
}
override bool equals(in void* p1, in void* p2)
*/
module rt.typeinfo.ti_ulong;
-private import rt.util.hash;
// ulong
override string toString() const pure nothrow @safe { return "ulong"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return rt.util.hash.hashOf(p[0 .. ulong.sizeof], 0);
+ static if (ulong.sizeof <= size_t.sizeof)
+ return *cast(const ulong*)p;
+ else
+ return hashOf(*cast(const ulong*)p);
}
override bool equals(in void* p1, in void* p2)
override string toString() const pure nothrow @safe { return "ushort"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(ushort *)p;
+ return *cast(const ushort *)p;
}
override bool equals(in void* p1, in void* p2)
override string toString() const pure nothrow @safe { return "void"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
assert(0);
}
override string toString() { return "wchar"; }
- override size_t getHash(in void* p)
+ override size_t getHash(scope const void* p)
{
- return *cast(wchar *)p;
+ return *cast(const wchar *)p;
}
override bool equals(in void* p1, in void* p2)
static hash_t hashOf(in ref Key key) @trusted
{
- import rt.util.hash : hashOf;
static if (is(Key U : U[]))
- return hashOf(key, 0);
+ return .hashOf(key, 0);
else
- return hashOf((&key)[0 .. 1], 0);
+ return .hashOf((&key)[0 .. 1], 0);
}
@property hash_t mask() const
+++ /dev/null
-/**
- * The default hash implementation.
- *
- * Copyright: Copyright Sean Kelly 2009 - 2016.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Sean Kelly
- * Source: $(DRUNTIMESRC src/rt/util/_hash.d)
- */
-module rt.util.hash;
-
-
-version (X86)
- version = AnyX86;
-version (X86_64)
- version = AnyX86;
-version (AnyX86)
- version = HasUnalignedOps;
-
-
-@trusted pure nothrow @nogc
-size_t hashOf( const(void)[] buf, size_t seed )
-{
- /*
- * This is Paul Hsieh's SuperFastHash algorithm, described here:
- * http://www.azillionmonkeys.com/qed/hash.html
- * It is protected by the following open source license:
- * http://www.azillionmonkeys.com/qed/weblicense.html
- */
- static uint get16bits( const (ubyte)* x ) pure nothrow @nogc
- {
- // CTFE doesn't support casting ubyte* -> ushort*, so revert to
- // per-byte access when in CTFE.
- version (HasUnalignedOps)
- {
- if (!__ctfe)
- return *cast(ushort*) x;
- }
-
- return ((cast(uint) x[1]) << 8) + (cast(uint) x[0]);
- }
-
- // NOTE: SuperFastHash normally starts with a zero hash value. The seed
- // value was incorporated to allow chaining.
- auto data = cast(const(ubyte)*) buf.ptr;
- auto len = buf.length;
- auto hash = seed;
-
- if ( len == 0 || data is null )
- return 0;
-
- int rem = len & 3;
- len >>= 2;
-
- for ( ; len > 0; len-- )
- {
- hash += get16bits( data );
- auto tmp = (get16bits( data + 2 ) << 11) ^ hash;
- hash = (hash << 16) ^ tmp;
- data += 2 * ushort.sizeof;
- hash += hash >> 11;
- }
-
- switch ( rem )
- {
- case 3: hash += get16bits( data );
- hash ^= hash << 16;
- hash ^= data[ushort.sizeof] << 18;
- hash += hash >> 11;
- break;
- case 2: hash += get16bits( data );
- hash ^= hash << 11;
- hash += hash >> 17;
- break;
- case 1: hash += *data;
- hash ^= hash << 10;
- hash += hash >> 1;
- break;
- default:
- break;
- }
-
- /* Force "avalanching" of final 127 bits */
- hash ^= hash << 3;
- hash += hash >> 5;
- hash ^= hash << 4;
- hash += hash >> 17;
- hash ^= hash << 25;
- hash += hash >> 6;
-
- return hash;
-}
-
-unittest
-{
- enum test_str = "Sample string";
- size_t hashval = hashOf(test_str, 5);
-
- //import core.stdc.stdio;
- //printf("hashval = %lld\n", cast(long)hashval);
-
- if (hashval.sizeof == 4)
- assert(hashval == 528740845);
- else if (hashval.sizeof == 8)
- assert(hashval == 8106800467257150594L);
- else
- assert(0);
-}
* Authors: Kenji Hara
*/
module rt.util.typeinfo;
+static import core.internal.hash;
template Floating(T)
if (is(T == float) || is(T == double) || is(T == real))
return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1);
}
- size_t hashOf(T value) @trusted
- {
- if (value == 0) // +0.0 and -0.0
- value = 0;
-
- static if (is(T == float)) // special case?
- return *cast(uint*)&value;
- else
- {
- import rt.util.hash;
- return rt.util.hash.hashOf((&value)[0 .. 1], 0);
- }
- }
+ public alias hashOf = core.internal.hash.hashOf;
}
template Floating(T)
if (is(T == cfloat) || is(T == cdouble) || is(T == creal))
return result;
}
- size_t hashOf(T value) @trusted
- {
- if (value == 0 + 0i)
- value = 0 + 0i;
- import rt.util.hash;
- return rt.util.hash.hashOf((&value)[0 .. 1], 0);
- }
+ public alias hashOf = core.internal.hash.hashOf;
}
template Array(T)
return 0;
}
- size_t hashOf(T[] value)
- {
- size_t h = 0;
- foreach (e; value)
- h += Floating!T.hashOf(e);
- return h;
- }
+ public alias hashOf = core.internal.hash.hashOf;
}
version (unittest)
{
assert(f1 == 0 + 0i);
- assert(f1 == f2);
+ assert(f1 == f2);
assert(f1 !is f2);
ti = typeid(F);
assert(ti.getHash(&f1) == ti.getHash(&f2));
--- /dev/null
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+load_lib libphobos-dg.exp
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+# Main loop.
+dg-runtest $tests "" $DEFAULT_DFLAGS
+
+# All done.
+dg-finish
--- /dev/null
+void main()
+{
+ testKeysValues1();
+ testKeysValues2();
+ testGet1();
+ testGet2();
+ testRequire1();
+ testRequire2();
+ testRequire3();
+ testUpdate1();
+ testUpdate2();
+ testByKey1();
+ testByKey2();
+ testByKey3();
+ testByKey4();
+ issue5842();
+ issue5842Expanded();
+ issue5925();
+ issue8583();
+ issue9052();
+ issue9119();
+ issue9852();
+ issue10381();
+ issue10720();
+ issue11761();
+ issue13078();
+ issue14104();
+ issue14626();
+ issue15290();
+ issue15367();
+ issue16974();
+ issue18071();
+ testIterationWithConst();
+ testStructArrayKey();
+ miscTests1();
+ miscTests2();
+ testRemove();
+ testZeroSizedValue();
+ testTombstonePurging();
+ testClear();
+}
+
+void testKeysValues1()
+{
+ static struct T
+ {
+ byte b;
+ static size_t count;
+ this(this) { ++count; }
+ }
+ T[int] aa;
+ T t;
+ aa[0] = t;
+ aa[1] = t;
+ assert(T.count == 2);
+ auto vals = aa.values;
+ assert(vals.length == 2);
+ assert(T.count == 4);
+
+ T.count = 0;
+ int[T] aa2;
+ aa2[t] = 0;
+ assert(T.count == 1);
+ aa2[t] = 1;
+ assert(T.count == 1);
+ auto keys = aa2.keys;
+ assert(keys.length == 1);
+ assert(T.count == 2);
+}
+
+void testKeysValues2() nothrow pure
+{
+ int[string] aa;
+
+ assert(aa.keys.length == 0);
+ assert(aa.values.length == 0);
+
+ aa["hello"] = 3;
+ assert(aa["hello"] == 3);
+ aa["hello"]++;
+ assert(aa["hello"] == 4);
+
+ assert(aa.length == 1);
+
+ string[] keys = aa.keys;
+ assert(keys.length == 1);
+ assert(keys[0] == "hello");
+
+ int[] values = aa.values;
+ assert(values.length == 1);
+ assert(values[0] == 4);
+
+ aa.rehash;
+ assert(aa.length == 1);
+ assert(aa["hello"] == 4);
+
+ aa["foo"] = 1;
+ aa["bar"] = 2;
+ aa["batz"] = 3;
+
+ assert(aa.keys.length == 4);
+ assert(aa.values.length == 4);
+
+ foreach (a; aa.keys)
+ {
+ assert(a.length != 0);
+ assert(a.ptr != null);
+ }
+
+ foreach (v; aa.values)
+ {
+ assert(v != 0);
+ }
+}
+
+void testGet1() @safe
+{
+ int[string] aa;
+ int a;
+ foreach (val; aa.byKeyValue)
+ {
+ ++aa[val.key];
+ a = val.value;
+ }
+}
+
+void testGet2()
+{
+ static class T
+ {
+ static size_t count;
+ this() { ++count; }
+ }
+
+ T[string] aa;
+
+ auto a = new T;
+ aa["foo"] = a;
+ assert(T.count == 1);
+ auto b = aa.get("foo", new T);
+ assert(T.count == 1);
+ assert(b is a);
+ auto c = aa.get("bar", new T);
+ assert(T.count == 2);
+ assert(c !is a);
+
+ //Obviously get doesn't add.
+ assert("bar" !in aa);
+}
+
+void testRequire1()
+{
+ static class T
+ {
+ static size_t count;
+ this() { ++count; }
+ }
+
+ T[string] aa;
+
+ auto a = new T;
+ aa["foo"] = a;
+ assert(T.count == 1);
+ auto b = aa.require("foo", new T);
+ assert(T.count == 1);
+ assert(b is a);
+ auto c = aa.require("bar", null);
+ assert(T.count == 1);
+ assert(c is null);
+ assert("bar" in aa);
+ auto d = aa.require("bar", new T);
+ assert(d is null);
+ auto e = aa.require("baz", new T);
+ assert(T.count == 2);
+ assert(e !is a);
+
+ assert("baz" in aa);
+
+ bool created = false;
+ auto f = aa.require("qux", { created = true; return new T; }());
+ assert(created == true);
+
+ T g;
+ auto h = aa.require("qux", { g = new T; return g; }());
+ assert(g !is h);
+}
+
+void testRequire2()
+{
+ static struct S
+ {
+ int value;
+ }
+
+ S[string] aa;
+
+ aa.require("foo").value = 1;
+ assert(aa == ["foo" : S(1)]);
+
+ aa["bar"] = S(2);
+ auto a = aa.require("bar", S(3));
+ assert(a == S(2));
+
+ auto b = aa["bar"];
+ assert(b == S(2));
+
+ S* c = &aa.require("baz", S(4));
+ assert(c is &aa["baz"]);
+ assert(*c == S(4));
+
+ assert("baz" in aa);
+
+ auto d = aa["baz"];
+ assert(d == S(4));
+}
+
+void testRequire3() pure
+{
+ string[string] aa;
+
+ auto a = aa.require("foo", "bar");
+ assert("foo" in aa);
+}
+
+
+void testUpdate1()
+{
+ static class C {}
+ C[string] aa;
+
+ C orig = new C;
+ aa["foo"] = orig;
+
+ C newer;
+ C older;
+
+ void test(string key)
+ {
+ aa.update(key, {
+ newer = new C;
+ return newer;
+ }, (ref C c) {
+ older = c;
+ newer = new C;
+ return newer;
+ });
+ }
+
+ test("foo");
+ assert(older is orig);
+ assert(newer is aa["foo"]);
+
+ test("bar");
+ assert(newer is aa["bar"]);
+}
+
+void testUpdate2()
+{
+ static class C {}
+ C[string] aa;
+
+ auto created = false;
+ auto updated = false;
+
+ class Creator
+ {
+ C opCall()
+ {
+ created = true;
+ return new C();
+ }
+ }
+
+ class Updater
+ {
+ C opCall(ref C)
+ {
+ updated = true;
+ return new C();
+ }
+ }
+
+ aa.update("foo", new Creator, new Updater);
+ assert(created);
+ aa.update("foo", new Creator, new Updater);
+ assert(updated);
+}
+
+void testByKey1()
+{
+ static assert(!__traits(compiles,
+ () @safe {
+ struct BadValue
+ {
+ int x;
+ this(this) @safe { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe
+ alias x this;
+ }
+
+ BadValue[int] aa;
+ () @safe { auto x = aa.byKey.front; } ();
+ }
+ ));
+}
+
+void testByKey2() nothrow pure
+{
+ int[int] a;
+ foreach (i; a.byKey)
+ {
+ assert(false);
+ }
+ foreach (i; a.byValue)
+ {
+ assert(false);
+ }
+}
+
+void testByKey3() /*nothrow*/ pure
+{
+ auto a = [ 1:"one", 2:"two", 3:"three" ];
+ auto b = a.dup;
+ assert(b == [ 1:"one", 2:"two", 3:"three" ]);
+
+ int[] c;
+ foreach (k; a.byKey)
+ {
+ c ~= k;
+ }
+
+ assert(c.length == 3);
+ assert(c[0] == 1 || c[1] == 1 || c[2] == 1);
+ assert(c[0] == 2 || c[1] == 2 || c[2] == 2);
+ assert(c[0] == 3 || c[1] == 3 || c[2] == 3);
+}
+
+void testByKey4() nothrow pure
+{
+ string[] keys = ["a", "b", "c", "d", "e", "f"];
+
+ // Test forward range capabilities of byKey
+ {
+ int[string] aa;
+ foreach (key; keys)
+ aa[key] = 0;
+
+ auto keyRange = aa.byKey();
+ auto savedKeyRange = keyRange.save;
+
+ // Consume key range once
+ size_t keyCount = 0;
+ while (!keyRange.empty)
+ {
+ aa[keyRange.front]++;
+ keyCount++;
+ keyRange.popFront();
+ }
+
+ foreach (key; keys)
+ {
+ assert(aa[key] == 1);
+ }
+ assert(keyCount == keys.length);
+
+ // Verify it's possible to iterate the range the second time
+ keyCount = 0;
+ while (!savedKeyRange.empty)
+ {
+ aa[savedKeyRange.front]++;
+ keyCount++;
+ savedKeyRange.popFront();
+ }
+
+ foreach (key; keys)
+ {
+ assert(aa[key] == 2);
+ }
+ assert(keyCount == keys.length);
+ }
+
+ // Test forward range capabilities of byValue
+ {
+ size_t[string] aa;
+ foreach (i; 0 .. keys.length)
+ {
+ aa[keys[i]] = i;
+ }
+
+ auto valRange = aa.byValue();
+ auto savedValRange = valRange.save;
+
+ // Consume value range once
+ int[] hasSeen;
+ hasSeen.length = keys.length;
+ while (!valRange.empty)
+ {
+ assert(hasSeen[valRange.front] == 0);
+ hasSeen[valRange.front]++;
+ valRange.popFront();
+ }
+
+ foreach (sawValue; hasSeen) { assert(sawValue == 1); }
+
+ // Verify it's possible to iterate the range the second time
+ hasSeen = null;
+ hasSeen.length = keys.length;
+ while (!savedValRange.empty)
+ {
+ assert(!hasSeen[savedValRange.front]);
+ hasSeen[savedValRange.front] = true;
+ savedValRange.popFront();
+ }
+
+ foreach (sawValue; hasSeen) { assert(sawValue); }
+ }
+}
+
+void issue5842() pure nothrow
+{
+ string[string] test = null;
+ test["test1"] = "test1";
+ test.remove("test1");
+ test.rehash;
+ test["test3"] = "test3"; // causes divide by zero if rehash broke the AA
+}
+
+/// expanded test for 5842: increase AA size past the point where the AA
+/// stops using binit, in order to test another code path in rehash.
+void issue5842Expanded() pure nothrow
+{
+ int[int] aa;
+ foreach (int i; 0 .. 32)
+ aa[i] = i;
+ foreach (int i; 0 .. 32)
+ aa.remove(i);
+ aa.rehash;
+ aa[1] = 1;
+}
+
+void issue5925() nothrow pure
+{
+ const a = [4:0];
+ const b = [4:0];
+ assert(a == b);
+}
+
+/// test for bug 8583: ensure Slot and aaA are on the same page wrt value alignment
+void issue8583() nothrow pure
+{
+ string[byte] aa0 = [0: "zero"];
+ string[uint[3]] aa1 = [[1,2,3]: "onetwothree"];
+ ushort[uint[3]] aa2 = [[9,8,7]: 987];
+ ushort[uint[4]] aa3 = [[1,2,3,4]: 1234];
+ string[uint[5]] aa4 = [[1,2,3,4,5]: "onetwothreefourfive"];
+
+ assert(aa0.byValue.front == "zero");
+ assert(aa1.byValue.front == "onetwothree");
+ assert(aa2.byValue.front == 987);
+ assert(aa3.byValue.front == 1234);
+ assert(aa4.byValue.front == "onetwothreefourfive");
+}
+
+void issue9052() nothrow pure
+{
+ static struct Json {
+ Json[string] aa;
+ void opAssign(Json) {}
+ size_t length() const { return aa.length; }
+ // This length() instantiates AssociativeArray!(string, const(Json)) to call AA.length(), and
+ // inside ref Slot opAssign(Slot p); (which is automatically generated by compiler in Slot),
+ // this.value = p.value would actually fail, because both side types of the assignment
+ // are const(Json).
+ }
+}
+
+void issue9119()
+{
+ int[string] aa;
+ assert(aa.byKeyValue.empty);
+
+ aa["a"] = 1;
+ aa["b"] = 2;
+ aa["c"] = 3;
+
+ auto pairs = aa.byKeyValue;
+
+ auto savedPairs = pairs.save;
+ size_t count = 0;
+ while (!pairs.empty)
+ {
+ assert(pairs.front.key in aa);
+ assert(pairs.front.value == aa[pairs.front.key]);
+ count++;
+ pairs.popFront();
+ }
+ assert(count == aa.length);
+
+ // Verify that saved range can iterate over the AA again
+ count = 0;
+ while (!savedPairs.empty)
+ {
+ assert(savedPairs.front.key in aa);
+ assert(savedPairs.front.value == aa[savedPairs.front.key]);
+ count++;
+ savedPairs.popFront();
+ }
+ assert(count == aa.length);
+}
+
+void issue9852() nothrow pure
+{
+ // Original test case (revised, original assert was wrong)
+ int[string] a;
+ a["foo"] = 0;
+ a.remove("foo");
+ assert(a == null); // should not crash
+
+ int[string] b;
+ assert(b is null);
+ assert(a == b); // should not deref null
+ assert(b == a); // ditto
+
+ int[string] c;
+ c["a"] = 1;
+ assert(a != c); // comparison with empty non-null AA
+ assert(c != a);
+ assert(b != c); // comparison with null AA
+ assert(c != b);
+}
+
+void issue10381()
+{
+ alias II = int[int];
+ II aa1 = [0 : 1];
+ II aa2 = [0 : 1];
+ II aa3 = [0 : 2];
+ assert(aa1 == aa2); // Passes
+ assert(typeid(II).equals(&aa1, &aa2));
+ assert(!typeid(II).equals(&aa1, &aa3));
+}
+
+void issue10720() nothrow pure
+{
+ static struct NC
+ {
+ @disable this(this) { }
+ }
+
+ NC[string] aa;
+ static assert(!is(aa.nonExistingField));
+}
+
+/// bug 11761: test forward range functionality
+void issue11761() pure nothrow
+{
+ auto aa = ["a": 1];
+
+ void testFwdRange(R, T)(R fwdRange, T testValue)
+ {
+ assert(!fwdRange.empty);
+ assert(fwdRange.front == testValue);
+ static assert(is(typeof(fwdRange.save) == typeof(fwdRange)));
+
+ auto saved = fwdRange.save;
+ fwdRange.popFront();
+ assert(fwdRange.empty);
+
+ assert(!saved.empty);
+ assert(saved.front == testValue);
+ saved.popFront();
+ assert(saved.empty);
+ }
+
+ testFwdRange(aa.byKey, "a");
+ testFwdRange(aa.byValue, 1);
+ //testFwdRange(aa.byPair, tuple("a", 1));
+}
+
+void issue13078() nothrow pure
+{
+ shared string[][string] map;
+ map.rehash;
+}
+
+void issue14104()
+{
+ import core.stdc.stdio;
+
+ alias K = const(ubyte)*;
+ size_t[K] aa;
+ immutable key = cast(K)(cast(size_t) uint.max + 1);
+ aa[key] = 12;
+ assert(key in aa);
+}
+
+void issue14626()
+{
+ static struct S
+ {
+ string[string] aa;
+ inout(string) key() inout { return aa.byKey().front; }
+ inout(string) val() inout { return aa.byValue().front; }
+ auto keyval() inout { return aa.byKeyValue().front; }
+ }
+
+ S s = S(["a":"b"]);
+ assert(s.key() == "a");
+ assert(s.val() == "b");
+ assert(s.keyval().key == "a");
+ assert(s.keyval().value == "b");
+
+ void testInoutKeyVal(inout(string) key)
+ {
+ inout(string)[typeof(key)] aa;
+
+ foreach (i; aa.byKey()) {}
+ foreach (i; aa.byValue()) {}
+ foreach (i; aa.byKeyValue()) {}
+ }
+
+ const int[int] caa;
+ static assert(is(typeof(caa.byValue().front) == const int));
+}
+
+/// test duplicated keys in AA literal
+/// https://issues.dlang.org/show_bug.cgi?id=15290
+void issue15290()
+{
+ string[int] aa = [ 0: "a", 0: "b" ];
+ assert(aa.length == 1);
+ assert(aa.keys == [ 0 ]);
+}
+
+void issue15367()
+{
+ void f1() {}
+ void f2() {}
+
+ // TypeInfo_Delegate.getHash
+ int[void delegate()] aa;
+ assert(aa.length == 0);
+ aa[&f1] = 1;
+ assert(aa.length == 1);
+ aa[&f1] = 1;
+ assert(aa.length == 1);
+
+ auto a1 = [&f2, &f1];
+ auto a2 = [&f2, &f1];
+
+ // TypeInfo_Delegate.equals
+ for (auto i = 0; i < 2; i++)
+ assert(a1[i] == a2[i]);
+ assert(a1 == a2);
+
+ // TypeInfo_Delegate.compare
+ for (auto i = 0; i < 2; i++)
+ assert(a1[i] <= a2[i]);
+ assert(a1 <= a2);
+}
+
+/// test AA as key
+/// https://issues.dlang.org/show_bug.cgi?id=16974
+void issue16974()
+{
+ int[int] a = [1 : 2], a2 = [1 : 2];
+
+ assert([a : 3] == [a : 3]);
+ assert([a : 3] == [a2 : 3]);
+
+ assert(typeid(a).getHash(&a) == typeid(a).getHash(&a));
+ assert(typeid(a).getHash(&a) == typeid(a).getHash(&a2));
+}
+
+/// test safety for alias-this'd AA that have unsafe opCast
+/// https://issues.dlang.org/show_bug.cgi?id=18071
+void issue18071()
+{
+ static struct Foo
+ {
+ int[int] aa;
+ auto opCast() pure nothrow @nogc
+ {
+ *cast(uint*)0xdeadbeef = 0xcafebabe;// unsafe
+ return null;
+ }
+ alias aa this;
+ }
+
+ Foo f;
+ () @safe { assert(f.byKey.empty); }();
+}
+
+/// Verify iteration with const.
+void testIterationWithConst()
+{
+ auto aa = [1:2, 3:4];
+ foreach (const t; aa.byKeyValue)
+ {
+ auto k = t.key;
+ auto v = t.value;
+ }
+}
+
+void testStructArrayKey() @safe
+{
+ struct S
+ {
+ int i;
+ const @safe nothrow:
+ hash_t toHash() { return 0; }
+ bool opEquals(const S) { return true; }
+ int opCmp(const S) { return 0; }
+ }
+
+ int[S[]] aa = [[S(11)] : 13];
+ assert(aa[[S(12)]] == 13);
+}
+
+void miscTests1() pure nothrow
+{
+ string[int] key1 = [1 : "true", 2 : "false"];
+ string[int] key2 = [1 : "false", 2 : "true"];
+ string[int] key3;
+
+ // AA lits create a larger hashtable
+ int[string[int]] aa1 = [key1 : 100, key2 : 200, key3 : 300];
+
+ // Ensure consistent hash values are computed for key1
+ assert((key1 in aa1) !is null);
+
+ // Manually assigning to an empty AA creates a smaller hashtable
+ int[string[int]] aa2;
+ aa2[key1] = 100;
+ aa2[key2] = 200;
+ aa2[key3] = 300;
+
+ assert(aa1 == aa2);
+
+ // Ensure binary-independence of equal hash keys
+ string[int] key2a;
+ key2a[1] = "false";
+ key2a[2] = "true";
+
+ assert(aa1[key2a] == 200);
+}
+
+void miscTests2()
+{
+ int[int] aa;
+ foreach (k, v; aa)
+ assert(false);
+ foreach (v; aa)
+ assert(false);
+ assert(aa.byKey.empty);
+ assert(aa.byValue.empty);
+ assert(aa.byKeyValue.empty);
+
+ size_t n;
+ aa = [0 : 3, 1 : 4, 2 : 5];
+ foreach (k, v; aa)
+ {
+ n += k;
+ assert(k >= 0 && k < 3);
+ assert(v >= 3 && v < 6);
+ }
+ assert(n == 3);
+ n = 0;
+
+ foreach (v; aa)
+ {
+ n += v;
+ assert(v >= 3 && v < 6);
+ }
+ assert(n == 12);
+
+ n = 0;
+ foreach (k, v; aa)
+ {
+ ++n;
+ break;
+ }
+ assert(n == 1);
+
+ n = 0;
+ foreach (v; aa)
+ {
+ ++n;
+ break;
+ }
+ assert(n == 1);
+}
+
+void testRemove()
+{
+ int[int] aa;
+ assert(!aa.remove(0));
+ aa = [0 : 1];
+ assert(aa.remove(0));
+ assert(!aa.remove(0));
+ aa[1] = 2;
+ assert(!aa.remove(0));
+ assert(aa.remove(1));
+
+ assert(aa.length == 0);
+ assert(aa.byKey.empty);
+}
+
+/// test zero sized value (hashset)
+void testZeroSizedValue()
+{
+ alias V = void[0];
+ auto aa = [0 : V.init];
+ assert(aa.length == 1);
+ assert(aa.byKey.front == 0);
+ assert(aa.byValue.front == V.init);
+ aa[1] = V.init;
+ assert(aa.length == 2);
+ aa[0] = V.init;
+ assert(aa.length == 2);
+ assert(aa.remove(0));
+ aa[0] = V.init;
+ assert(aa.length == 2);
+ assert(aa == [0 : V.init, 1 : V.init]);
+}
+
+void testTombstonePurging()
+{
+ int[int] aa;
+ foreach (i; 0 .. 6)
+ aa[i] = i;
+ foreach (i; 0 .. 6)
+ assert(aa.remove(i));
+ foreach (i; 6 .. 10)
+ aa[i] = i;
+ assert(aa.length == 4);
+ foreach (i; 6 .. 10)
+ assert(i in aa);
+}
+
+void testClear()
+{
+ int[int] aa;
+ assert(aa.length == 0);
+ foreach (i; 0 .. 100)
+ aa[i] = i * 2;
+ assert(aa.length == 100);
+ auto aa2 = aa;
+ assert(aa2.length == 100);
+ aa.clear();
+ assert(aa.length == 0);
+ assert(aa2.length == 0);
+
+ aa2[5] = 6;
+ assert(aa.length == 1);
+ assert(aa[5] == 6);
+}
--- /dev/null
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+load_lib libphobos-dg.exp
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+# Main loop.
+dg-runtest $tests "" $DEFAULT_DFLAGS
+
+# All done.
+dg-finish
--- /dev/null
+void main()
+{
+ issue19562();
+ issue15111();
+ issues16654And16764();
+ issue18918();
+ issue18925();
+ issue19005();
+ issue19204();
+ issue19262();
+ issue19568();
+ issue19582();
+ testTypeInfoArrayGetHash1();
+ testTypeInfoArrayGetHash2();
+ pr2243();
+}
+
+/// Check hashOf an array of void pointers or delegates is @safe.
+void issue19562() @nogc nothrow pure @safe
+{
+ void*[10] val;
+ size_t h = hashOf(val[]);
+
+ alias D = void delegate();
+ D[10] ds;
+ h = hashOf(ds[]);
+}
+
+/// hashOf was failing for structs that had an `alias this` to a dynamic array.
+void issue15111()
+{
+ void testAlias(T)()
+ {
+ static struct Foo
+ {
+ T t;
+ alias t this;
+ }
+ Foo foo;
+ static assert(is(typeof(hashOf(foo))));
+ }
+ // was fixed
+ testAlias!(int[]);
+ testAlias!(int*);
+ // was not affected
+ testAlias!int;
+ testAlias!(void delegate());
+ testAlias!(string[string]);
+ testAlias!(int[8]);
+}
+
+void issues16654And16764()
+{
+ auto a = [1];
+ auto b = a.dup;
+ assert(hashOf(a) == hashOf(b));
+}
+
+/// Check hashOf dynamic array of scalars is usable in @safe code.
+void issue18918() nothrow pure @safe
+{
+ const _ = (() @nogc => hashOf("abc"))();
+
+ static struct S { string array; }
+ auto s1 = S("abc");
+ auto s2 = S(s1.array.idup);
+ assert(hashOf(s1) == hashOf(s2));
+ enum e = hashOf(S("abc"));
+ assert(hashOf(s1) == e);
+}
+
+/// Check hashOf struct of scalar fields is usable in @safe code.
+void issue18925() @nogc nothrow pure @safe
+{
+
+ static struct S { int a; int b; }
+ auto h = hashOf(S.init);
+}
+
+void issue19005() @nogc nothrow pure @safe
+{
+ enum Month : ubyte
+ {
+ jan = 1
+ }
+ static struct Date
+ {
+ short _year;
+ Month _month;
+ ubyte _day;
+ }
+ Date date;
+ auto hash = date.hashOf;
+}
+
+/// Accept SIMD vectors.
+void issue19204() @nogc nothrow pure @safe
+{
+ version (D_SIMD)
+ {
+ static import simd = core.simd;
+ static if (is(simd.int4)) // __traits(isArithmetic)
+ {{
+ enum simd.int4 val = [1,2,3,4];
+ enum ctfeHash = hashOf(val);
+ simd.int4 rtVal = val;
+ auto rtHash = hashOf(rtVal);
+ assert(ctfeHash == rtHash);
+ }}
+ static if (is(simd.void16)) // non __traits(isArithmetic)
+ {{
+ auto h = hashOf(simd.void16.init);
+ }}
+ static if (is(simd.float4)) // __traits(isArithmetic) and __traits(isFloating)
+ {{
+ enum simd.float4 val = [1.1f, 2.2f, 3.3f, 4.4f];
+ enum ctfeHash = hashOf(val);
+ simd.float4 rtVal = val;
+ auto rtHash = hashOf(rtVal);
+ assert(ctfeHash == rtHash);
+ }}
+ }
+}
+
+/// hashOf associative array should infer nothrow
+void issue19262() nothrow
+{
+ int[int] aa;
+ auto h = hashOf(aa);
+ h = hashOf(aa, h);
+}
+
+/// hashOf should not unnecessarily call a struct's fields' postblits & dtors in CTFE
+void issue19568()
+{
+ static struct S1
+ {
+ @disable this(this);
+
+ ~this() @nogc nothrow
+ {
+ import core.stdc.stdio;
+ if (mptr) puts("impure");
+ }
+
+ size_t[2] pad;
+ void* mptr;
+ }
+
+ static struct S2
+ {
+ @disable this(this);
+
+ ~this() @nogc nothrow
+ {
+ import core.stdc.stdio;
+ if (fd != -1) puts("impure");
+ }
+
+ int fd = -1;
+ S1 s1;
+ }
+
+ static struct S3
+ {
+ private S2 s2;
+ }
+
+ S3 s3;
+ size_t h = ((ref S3 s3) pure => hashOf(s3))(s3);
+}
+
+/// Check core.internal.convert.toUbyte in CTFE for arrays works with
+/// reference type elements and doesn't call postblits/dtors.
+void issue19582()
+{
+ import core.internal.convert : toUbyte;
+ final static class C : Object {}
+ enum b1 = (() @nogc nothrow pure @safe { C[10] o; return toUbyte(o[])[0]; })();
+
+ static struct S
+ {
+ int x;
+ @disable this(this);
+ ~this() @nogc nothrow
+ {
+ import core.stdc.stdio : puts;
+ if (x) puts("impure");
+ }
+ }
+ enum b2 = () {
+ S[10] a;
+ return ((const S[] a) @nogc nothrow pure @safe => toUbyte(a))(a);
+ }();
+}
+
+/// Tests ensure TypeInfo_Array.getHash uses element hash functions instead
+/// of hashing array data.
+void testTypeInfoArrayGetHash1()
+{
+ class C
+ {
+ int i;
+ this(in int i) { this.i = i; }
+ override hash_t toHash() { return 0; }
+ }
+ C[] a1 = [new C(11)], a2 = [new C(12)];
+ assert(typeid(C[]).getHash(&a1) == typeid(C[]).getHash(&a2));
+}
+
+/// ditto
+void testTypeInfoArrayGetHash2()
+{
+ struct S
+ {
+ int i;
+ hash_t toHash() const @safe nothrow { return 0; }
+ }
+ S[] a1 = [S(11)], a2 = [S(12)];
+ assert(typeid(S[]).getHash(&a1) == typeid(S[]).getHash(&a2));
+}
+
+/++
+Use the new `core.internal.hash.hashOf` in all `TypeInfo.getHash` instead of
+the `old rt.util.hash.hashOf`. Also make `typeid(T).getHash(&val)` get the
+same result as `hashOf(val)`.
++/
+void pr2243()
+{
+ static struct Foo
+ {
+ int a = 99;
+ float b = 4.0;
+ size_t toHash() const pure @safe nothrow
+ {
+ return a;
+ }
+ }
+
+ static struct Bar
+ {
+ char c = 'x';
+ int a = 99;
+ float b = 4.0;
+ void* d = null;
+ }
+
+ static struct Boom
+ {
+ char c = 'M';
+ int* a = null;
+ }
+
+ static struct Plain
+ {
+ int a = 1;
+ int b = 2;
+ }
+
+ interface IBoo
+ {
+ void boo();
+ }
+
+ static class Boo: IBoo
+ {
+ override void boo()
+ {
+ }
+
+ override size_t toHash()
+ {
+ return 1;
+ }
+ }
+
+ static struct Goo
+ {
+ size_t toHash() pure @safe nothrow
+ {
+ return 1;
+ }
+ }
+
+ enum Gun: long
+ {
+ A = 99,
+ B = 17
+ }
+
+ enum double dexpr = 3.14;
+ enum float fexpr = 2.71;
+ enum wstring wsexpr = "abcdef"w;
+ enum string csexpr = "abcdef";
+ enum int iexpr = 7;
+ enum long lexpr = 42;
+ enum int[2][3] saexpr = [[1, 2], [3, 4], [5, 6]];
+ enum int[] daexpr = [7,8,9];
+ enum Foo thsexpr = Foo();
+ enum Bar vsexpr = Bar();
+ enum int[int] aaexpr = [99:2, 12:6, 45:4];
+ enum Gun eexpr = Gun.A;
+ enum cdouble cexpr = 7+4i;
+ enum Foo[] staexpr = [Foo(), Foo(), Foo()];
+ enum Bar[] vsaexpr = [Bar(), Bar(), Bar()];
+ enum realexpr = 7.88;
+ enum raexpr = [8.99L+86i, 3.12L+99i, 5.66L+12i];
+ enum nullexpr = null;
+ enum plstr = Plain();
+ enum plarrstr = [Plain(), Plain(), Plain()];
+ //No CTFE:
+ Boom rstructexpr = Boom();
+ Boom[] rstrarrexpr = [Boom(), Boom(), Boom()];
+ int delegate() dgexpr = (){return 78;};
+ void* ptrexpr = &dgexpr;
+
+
+ //CTFE hashes
+ enum h1 = dexpr.hashOf();
+ enum h2 = fexpr.hashOf();
+ enum h3 = wsexpr.hashOf();
+ enum h4 = csexpr.hashOf();
+ enum h5 = iexpr.hashOf();
+ enum h6 = lexpr.hashOf();
+ enum h7 = saexpr.hashOf();
+ enum h8 = daexpr.hashOf();
+ enum h9 = thsexpr.hashOf();
+ enum h10 = vsexpr.hashOf();
+ enum h11 = aaexpr.hashOf();
+ enum h12 = eexpr.hashOf();
+ enum h13 = cexpr.hashOf();
+ enum h14 = hashOf(new Boo);
+ enum h15 = staexpr.hashOf();
+ enum h16 = hashOf([new Boo, new Boo, new Boo]);
+ enum h17 = hashOf([cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo]);
+ enum h18 = hashOf(cast(IBoo)new Boo);
+ enum h19 = vsaexpr.hashOf();
+ enum h20 = hashOf(cast(Foo[3])staexpr);
+
+ //BUG: cannot cast [Boo(), Boo(), Boo()][0] to object.Object at compile time
+ auto h21 = hashOf(cast(Boo[3])[new Boo, new Boo, new Boo]);
+ auto h22 = hashOf(cast(IBoo[3])[cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo]);
+ enum h23 = hashOf(cast(Bar[3])vsaexpr);
+
+ //NO CTFE (Compute, but don't check correctness):
+ auto h24 = rstructexpr.hashOf();
+ auto h25 = rstrarrexpr.hashOf();
+ auto h26 = dgexpr.hashOf();
+ auto h27 = ptrexpr.hashOf();
+
+ enum h28 = realexpr.hashOf();
+ enum h29 = raexpr.hashOf();
+ enum h30 = nullexpr.hashOf();
+ enum h31 = plstr.hashOf();
+ enum h32 = plarrstr.hashOf();
+ enum h33 = hashOf(cast(Plain[3])plarrstr);
+
+ auto v1 = dexpr;
+ auto v2 = fexpr;
+ auto v3 = wsexpr;
+ auto v4 = csexpr;
+ auto v5 = iexpr;
+ auto v6 = lexpr;
+ auto v7 = saexpr;
+ auto v8 = daexpr;
+ auto v9 = thsexpr;
+ auto v10 = vsexpr;
+ auto v11 = aaexpr;
+ auto v12 = eexpr;
+ auto v13 = cexpr;
+ auto v14 = new Boo;
+ auto v15 = staexpr;
+ auto v16 = [new Boo, new Boo, new Boo];
+ auto v17 = [cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo];
+ auto v18 = cast(IBoo)new Boo;
+ auto v19 = vsaexpr;
+ auto v20 = cast(Foo[3])staexpr;
+ auto v21 = cast(Boo[3])[new Boo, new Boo, new Boo];
+ auto v22 = cast(IBoo[3])[cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo];
+ auto v23 = cast(Bar[3])vsaexpr;
+ auto v30 = null;
+ auto v31 = plstr;
+ auto v32 = plarrstr;
+ auto v33 = cast(Plain[3])plarrstr;
+
+ //NO CTFE:
+ auto v24 = rstructexpr;
+ auto v25 = rstrarrexpr;
+ auto v26 = dgexpr;
+ auto v27 = ptrexpr;
+ auto v28 = realexpr;
+ auto v29 = raexpr;
+
+ //runtime hashes
+ auto rth1 = hashOf(v1);
+ auto rth2 = hashOf(v2);
+ auto rth3 = hashOf(v3);
+ auto rth4 = hashOf(v4);
+ auto rth5 = hashOf(v5);
+ auto rth6 = hashOf(v6);
+ auto rth7 = hashOf(v7);
+ auto rth8 = hashOf(v8);
+ auto rth9 = hashOf(v9);
+ auto rth10 = hashOf(v10);
+ auto rth11 = hashOf(v11);
+ auto rth12 = hashOf(v12);
+ auto rth13 = hashOf(v13);
+ auto rth14 = hashOf(v14);
+ auto rth15 = hashOf(v15);
+ auto rth16 = hashOf(v16);
+ auto rth17 = hashOf(v17);
+ auto rth18 = hashOf(v18);
+ auto rth19 = hashOf(v19);
+ auto rth20 = hashOf(v20);
+ auto rth21 = hashOf(v21);
+ auto rth22 = hashOf(v22);
+ auto rth23 = hashOf(v23);
+ auto rth30 = hashOf(v30);
+ //NO CTFE:
+ auto rth24 = hashOf(v24);
+ auto rth25 = hashOf(v25);
+ auto rth26 = hashOf(v26);
+ auto rth27 = hashOf(v27);
+ auto rth28 = hashOf(v28);
+ auto rth29 = hashOf(v29);
+
+ auto rth31 = hashOf(v31);
+ auto rth32 = hashOf(v32);
+ auto rth33 = hashOf(v33);
+
+ assert(h1 == rth1);
+ assert(h2 == rth2);
+ assert(h3 == rth3);
+ assert(h4 == rth4);
+ assert(h5 == rth5);
+ assert(h6 == rth6);
+ assert(h7 == rth7);
+ assert(h8 == rth8);
+ assert(h9 == rth9);
+ assert(h10 == rth10);
+ assert(h11 == rth11);
+ assert(h12 == rth12);
+ assert(h13 == rth13);
+ assert(h14 == rth14);
+ assert(h15 == rth15);
+ assert(h16 == rth16);
+ assert(h17 == rth17);
+ assert(h18 == rth18);
+ assert(h19 == rth19);
+ assert(h20 == rth20);
+ assert(h21 == rth21);
+ assert(h22 == rth22);
+ assert(h23 == rth23);
+ /*assert(h24 == rth24);
+ assert(h25 == rth25);
+ assert(h26 == rth26);
+ assert(h27 == rth27);
+ assert(h28 == rth28);
+ assert(h29 == rth29);*/
+ assert(h30 == rth30);
+ assert(h31 == rth31);
+ assert(h32 == rth32);
+ assert(h33 == rth33);
+
+ // https://issues.dlang.org/show_bug.cgi?id=18932
+ assert(hashOf(null, 0) != hashOf(null, 123456789));
+
+ static size_t tiHashOf(T)(T var)
+ {
+ return typeid(T).getHash(&var);
+ }
+
+ auto tih1 = tiHashOf(v1);
+ auto tih2 = tiHashOf(v2);
+ auto tih3 = tiHashOf(v3);
+ auto tih4 = tiHashOf(v4);
+ auto tih5 = tiHashOf(v5);
+ auto tih6 = tiHashOf(v6);
+ auto tih7 = tiHashOf(v7);
+ auto tih8 = tiHashOf(v8);
+ auto tih9 = tiHashOf(v9);
+ auto tih10 = tiHashOf(v10);
+ auto tih11 = tiHashOf(v11);
+ auto tih12 = tiHashOf(v12);
+ auto tih13 = tiHashOf(v13);
+ auto tih14 = tiHashOf(v14);
+ auto tih15 = tiHashOf(v15);
+ auto tih16 = tiHashOf(v16);
+ auto tih17 = tiHashOf(v17);
+ auto tih18 = tiHashOf(v18);
+ auto tih19 = tiHashOf(v19);
+ auto tih20 = tiHashOf(v20);
+ auto tih21 = tiHashOf(v21);
+ auto tih22 = tiHashOf(v22);
+ auto tih23 = tiHashOf(v23);
+ auto tih24 = tiHashOf(v24);
+ auto tih25 = tiHashOf(v25);
+ auto tih26 = tiHashOf(v26);
+ auto tih27 = tiHashOf(v27);
+ auto tih28 = tiHashOf(v28);
+ auto tih29 = tiHashOf(v29);
+ auto tih30 = tiHashOf(v30);
+ auto tih31 = tiHashOf(v31);
+ auto tih32 = tiHashOf(v32);
+ auto tih33 = tiHashOf(v33);
+
+ assert(tih1 == rth1);
+ assert(tih2 == rth2);
+ assert(tih3 == rth3);
+ assert(tih4 == rth4);
+ assert(tih5 == rth5);
+ assert(tih6 == rth6);
+ assert(tih7 == rth7);
+ assert(tih8 == rth8);
+ assert(tih9 == rth9);
+ //assert(tih10 == rth10); // need compiler-generated __xtoHash changes
+ assert(tih11 == rth11);
+ assert(tih12 == rth12);
+ assert(tih13 == rth13);
+ assert(tih14 == rth14);
+ assert(tih15 == rth15);
+ assert(tih16 == rth16);
+ assert(tih17 == rth17);
+ assert(tih18 == rth18);
+ //assert(tih19 == rth19); // need compiler-generated __xtoHash changes
+ assert(tih20 == rth20);
+ assert(tih21 == rth21);
+ assert(tih22 == rth22);
+ //assert(tih23 == rth23); // need compiler-generated __xtoHash changes
+ //assert(tih24 == rth24);
+ //assert(tih25 == rth25);
+ assert(tih26 == rth26);
+ assert(tih27 == rth27);
+ assert(tih28 == rth28);
+ //assert(tih29 == rth29); // XGDC: Implementation wrongly hashes padding.
+ assert(tih30 == rth30);
+ assert(tih31 == rth31);
+ assert(tih32 == rth32);
+ assert(tih33 == rth33);
+}