| 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); |
| } |