main()1 void main()
2 {
3 testKeysValues1();
4 testKeysValues2();
5 testGet1();
6 testGet2();
7 testRequire1();
8 testRequire2();
9 testRequire3();
10 testUpdate1();
11 testUpdate2();
12 testByKey1();
13 testByKey2();
14 testByKey3();
15 testByKey4();
16 issue5842();
17 issue5842Expanded();
18 issue5925();
19 issue8583();
20 issue9052();
21 issue9119();
22 issue9852();
23 issue10381();
24 issue10720();
25 issue11761();
26 issue13078();
27 issue14104();
28 issue14626();
29 issue15290();
30 issue15367();
31 issue16974();
32 issue18071();
33 testIterationWithConst();
34 testStructArrayKey();
35 miscTests1();
36 miscTests2();
37 testRemove();
38 testZeroSizedValue();
39 testTombstonePurging();
40 testClear();
41 }
42
testKeysValues1()43 void testKeysValues1()
44 {
45 static struct T
46 {
47 byte b;
48 static size_t count;
49 this(this) { ++count; }
50 }
51 T[int] aa;
52 T t;
53 aa[0] = t;
54 aa[1] = t;
55 assert(T.count == 2);
56 auto vals = aa.values;
57 assert(vals.length == 2);
58 assert(T.count == 4);
59
60 T.count = 0;
61 int[T] aa2;
62 aa2[t] = 0;
63 assert(T.count == 1);
64 aa2[t] = 1;
65 assert(T.count == 1);
66 auto keys = aa2.keys;
67 assert(keys.length == 1);
68 assert(T.count == 2);
69 }
70
testKeysValues2()71 void testKeysValues2() nothrow pure
72 {
73 int[string] aa;
74
75 assert(aa.keys.length == 0);
76 assert(aa.values.length == 0);
77
78 aa["hello"] = 3;
79 assert(aa["hello"] == 3);
80 aa["hello"]++;
81 assert(aa["hello"] == 4);
82
83 assert(aa.length == 1);
84
85 string[] keys = aa.keys;
86 assert(keys.length == 1);
87 assert(keys[0] == "hello");
88
89 int[] values = aa.values;
90 assert(values.length == 1);
91 assert(values[0] == 4);
92
93 aa.rehash;
94 assert(aa.length == 1);
95 assert(aa["hello"] == 4);
96
97 aa["foo"] = 1;
98 aa["bar"] = 2;
99 aa["batz"] = 3;
100
101 assert(aa.keys.length == 4);
102 assert(aa.values.length == 4);
103
104 foreach (a; aa.keys)
105 {
106 assert(a.length != 0);
107 assert(a.ptr != null);
108 }
109
110 foreach (v; aa.values)
111 {
112 assert(v != 0);
113 }
114 }
115
testGet1()116 void testGet1() @safe
117 {
118 int[string] aa;
119 int a;
120 foreach (val; aa.byKeyValue)
121 {
122 ++aa[val.key];
123 a = val.value;
124 }
125 }
126
testGet2()127 void testGet2()
128 {
129 static class T
130 {
131 static size_t count;
132 this() { ++count; }
133 }
134
135 T[string] aa;
136
137 auto a = new T;
138 aa["foo"] = a;
139 assert(T.count == 1);
140 auto b = aa.get("foo", new T);
141 assert(T.count == 1);
142 assert(b is a);
143 auto c = aa.get("bar", new T);
144 assert(T.count == 2);
145 assert(c !is a);
146
147 //Obviously get doesn't add.
148 assert("bar" !in aa);
149 }
150
testRequire1()151 void testRequire1()
152 {
153 static class T
154 {
155 static size_t count;
156 this() { ++count; }
157 }
158
159 T[string] aa;
160
161 auto a = new T;
162 aa["foo"] = a;
163 assert(T.count == 1);
164 auto b = aa.require("foo", new T);
165 assert(T.count == 1);
166 assert(b is a);
167 auto c = aa.require("bar", null);
168 assert(T.count == 1);
169 assert(c is null);
170 assert("bar" in aa);
171 auto d = aa.require("bar", new T);
172 assert(d is null);
173 auto e = aa.require("baz", new T);
174 assert(T.count == 2);
175 assert(e !is a);
176
177 assert("baz" in aa);
178
179 bool created = false;
180 auto f = aa.require("qux", { created = true; return new T; }());
181 assert(created == true);
182
183 T g;
184 auto h = aa.require("qux", { g = new T; return g; }());
185 assert(g !is h);
186 }
187
testRequire2()188 void testRequire2()
189 {
190 static struct S
191 {
192 int value;
193 }
194
195 S[string] aa;
196
197 aa.require("foo").value = 1;
198 assert(aa == ["foo" : S(1)]);
199
200 aa["bar"] = S(2);
201 auto a = aa.require("bar", S(3));
202 assert(a == S(2));
203
204 auto b = aa["bar"];
205 assert(b == S(2));
206
207 S* c = &aa.require("baz", S(4));
208 assert(c is &aa["baz"]);
209 assert(*c == S(4));
210
211 assert("baz" in aa);
212
213 auto d = aa["baz"];
214 assert(d == S(4));
215 }
216
testRequire3()217 void testRequire3() pure
218 {
219 string[string] aa;
220
221 auto a = aa.require("foo", "bar");
222 assert("foo" in aa);
223 }
224
225
testUpdate1()226 void testUpdate1()
227 {
228 static class C {}
229 C[string] aa;
230
231 C orig = new C;
232 aa["foo"] = orig;
233
234 C newer;
235 C older;
236
237 void test(string key)
238 {
239 aa.update(key, {
240 newer = new C;
241 return newer;
242 }, (ref C c) {
243 older = c;
244 newer = new C;
245 return newer;
246 });
247 }
248
249 test("foo");
250 assert(older is orig);
251 assert(newer is aa["foo"]);
252
253 test("bar");
254 assert(newer is aa["bar"]);
255 }
256
testUpdate2()257 void testUpdate2()
258 {
259 static class C {}
260 C[string] aa;
261
262 auto created = false;
263 auto updated = false;
264
265 class Creator
266 {
267 C opCall()
268 {
269 created = true;
270 return new C();
271 }
272 }
273
274 class Updater
275 {
276 C opCall(ref C)
277 {
278 updated = true;
279 return new C();
280 }
281 }
282
283 aa.update("foo", new Creator, new Updater);
284 assert(created);
285 aa.update("foo", new Creator, new Updater);
286 assert(updated);
287 }
288
testByKey1()289 void testByKey1()
290 {
291 static assert(!__traits(compiles,
292 () @safe {
293 struct BadValue
294 {
295 int x;
296 this(this) @safe { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe
297 alias x this;
298 }
299
300 BadValue[int] aa;
301 () @safe { auto x = aa.byKey.front; } ();
302 }
303 ));
304 }
305
testByKey2()306 void testByKey2() nothrow pure
307 {
308 int[int] a;
309 foreach (i; a.byKey)
310 {
311 assert(false);
312 }
313 foreach (i; a.byValue)
314 {
315 assert(false);
316 }
317 }
318
testByKey3()319 void testByKey3() /*nothrow*/ pure
320 {
321 auto a = [ 1:"one", 2:"two", 3:"three" ];
322 auto b = a.dup;
323 assert(b == [ 1:"one", 2:"two", 3:"three" ]);
324
325 int[] c;
326 foreach (k; a.byKey)
327 {
328 c ~= k;
329 }
330
331 assert(c.length == 3);
332 assert(c[0] == 1 || c[1] == 1 || c[2] == 1);
333 assert(c[0] == 2 || c[1] == 2 || c[2] == 2);
334 assert(c[0] == 3 || c[1] == 3 || c[2] == 3);
335 }
336
testByKey4()337 void testByKey4() nothrow pure
338 {
339 string[] keys = ["a", "b", "c", "d", "e", "f"];
340
341 // Test forward range capabilities of byKey
342 {
343 int[string] aa;
344 foreach (key; keys)
345 aa[key] = 0;
346
347 auto keyRange = aa.byKey();
348 auto savedKeyRange = keyRange.save;
349
350 // Consume key range once
351 size_t keyCount = 0;
352 while (!keyRange.empty)
353 {
354 aa[keyRange.front]++;
355 keyCount++;
356 keyRange.popFront();
357 }
358
359 foreach (key; keys)
360 {
361 assert(aa[key] == 1);
362 }
363 assert(keyCount == keys.length);
364
365 // Verify it's possible to iterate the range the second time
366 keyCount = 0;
367 while (!savedKeyRange.empty)
368 {
369 aa[savedKeyRange.front]++;
370 keyCount++;
371 savedKeyRange.popFront();
372 }
373
374 foreach (key; keys)
375 {
376 assert(aa[key] == 2);
377 }
378 assert(keyCount == keys.length);
379 }
380
381 // Test forward range capabilities of byValue
382 {
383 size_t[string] aa;
384 foreach (i; 0 .. keys.length)
385 {
386 aa[keys[i]] = i;
387 }
388
389 auto valRange = aa.byValue();
390 auto savedValRange = valRange.save;
391
392 // Consume value range once
393 int[] hasSeen;
394 hasSeen.length = keys.length;
395 while (!valRange.empty)
396 {
397 assert(hasSeen[valRange.front] == 0);
398 hasSeen[valRange.front]++;
399 valRange.popFront();
400 }
401
402 foreach (sawValue; hasSeen) { assert(sawValue == 1); }
403
404 // Verify it's possible to iterate the range the second time
405 hasSeen = null;
406 hasSeen.length = keys.length;
407 while (!savedValRange.empty)
408 {
409 assert(!hasSeen[savedValRange.front]);
410 hasSeen[savedValRange.front] = true;
411 savedValRange.popFront();
412 }
413
414 foreach (sawValue; hasSeen) { assert(sawValue); }
415 }
416 }
417
issue5842()418 void issue5842() pure nothrow
419 {
420 string[string] test = null;
421 test["test1"] = "test1";
422 test.remove("test1");
423 test.rehash;
424 test["test3"] = "test3"; // causes divide by zero if rehash broke the AA
425 }
426
427 /// expanded test for 5842: increase AA size past the point where the AA
428 /// stops using binit, in order to test another code path in rehash.
issue5842Expanded()429 void issue5842Expanded() pure nothrow
430 {
431 int[int] aa;
432 foreach (int i; 0 .. 32)
433 aa[i] = i;
434 foreach (int i; 0 .. 32)
435 aa.remove(i);
436 aa.rehash;
437 aa[1] = 1;
438 }
439
issue5925()440 void issue5925() nothrow pure
441 {
442 const a = [4:0];
443 const b = [4:0];
444 assert(a == b);
445 }
446
447 /// test for bug 8583: ensure Slot and aaA are on the same page wrt value alignment
issue8583()448 void issue8583() nothrow pure
449 {
450 string[byte] aa0 = [0: "zero"];
451 string[uint[3]] aa1 = [[1,2,3]: "onetwothree"];
452 ushort[uint[3]] aa2 = [[9,8,7]: 987];
453 ushort[uint[4]] aa3 = [[1,2,3,4]: 1234];
454 string[uint[5]] aa4 = [[1,2,3,4,5]: "onetwothreefourfive"];
455
456 assert(aa0.byValue.front == "zero");
457 assert(aa1.byValue.front == "onetwothree");
458 assert(aa2.byValue.front == 987);
459 assert(aa3.byValue.front == 1234);
460 assert(aa4.byValue.front == "onetwothreefourfive");
461 }
462
issue9052()463 void issue9052() nothrow pure
464 {
465 static struct Json {
466 Json[string] aa;
467 void opAssign(Json) {}
468 size_t length() const { return aa.length; }
469 // This length() instantiates AssociativeArray!(string, const(Json)) to call AA.length(), and
470 // inside ref Slot opAssign(Slot p); (which is automatically generated by compiler in Slot),
471 // this.value = p.value would actually fail, because both side types of the assignment
472 // are const(Json).
473 }
474 }
475
476 void issue9119()
477 {
478 int[string] aa;
479 assert(aa.byKeyValue.empty);
480
481 aa["a"] = 1;
482 aa["b"] = 2;
483 aa["c"] = 3;
484
485 auto pairs = aa.byKeyValue;
486
487 auto savedPairs = pairs.save;
488 size_t count = 0;
489 while (!pairs.empty)
490 {
491 assert(pairs.front.key in aa);
492 assert(pairs.front.value == aa[pairs.front.key]);
493 count++;
494 pairs.popFront();
495 }
496 assert(count == aa.length);
497
498 // Verify that saved range can iterate over the AA again
499 count = 0;
500 while (!savedPairs.empty)
501 {
502 assert(savedPairs.front.key in aa);
503 assert(savedPairs.front.value == aa[savedPairs.front.key]);
504 count++;
505 savedPairs.popFront();
506 }
507 assert(count == aa.length);
508 }
509
510 void issue9852() nothrow pure
511 {
512 // Original test case (revised, original assert was wrong)
513 int[string] a;
514 a["foo"] = 0;
515 a.remove("foo");
516 assert(a == null); // should not crash
517
518 int[string] b;
519 assert(b is null);
520 assert(a == b); // should not deref null
521 assert(b == a); // ditto
522
523 int[string] c;
524 c["a"] = 1;
525 assert(a != c); // comparison with empty non-null AA
526 assert(c != a);
527 assert(b != c); // comparison with null AA
528 assert(c != b);
529 }
530
531 void issue10381()
532 {
533 alias II = int[int];
534 II aa1 = [0 : 1];
535 II aa2 = [0 : 1];
536 II aa3 = [0 : 2];
537 assert(aa1 == aa2); // Passes
538 assert(typeid(II).equals(&aa1, &aa2));
539 assert(!typeid(II).equals(&aa1, &aa3));
540 }
541
542 void issue10720() nothrow pure
543 {
544 static struct NC
545 {
546 @disable this(this) { }
547 }
548
549 NC[string] aa;
550 static assert(!is(aa.nonExistingField));
551 }
552
553 /// bug 11761: test forward range functionality
554 void issue11761() pure nothrow
555 {
556 auto aa = ["a": 1];
557
558 void testFwdRange(R, T)(R fwdRange, T testValue)
559 {
560 assert(!fwdRange.empty);
561 assert(fwdRange.front == testValue);
562 static assert(is(typeof(fwdRange.save) == typeof(fwdRange)));
563
564 auto saved = fwdRange.save;
565 fwdRange.popFront();
566 assert(fwdRange.empty);
567
568 assert(!saved.empty);
569 assert(saved.front == testValue);
570 saved.popFront();
571 assert(saved.empty);
572 }
573
574 testFwdRange(aa.byKey, "a");
575 testFwdRange(aa.byValue, 1);
576 //testFwdRange(aa.byPair, tuple("a", 1));
577 }
578
579 void issue13078() nothrow pure
580 {
581 shared string[][string] map;
582 map.rehash;
583 }
584
585 void issue14104()
586 {
587 import core.stdc.stdio;
588
589 alias K = const(ubyte)*;
590 size_t[K] aa;
591 immutable key = cast(K)(cast(size_t) uint.max + 1);
592 aa[key] = 12;
593 assert(key in aa);
594 }
595
596 void issue14626()
597 {
598 static struct S
599 {
600 string[string] aa;
601 inout(string) key() inout { return aa.byKey().front; }
602 inout(string) val() inout { return aa.byValue().front; }
603 auto keyval() inout { return aa.byKeyValue().front; }
604 }
605
606 S s = S(["a":"b"]);
607 assert(s.key() == "a");
608 assert(s.val() == "b");
609 assert(s.keyval().key == "a");
610 assert(s.keyval().value == "b");
611
612 void testInoutKeyVal(inout(string) key)
613 {
614 inout(string)[typeof(key)] aa;
615
616 foreach (i; aa.byKey()) {}
617 foreach (i; aa.byValue()) {}
618 foreach (i; aa.byKeyValue()) {}
619 }
620
621 const int[int] caa;
622 static assert(is(typeof(caa.byValue().front) == const int));
623 }
624
625 /// test duplicated keys in AA literal
626 /// https://issues.dlang.org/show_bug.cgi?id=15290
627 void issue15290()
628 {
629 string[int] aa = [ 0: "a", 0: "b" ];
630 assert(aa.length == 1);
631 assert(aa.keys == [ 0 ]);
632 }
633
634 void issue15367()
635 {
636 void f1() {}
637 void f2() {}
638
639 // TypeInfo_Delegate.getHash
640 int[void delegate()] aa;
641 assert(aa.length == 0);
642 aa[&f1] = 1;
643 assert(aa.length == 1);
644 aa[&f1] = 1;
645 assert(aa.length == 1);
646
647 auto a1 = [&f2, &f1];
648 auto a2 = [&f2, &f1];
649
650 // TypeInfo_Delegate.equals
651 for (auto i = 0; i < 2; i++)
652 assert(a1[i] == a2[i]);
653 assert(a1 == a2);
654
655 // TypeInfo_Delegate.compare
656 for (auto i = 0; i < 2; i++)
657 assert(a1[i] <= a2[i]);
658 assert(a1 <= a2);
659 }
660
661 /// test AA as key
662 /// https://issues.dlang.org/show_bug.cgi?id=16974
663 void issue16974()
664 {
665 int[int] a = [1 : 2], a2 = [1 : 2];
666
667 assert([a : 3] == [a : 3]);
668 assert([a : 3] == [a2 : 3]);
669
670 assert(typeid(a).getHash(&a) == typeid(a).getHash(&a));
671 assert(typeid(a).getHash(&a) == typeid(a).getHash(&a2));
672 }
673
674 /// test safety for alias-this'd AA that have unsafe opCast
675 /// https://issues.dlang.org/show_bug.cgi?id=18071
676 void issue18071()
677 {
678 static struct Foo
679 {
680 int[int] aa;
681 auto opCast() pure nothrow @nogc
682 {
683 *cast(uint*)0xdeadbeef = 0xcafebabe;// unsafe
684 return null;
685 }
686 alias aa this;
687 }
688
689 Foo f;
690 () @safe { assert(f.byKey.empty); }();
691 }
692
693 /// Verify iteration with const.
694 void testIterationWithConst()
695 {
696 auto aa = [1:2, 3:4];
697 foreach (const t; aa.byKeyValue)
698 {
699 auto k = t.key;
700 auto v = t.value;
701 }
702 }
703
704 void testStructArrayKey() @safe
705 {
706 struct S
707 {
708 int i;
709 const @safe nothrow:
710 hash_t toHash() { return 0; }
711 bool opEquals(const S) { return true; }
712 int opCmp(const S) { return 0; }
713 }
714
715 int[S[]] aa = [[S(11)] : 13];
716 assert(aa[[S(12)]] == 13);
717 }
718
719 void miscTests1() pure nothrow
720 {
721 string[int] key1 = [1 : "true", 2 : "false"];
722 string[int] key2 = [1 : "false", 2 : "true"];
723 string[int] key3;
724
725 // AA lits create a larger hashtable
726 int[string[int]] aa1 = [key1 : 100, key2 : 200, key3 : 300];
727
728 // Ensure consistent hash values are computed for key1
729 assert((key1 in aa1) !is null);
730
731 // Manually assigning to an empty AA creates a smaller hashtable
732 int[string[int]] aa2;
733 aa2[key1] = 100;
734 aa2[key2] = 200;
735 aa2[key3] = 300;
736
737 assert(aa1 == aa2);
738
739 // Ensure binary-independence of equal hash keys
740 string[int] key2a;
741 key2a[1] = "false";
742 key2a[2] = "true";
743
744 assert(aa1[key2a] == 200);
745 }
746
747 void miscTests2()
748 {
749 int[int] aa;
750 foreach (k, v; aa)
751 assert(false);
752 foreach (v; aa)
753 assert(false);
754 assert(aa.byKey.empty);
755 assert(aa.byValue.empty);
756 assert(aa.byKeyValue.empty);
757
758 size_t n;
759 aa = [0 : 3, 1 : 4, 2 : 5];
760 foreach (k, v; aa)
761 {
762 n += k;
763 assert(k >= 0 && k < 3);
764 assert(v >= 3 && v < 6);
765 }
766 assert(n == 3);
767 n = 0;
768
769 foreach (v; aa)
770 {
771 n += v;
772 assert(v >= 3 && v < 6);
773 }
774 assert(n == 12);
775
776 n = 0;
777 foreach (k, v; aa)
778 {
779 ++n;
780 break;
781 }
782 assert(n == 1);
783
784 n = 0;
785 foreach (v; aa)
786 {
787 ++n;
788 break;
789 }
790 assert(n == 1);
791 }
792
793 void testRemove()
794 {
795 int[int] aa;
796 assert(!aa.remove(0));
797 aa = [0 : 1];
798 assert(aa.remove(0));
799 assert(!aa.remove(0));
800 aa[1] = 2;
801 assert(!aa.remove(0));
802 assert(aa.remove(1));
803
804 assert(aa.length == 0);
805 assert(aa.byKey.empty);
806 }
807
808 /// test zero sized value (hashset)
809 void testZeroSizedValue()
810 {
811 alias V = void[0];
812 auto aa = [0 : V.init];
813 assert(aa.length == 1);
814 assert(aa.byKey.front == 0);
815 assert(aa.byValue.front == V.init);
816 aa[1] = V.init;
817 assert(aa.length == 2);
818 aa[0] = V.init;
819 assert(aa.length == 2);
820 assert(aa.remove(0));
821 aa[0] = V.init;
822 assert(aa.length == 2);
823 assert(aa == [0 : V.init, 1 : V.init]);
824 }
825
826 void testTombstonePurging()
827 {
828 int[int] aa;
829 foreach (i; 0 .. 6)
830 aa[i] = i;
831 foreach (i; 0 .. 6)
832 assert(aa.remove(i));
833 foreach (i; 6 .. 10)
834 aa[i] = i;
835 assert(aa.length == 4);
836 foreach (i; 6 .. 10)
837 assert(i in aa);
838 }
839
840 void testClear()
841 {
842 int[int] aa;
843 assert(aa.length == 0);
844 foreach (i; 0 .. 100)
845 aa[i] = i * 2;
846 assert(aa.length == 100);
847 auto aa2 = aa;
848 assert(aa2.length == 100);
849 aa.clear();
850 assert(aa.length == 0);
851 assert(aa2.length == 0);
852
853 aa2[5] = 6;
854 assert(aa.length == 1);
855 assert(aa[5] == 6);
856 }
857