libphobos: Apply core.internal.hash updates from druntime

Backported from upstream druntime 2.084

Reviewed-on: https://github.com/dlang/druntime/pull/2469

libphobos/ChangeLog:

	* 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.

From-SVN: r268754
This commit is contained in:
Iain Buclaw 2019-02-10 21:01:24 +00:00 committed by Iain Buclaw
parent f1b7b50aff
commit e613d99266
50 changed files with 2647 additions and 1385 deletions

View File

@ -1,3 +1,12 @@
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> 2019-01-12 Iain Buclaw <ibuclaw@gdcproject.org>
* README.gcc: New file. * README.gcc: New file.

View File

@ -1,4 +1,4 @@
f2db21937e650553066c30f1a9d5a7d08a1b3573 cc215408bbdbc3324a95080aeef31287f663e57c
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the dlang/druntime repository. merge done from the dlang/druntime repository.

View File

@ -197,8 +197,8 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
rt/typeinfo/ti_ucent.d rt/typeinfo/ti_uint.d rt/typeinfo/ti_ulong.d \ 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/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/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/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
rt/util/random.d rt/util/typeinfo.d rt/util/utf.d rt/util/typeinfo.d rt/util/utf.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \ DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
core/stdcpp/typeinfo.d core/stdcpp/typeinfo.d

View File

@ -229,8 +229,7 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
rt/typeinfo/ti_wchar.lo rt/util/array.lo \ rt/typeinfo/ti_wchar.lo rt/util/array.lo \
rt/util/container/array.lo rt/util/container/common.lo \ rt/util/container/array.lo rt/util/container/common.lo \
rt/util/container/hashtab.lo rt/util/container/treap.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/random.lo rt/util/typeinfo.lo rt/util/utf.lo
rt/util/utf.lo
am__objects_2 = gc/bits.lo gc/config.lo gc/gcinterface.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/impl/conservative/gc.lo gc/impl/manual/gc.lo gc/os.lo \
gc/pooltable.lo gc/proxy.lo gc/pooltable.lo gc/proxy.lo
@ -831,8 +830,8 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
rt/typeinfo/ti_ucent.d rt/typeinfo/ti_uint.d rt/typeinfo/ti_ulong.d \ 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/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/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/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
rt/util/random.d rt/util/typeinfo.d rt/util/utf.d rt/util/typeinfo.d rt/util/utf.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \ DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
core/stdcpp/typeinfo.d core/stdcpp/typeinfo.d
@ -1256,7 +1255,6 @@ rt/util/container/array.lo: rt/util/container/$(am__dirstamp)
rt/util/container/common.lo: rt/util/container/$(am__dirstamp) rt/util/container/common.lo: rt/util/container/$(am__dirstamp)
rt/util/container/hashtab.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/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/random.lo: rt/util/$(am__dirstamp)
rt/util/typeinfo.lo: rt/util/$(am__dirstamp) rt/util/typeinfo.lo: rt/util/$(am__dirstamp)
rt/util/utf.lo: rt/util/$(am__dirstamp) rt/util/utf.lo: rt/util/$(am__dirstamp)

View File

@ -33,7 +33,7 @@ private ubyte[] ctfe_alloc()(size_t n)
} }
} }
@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) || 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)) is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
{ {
@ -72,7 +72,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqua
ulong mantissa2 = parsed.mantissa2; ulong mantissa2 = parsed.mantissa2;
off_bytes--; // go back one, since mantissa only stored data in 56 off_bytes--; // go back one, since mantissa only stored data in 56
// bits, ie 7 bytes // 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; buff[off_bytes] = cast(ubyte)mantissa2;
mantissa2 >>= 8; mantissa2 >>= 8;
@ -114,13 +114,13 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqua
} }
} }
@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)) 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); 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) private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!T != FloatFormat.Real80)
{ {
Unqual!T x = x_; Unqual!T x = x_;
@ -178,7 +178,7 @@ private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!
} }
} }
@safe pure nothrow @safe pure nothrow @nogc
private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80) private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80)
{ {
Unqual!T x = x_; Unqual!T x = x_;
@ -291,10 +291,10 @@ private template FloatTraits(T) if (floatFormat!T == FloatFormat.Quadruple)
} }
@safe pure nothrow @safe pure nothrow @nogc
private real binPow2(int pow) 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); assert(pow > 0);
@ -319,14 +319,14 @@ private real binPow2(int pow)
//Need in CTFE, because CTFE float and double expressions computed more precisely that run-time expressions. //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) private ulong shiftrRound(ulong x)
{ {
return (x >> 1) + (x & 1); return (x >> 1) + (x & 1);
} }
@safe pure nothrow @safe pure nothrow @nogc
private uint binLog2(T)(T x) private uint binLog2(T)(const T x)
{ {
assert(x > 0); assert(x > 0);
int max = 2 ^^ (FloatTraits!T.EXPONENT-1)-1; int max = 2 ^^ (FloatTraits!T.EXPONENT-1)-1;
@ -353,7 +353,7 @@ private uint binLog2(T)(T x)
return max; return max;
} }
@safe pure nothrow @safe pure nothrow @nogc
private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Real80) private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Real80)
{ {
x *= 2.0L^^FloatTraits!T.MANTISSA; x *= 2.0L^^FloatTraits!T.MANTISSA;
@ -362,7 +362,7 @@ private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == Float
return Float(fl.mantissa >> pow, 0, sign); return Float(fl.mantissa >> pow, 0, sign);
} }
@safe pure nothrow @safe pure nothrow @nogc
private Float denormalizedMantissa(T)(T x, uint sign) private Float denormalizedMantissa(T)(T x, uint sign)
if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double) if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double)
{ {
@ -372,7 +372,7 @@ private Float denormalizedMantissa(T)(T x, uint sign)
return Float(shiftrRound(mant), 0, sign); 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) private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Quadruple)
{ {
x *= 2.0L^^FloatTraits!T.MANTISSA; x *= 2.0L^^FloatTraits!T.MANTISSA;
@ -568,21 +568,35 @@ template floatFormat(T) if (is(T:real) || is(T:ireal))
} }
// all toUbyte functions must be evaluable at compile time // all toUbyte functions must be evaluable at compile time
@trusted pure nothrow @trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(T[] arr) if (T.sizeof == 1) const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof == 1)
{ {
return cast(const(ubyte)[])arr; return cast(const(ubyte)[])arr;
} }
@trusted pure nothrow @trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyte)[])) && (T.sizeof > 1)) const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof > 1)
{ {
if (__ctfe) if (__ctfe)
{ {
const(ubyte)[] ret; ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
foreach (cur; arr) 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)))
{ {
ret ~= toUbyte(cur); size_t offset = 0;
foreach (ref cur; arr)
{
ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
offset += T.sizeof;
}
}
else
{
foreach (cur; arr)
assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
} }
return ret; return ret;
} }
@ -592,14 +606,16 @@ const(ubyte)[] toUbyte(T)(T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyt
} }
} }
@trusted pure nothrow @trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enum)) const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
{ {
static if (T.sizeof == 1) static if (T.sizeof == 1)
{ {
if (__ctfe) if (__ctfe)
{ {
return cast(const(ubyte)[])[val]; ubyte[] result = ctfe_alloc(1);
result[0] = cast(ubyte) val;
return result;
} }
else else
{ {
@ -608,7 +624,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu
} }
else if (__ctfe) else if (__ctfe)
{ {
ubyte[T.sizeof] tmp; ubyte[] tmp = ctfe_alloc(T.sizeof);
Unqual!T val_ = val; Unqual!T val_ = val;
for (size_t i = 0; i < T.sizeof; ++i) for (size_t i = 0; i < T.sizeof; ++i)
{ {
@ -618,7 +634,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu
tmp[idx] = cast(ubyte)(val_&0xff); tmp[idx] = cast(ubyte)(val_&0xff);
val_ >>= 8; val_ >>= 8;
} }
return tmp[].dup; return tmp;
} }
else else
{ {
@ -626,14 +642,41 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu
} }
} }
@trusted pure nothrow @trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal)) 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) if (__ctfe)
{ {
auto re = val.re; auto re = val.re;
auto im = val.im; 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 else
{ {
@ -641,14 +684,13 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T =
} }
} }
@trusted pure nothrow @trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(ref T val) if (is(T == enum) && is(typeof(toUbyte(cast(V)val)) == const(ubyte)[])) const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == enum))
{ {
if (__ctfe) if (__ctfe)
{ {
static if (is(T V == enum)){} static if (is(T V == enum)){}
V e_val = val; return toUbyte(cast(const V) val);
return toUbyte(e_val);
} }
else else
{ {
@ -656,76 +698,65 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(T == enum) && is(typeof(toUbyte(cast
} }
} }
private bool isNonReference(T)() nothrow pure @safe unittest
{ {
static if (is(T == struct) || is(T == union)) // Issue 19008 - check toUbyte works on enums.
{ enum Month : uint { jan = 1}
return isNonReferenceStruct!T(); Month m = Month.jan;
} const bytes = toUbyte(m);
else static if (__traits(isStaticArray, T)) enum ctfe_works = (() => { Month x = Month.jan; return toUbyte(x).length > 0; })();
{
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;
}
} }
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)
{
static if (!isNonReference!(typeof(cur))()) return false;
}
return true;
}
@trusted pure nothrow
const(ubyte)[] toUbyte(T)(ref T val) if (is(T == struct) || is(T == union))
{ {
if (__ctfe) if (__ctfe)
{ {
ubyte[T.sizeof] bytes; if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
foreach (key, cur; val.tupleof) return ctfe_alloc(T.sizeof);
{
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)))
{
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
}
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");
}
}
return bytes[].dup;
} }
else else
{ {
return (cast(const(ubyte)*)&val)[0 .. T.sizeof]; return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
} }
} }
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == struct) || is(T == union))
{
if (__ctfe)
{
ubyte[] bytes = ctfe_alloc(T.sizeof);
foreach (key, ref cur; val.tupleof)
{
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)))
{
bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
}
else
{
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;
}
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -128,6 +128,30 @@ template dtorIsNothrow(T)
enum dtorIsNothrow = is(typeof(function{T t=void;}) : void function() nothrow); 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...) template anySatisfy(alias F, T...)
{ {
static if (T.length == 0) static if (T.length == 0)

File diff suppressed because it is too large Load Diff

View File

@ -88,7 +88,7 @@ private:
return used - deleted; return used - deleted;
} }
@property size_t dim() const pure nothrow @nogc @property size_t dim() const pure nothrow @nogc @safe
{ {
return buckets.length; return buckets.length;
} }
@ -183,7 +183,7 @@ private pure nothrow @nogc:
return hash == HASH_DELETED; return hash == HASH_DELETED;
} }
@property bool filled() const @property bool filled() const @safe
{ {
return cast(ptrdiff_t) hash < 0; return cast(ptrdiff_t) hash < 0;
} }
@ -365,8 +365,29 @@ extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc
* If key was not in the aa, a mutable pointer to newly inserted value which * If key was not in the aa, a mutable pointer to newly inserted value which
* is set to all zeros * is set to all zeros
*/ */
extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, in size_t valsz, extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti,
in void* pkey) 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 // lazily alloc implementation
if (aa.impl is null) if (aa.impl is null)
@ -377,7 +398,10 @@ extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, in size_t v
// found a value => return it // found a value => return it
if (auto p = aa.findSlotLookup(hash, pkey, ti.key)) if (auto p = aa.findSlotLookup(hash, pkey, ti.key))
{
found = true;
return p.entry + aa.valoff; return p.entry + aa.valoff;
}
auto p = aa.findSlotInsert(hash); auto p = aa.findSlotInsert(hash);
if (p.deleted) if (p.deleted)
@ -584,6 +608,7 @@ extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void
void* pkey = keys.ptr; void* pkey = keys.ptr;
void* pval = vals.ptr; void* pval = vals.ptr;
immutable off = aa.valoff; immutable off = aa.valoff;
uint actualLength = 0;
foreach (_; 0 .. length) foreach (_; 0 .. length)
{ {
immutable hash = calcHash(pkey, ti.key); immutable hash = calcHash(pkey, ti.key);
@ -595,6 +620,7 @@ extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void
p.hash = hash; p.hash = hash;
p.entry = allocEntry(aa, pkey); // move key, no postblit p.entry = allocEntry(aa, pkey); // move key, no postblit
aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr)); aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
actualLength++;
} }
else if (aa.entryTI && hasDtor(ti.value)) else if (aa.entryTI && hasDtor(ti.value))
{ {
@ -608,7 +634,7 @@ extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void
pkey += keysz; pkey += keysz;
pval += valsz; pval += valsz;
} }
aa.used = cast(uint) length; aa.used = actualLength;
return aa; return aa;
} }
@ -653,6 +679,7 @@ extern (C) hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow
auto uti = unqualify(tiRaw); auto uti = unqualify(tiRaw);
auto ti = *cast(TypeInfo_AssociativeArray*)&uti; auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
immutable off = aa.valoff; immutable off = aa.valoff;
auto keyHash = &ti.key.getHash;
auto valHash = &ti.value.getHash; auto valHash = &ti.value.getHash;
size_t h; size_t h;
@ -660,10 +687,11 @@ extern (C) hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow
{ {
if (!b.filled) if (!b.filled)
continue; continue;
size_t[2] h2 = [b.hash, valHash(b.entry + off)]; size_t[2] h2 = [keyHash(b.entry), valHash(b.entry + off)];
// use XOR here, so that hash is independent of element order // use addition here, so that hash is independent of element order
h ^= hashOf(h2); h += hashOf(h2);
} }
return h; return h;
} }
@ -677,7 +705,7 @@ struct Range
alias impl this; alias impl this;
} }
extern (C) pure nothrow @nogc extern (C) pure nothrow @nogc @safe
{ {
Range _aaRange(AA aa) Range _aaRange(AA aa)
{ {
@ -694,21 +722,32 @@ extern (C) pure nothrow @nogc
bool _aaRangeEmpty(Range r) 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) void* _aaRangeFrontKey(Range r)
{ {
assert(!_aaRangeEmpty(r));
if (r.idx >= r.dim)
return null;
return r.buckets[r.idx].entry; return r.buckets[r.idx].entry;
} }
void* _aaRangeFrontValue(Range r) 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) void _aaRangePopFront(ref Range r)
{ {
if (r.idx >= r.dim) return;
for (++r.idx; r.idx < r.dim; ++r.idx) for (++r.idx; r.idx < r.dim; ++r.idx)
{ {
if (r.buckets[r.idx].filled) if (r.buckets[r.idx].filled)
@ -717,221 +756,7 @@ extern (C) pure nothrow @nogc
} }
} }
//============================================================================== // Most tests are now in in test_aa.d
// 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);
}
// test postblit for AA literals // test postblit for AA literals
unittest unittest
@ -982,34 +807,3 @@ unittest
GC.runFinalizers((cast(char*)(&entryDtor))[0 .. 1]); GC.runFinalizers((cast(char*)(&entryDtor))[0 .. 1]);
assert(T.dtor == 6 && T.postblit == 2); 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));
}

View File

@ -25,7 +25,7 @@ class TypeInfo_Ar : TypeInfo_Array
override string toString() const { return (F[]).stringof; } 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); return Array!F.hashOf(*cast(F[]*)p);
} }

View File

@ -25,7 +25,7 @@ class TypeInfo_Aq : TypeInfo_Array
override string toString() const { return (F[]).stringof; } 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); return Array!F.hashOf(*cast(F[]*)p);
} }

View File

@ -25,7 +25,7 @@ class TypeInfo_Ac : TypeInfo_Array
override string toString() const { return (F[]).stringof; } 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); return Array!F.hashOf(*cast(F[]*)p);
} }

View File

@ -25,7 +25,7 @@ class TypeInfo_Ad : TypeInfo_Array
override string toString() const { return (F[]).stringof; } 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); return Array!F.hashOf(*cast(F[]*)p);
} }

View File

@ -25,7 +25,7 @@ class TypeInfo_Af : TypeInfo_Array
override string toString() const { return (F[]).stringof; } 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); return Array!F.hashOf(*cast(F[]*)p);
} }

View File

@ -14,7 +14,6 @@
module rt.typeinfo.ti_Ag; module rt.typeinfo.ti_Ag;
private import core.stdc.string; private import core.stdc.string;
private import rt.util.hash;
private import core.internal.string; private import core.internal.string;
// byte[] // byte[]
@ -25,10 +24,10 @@ class TypeInfo_Ag : TypeInfo_Array
override string toString() const { return "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; 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 bool equals(in void* p1, in void* p2) const
@ -118,54 +117,10 @@ class TypeInfo_Aa : TypeInfo_Ah
{ {
override string toString() const { return "char[]"; } 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; char[] s = *cast(char[]*)p;
size_t hash = 0; return hashOf(s);
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;
} }
override @property inout(TypeInfo) next() inout override @property inout(TypeInfo) next() inout

View File

@ -14,7 +14,6 @@
module rt.typeinfo.ti_Aint; module rt.typeinfo.ti_Aint;
private import core.stdc.string; private import core.stdc.string;
private import rt.util.hash;
extern (C) void[] _adSort(void[] a, TypeInfo ti); extern (C) void[] _adSort(void[] a, TypeInfo ti);
@ -26,10 +25,11 @@ class TypeInfo_Ai : TypeInfo_Array
override string toString() const { return "int[]"; } 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; // Hash as if unsigned.
return rt.util.hash.hashOf(s, 0); const s = *cast(const uint[]*)p;
return hashOf(s);
} }
override bool equals(in void* p1, in void* p2) const override bool equals(in void* p1, in void* p2) const

View File

@ -14,7 +14,6 @@
module rt.typeinfo.ti_Along; module rt.typeinfo.ti_Along;
private import core.stdc.string; private import core.stdc.string;
private import rt.util.hash;
// long[] // long[]
@ -24,10 +23,11 @@ class TypeInfo_Al : TypeInfo_Array
override string toString() const { return "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; // Hash as if unsigned.
return rt.util.hash.hashOf(s, 0); const s = *cast(const ulong[]*)p;
return hashOf(s);
} }
override bool equals(in void* p1, in void* p2) const override bool equals(in void* p1, in void* p2) const

View File

@ -25,7 +25,7 @@ class TypeInfo_Ae : TypeInfo_Array
override string toString() const { return (F[]).stringof; } 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); return Array!F.hashOf(*cast(F[]*)p);
} }

View File

@ -14,7 +14,6 @@
module rt.typeinfo.ti_Ashort; module rt.typeinfo.ti_Ashort;
private import core.stdc.string; private import core.stdc.string;
private import rt.util.hash;
// short[] // short[]
@ -24,10 +23,11 @@ class TypeInfo_As : TypeInfo_Array
override string toString() const { return "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; // Hash as if unsigned.
return rt.util.hash.hashOf(s, 0); const s = *cast(const ushort[]*)p;
return hashOf(s);
} }
override bool equals(in void* p1, in void* p2) const override bool equals(in void* p1, in void* p2) const

View File

@ -22,7 +22,7 @@ class TypeInfo_C : TypeInfo
//pure: //pure:
//nothrow: //nothrow:
override size_t getHash(in void* p) override size_t getHash(scope const void* p)
{ {
Object o = *cast(Object*)p; Object o = *cast(Object*)p;
return o ? o.toHash() : 0; return o ? o.toHash() : 0;

View File

@ -24,9 +24,9 @@ class TypeInfo_g : TypeInfo
override string toString() const pure nothrow @safe { return "byte"; } 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 bool equals(in void* p1, in void* p2)

View File

@ -27,7 +27,7 @@ class TypeInfo_r : TypeInfo
override string toString() const { return F.stringof; } 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); return Floating!F.hashOf(*cast(F*)p);
} }

View File

@ -13,8 +13,6 @@
*/ */
module rt.typeinfo.ti_cent; module rt.typeinfo.ti_cent;
private import rt.util.hash;
static if (is(cent)): static if (is(cent)):
// cent // cent
@ -28,9 +26,10 @@ class TypeInfo_zi : TypeInfo
override string toString() const pure nothrow @safe { return "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 bool equals(in void* p1, in void* p2)

View File

@ -27,7 +27,7 @@ class TypeInfo_q : TypeInfo
override string toString() const { return F.stringof; } 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); return Floating!F.hashOf(*cast(F*)p);
} }

View File

@ -24,9 +24,9 @@ class TypeInfo_a : TypeInfo
override string toString() const pure nothrow @safe { return "char"; } 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 bool equals(in void* p1, in void* p2)

View File

@ -27,7 +27,7 @@ class TypeInfo_c : TypeInfo
override string toString() const { return F.stringof; } 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); return Floating!F.hashOf(*cast(F*)p);
} }

View File

@ -24,9 +24,9 @@ class TypeInfo_w : TypeInfo
override string toString() const pure nothrow @safe { return "dchar"; } 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) override bool equals(in void* p1, in void* p2)

View File

@ -13,7 +13,6 @@
*/ */
module rt.typeinfo.ti_delegate; module rt.typeinfo.ti_delegate;
private import rt.util.hash;
// delegate // delegate
@ -26,9 +25,9 @@ class TypeInfo_D : TypeInfo
pure: pure:
nothrow: 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 bool equals(in void* p1, in void* p2)

View File

@ -27,7 +27,7 @@ class TypeInfo_d : TypeInfo
override string toString() const { return F.stringof; } 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); return Floating!F.hashOf(*cast(F*)p);
} }

View File

@ -27,7 +27,7 @@ class TypeInfo_f : TypeInfo
override string toString() const { return F.stringof; } 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); return Floating!F.hashOf(*cast(F*)p);
} }

View File

@ -24,9 +24,9 @@ class TypeInfo_i : TypeInfo
override string toString() const pure nothrow @safe { return "int"; } 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) override bool equals(in void* p1, in void* p2)

View File

@ -13,8 +13,6 @@
*/ */
module rt.typeinfo.ti_long; module rt.typeinfo.ti_long;
private import rt.util.hash;
// long // long
class TypeInfo_l : TypeInfo class TypeInfo_l : TypeInfo
@ -26,9 +24,13 @@ class TypeInfo_l : TypeInfo
override string toString() const pure nothrow @safe { return "long"; } 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 bool equals(in void* p1, in void* p2)

View File

@ -19,7 +19,7 @@ class TypeInfo_n : TypeInfo
{ {
override string toString() const @safe { return "typeof(null)"; } 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; return 0;
} }

View File

@ -23,9 +23,10 @@ class TypeInfo_P : TypeInfo
pure: pure:
nothrow: 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 bool equals(in void* p1, in void* p2)

View File

@ -27,7 +27,7 @@ class TypeInfo_e : TypeInfo
override string toString() const { return F.stringof; } 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); return Floating!F.hashOf(*cast(F*)p);
} }

View File

@ -24,9 +24,9 @@ class TypeInfo_s : TypeInfo
override string toString() const pure nothrow @safe { return "short"; } 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 bool equals(in void* p1, in void* p2)

View File

@ -24,9 +24,9 @@ class TypeInfo_h : TypeInfo
override string toString() const pure nothrow @safe { return "ubyte"; } 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) override bool equals(in void* p1, in void* p2)

View File

@ -13,8 +13,6 @@
*/ */
module rt.typeinfo.ti_ucent; module rt.typeinfo.ti_ucent;
private import rt.util.hash;
static if (is(ucent)): static if (is(ucent)):
// ucent // ucent
@ -28,9 +26,9 @@ class TypeInfo_zk : TypeInfo
override string toString() const pure nothrow @safe { return "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 bool equals(in void* p1, in void* p2)

View File

@ -24,9 +24,9 @@ class TypeInfo_k : TypeInfo
override string toString() const pure nothrow @safe { return "uint"; } 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) override bool equals(in void* p1, in void* p2)

View File

@ -13,7 +13,6 @@
*/ */
module rt.typeinfo.ti_ulong; module rt.typeinfo.ti_ulong;
private import rt.util.hash;
// ulong // ulong
@ -26,9 +25,12 @@ class TypeInfo_m : TypeInfo
override string toString() const pure nothrow @safe { return "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 bool equals(in void* p1, in void* p2)

View File

@ -24,9 +24,9 @@ class TypeInfo_t : TypeInfo
override string toString() const pure nothrow @safe { return "ushort"; } 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 bool equals(in void* p1, in void* p2)

View File

@ -24,7 +24,7 @@ class TypeInfo_v : TypeInfo
override string toString() const pure nothrow @safe { return "void"; } 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); assert(0);
} }

View File

@ -24,9 +24,9 @@ class TypeInfo_u : TypeInfo
override string toString() { return "wchar"; } 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) override bool equals(in void* p1, in void* p2)

View File

@ -146,11 +146,10 @@ private:
static hash_t hashOf(in ref Key key) @trusted static hash_t hashOf(in ref Key key) @trusted
{ {
import rt.util.hash : hashOf;
static if (is(Key U : U[])) static if (is(Key U : U[]))
return hashOf(key, 0); return .hashOf(key, 0);
else else
return hashOf((&key)[0 .. 1], 0); return .hashOf((&key)[0 .. 1], 0);
} }
@property hash_t mask() const @property hash_t mask() const

View File

@ -1,107 +0,0 @@
/**
* 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);
}

View File

@ -6,6 +6,7 @@
* Authors: Kenji Hara * Authors: Kenji Hara
*/ */
module rt.util.typeinfo; module rt.util.typeinfo;
static import core.internal.hash;
template Floating(T) template Floating(T)
if (is(T == float) || is(T == double) || is(T == real)) if (is(T == float) || is(T == double) || is(T == real))
@ -32,19 +33,7 @@ if (is(T == float) || is(T == double) || is(T == real))
return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1);
} }
size_t hashOf(T value) @trusted public alias hashOf = core.internal.hash.hashOf;
{
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);
}
}
} }
template Floating(T) template Floating(T)
if (is(T == cfloat) || is(T == cdouble) || is(T == creal)) if (is(T == cfloat) || is(T == cdouble) || is(T == creal))
@ -73,13 +62,7 @@ if (is(T == cfloat) || is(T == cdouble) || is(T == creal))
return result; return result;
} }
size_t hashOf(T value) @trusted public alias hashOf = core.internal.hash.hashOf;
{
if (value == 0 + 0i)
value = 0 + 0i;
import rt.util.hash;
return rt.util.hash.hashOf((&value)[0 .. 1], 0);
}
} }
template Array(T) template Array(T)
@ -118,13 +101,7 @@ if (is(T == float) || is(T == double) || is(T == real) ||
return 0; return 0;
} }
size_t hashOf(T[] value) public alias hashOf = core.internal.hash.hashOf;
{
size_t h = 0;
foreach (e; value)
h += Floating!T.hashOf(e);
return h;
}
} }
version (unittest) version (unittest)
@ -247,7 +224,7 @@ unittest
{ {
assert(f1 == 0 + 0i); assert(f1 == 0 + 0i);
assert(f1 == f2); assert(f1 == f2);
assert(f1 !is f2); assert(f1 !is f2);
ti = typeid(F); ti = typeid(F);
assert(ti.getHash(&f1) == ti.getHash(&f2)); assert(ti.getHash(&f1) == ti.getHash(&f2));

View File

@ -0,0 +1,29 @@
# 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

View File

@ -0,0 +1,856 @@
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);
}

View File

@ -0,0 +1,29 @@
# 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

View File

@ -0,0 +1,540 @@
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);
}