blob: 11ad2f90ff1d00f4c17c3fe5f1320b87e7e7a809 [file] [log] [blame]
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();
issue20440();
issue21442();
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() @safe
{
static struct BadValue
{
int x;
this(this) @system { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe
alias x this;
}
BadValue[int] aa;
// FIXME: Should be @system because of the postblit
if (false)
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); }();
}
/// Test that `require` works even with types whose opAssign
/// doesn't return a reference to the receiver.
/// https://issues.dlang.org/show_bug.cgi?id=20440
void issue20440() @safe
{
static struct S
{
int value;
auto opAssign(S s) {
this.value = s.value;
return this;
}
}
S[S] aa;
assert(aa.require(S(1), S(2)) == S(2));
assert(aa[S(1)] == S(2));
}
///
void issue21442()
{
import core.memory;
size_t[size_t] glob;
class Foo
{
size_t count;
this (size_t entries) @safe
{
this.count = entries;
foreach (idx; 0 .. entries)
glob[idx] = idx;
}
~this () @safe
{
foreach (idx; 0 .. this.count)
glob.remove(idx);
}
}
void bar () @safe
{
Foo f = new Foo(16);
}
bar();
GC.collect(); // Needs to happen from a GC collection
}
/// 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);
}