e613d99266
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
857 lines
17 KiB
D
857 lines
17 KiB
D
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);
|
|
}
|