1 module dstruct.map;
2 
3 import core.memory;
4 import core.exception;
5 
6 import core.stdc..string: memcpy, memset;
7 
8 import std.range : ElementType;
9 import std.traits : Unqual, isPointer;
10 
11 import dstruct.support;
12 
13 private enum EntryState {
14     empty = 0,
15     occupied = 1,
16     deleted = 2,
17 }
18 
19 private enum SearchFor {
20     empty = 1,
21     occupied = 2,
22     deleted = 4,
23     notDeleted = empty | occupied,
24     notOccupied = empty | deleted,
25     any = empty | occupied | deleted,
26 }
27 
28 private enum is64Bit = is(size_t == ulong);
29 
30 // Given a size, compute the space in bytes required for a member when
31 // the member is aligned to a size_t.sizeof boundary.
32 private size_t alignedSize(size_t size) {
33     if (size % size_t.sizeof) {
34         // If there's a remainder, then our boundary is a little ahead.
35         return cast(size_t) (size / size_t.sizeof) + 1 * size_t.sizeof;
36     }
37 
38     // If the size is cleanly divisible, it will fit right into place.
39     return size;
40 }
41 
42 private enum isHashIdentical(T) =
43     is(T : uint)
44     | is(T : char)
45     | is(T : wchar)
46     | is(T : dchar)
47     | isPointer!T
48     | (is64Bit && is(T : ulong));
49 
50 /**
51  * An item from a map.
52  *
53  * The keys and the values in the map are references into the map itself.
54  */
55 struct Entry(K, V) {
56 private:
57     K _key;
58     V _value;
59     EntryState _state = EntryState.empty;
60 
61     static if (!isHashIdentical!K) {
62         size_t _hash;
63     }
64 public:
65     /**
66      * A key from the map.
67      */
68     @nogc @safe pure nothrow
69     @property ref inout(K) key() inout {
70         return _key;
71     }
72 
73     /**
74      * A value from the map.
75      */
76     @nogc @safe pure nothrow
77     @property ref inout(V) value() inout {
78         return _value;
79     }
80 }
81 
82 // TODO: Test different entry sizes.
83 
84 static if(__VERSION__ < 2066) {
85     private alias SafeGetHashType = size_t delegate(const(void*)) pure nothrow;
86 } else {
87     // Use mixin to hide UDA errors from old D compilers, it will never
88     // execute the mixin, so it won't report an error in syntax.
89     mixin("private alias SafeGetHashType = size_t delegate(const(void*)) @nogc pure nothrow;");
90 }
91 
92 @nogc @trusted pure nothrow
93 private size_t computeHash(K)(ref K key) if (!isHashIdentical!K) {
94     // Cast so we can keep our function qualifiers.
95     return (cast(SafeGetHashType) &(typeid(K).getHash))(&key);
96 }
97 
98 @nogc @trusted pure nothrow
99 private size_t castHash(K)(K key) if (isHashIdentical!K) {
100     return cast(size_t) key;
101 }
102 
103 private enum size_t minimumBucketListSize = 8;
104 
105 @nogc @safe pure nothrow
106 private size_t newBucketListSize(size_t currentLength) {
107     return currentLength * 2;
108 }
109 
110 @trusted
111 private size_t bucketListSearch(SearchFor searchFor, K, V)
112 (ref const(Entry!(K, V)[]) bucketList, const(K) key)
113 if (isHashIdentical!K) {
114     size_t index = castHash(key) & (bucketList.length - 1);
115 
116     foreach(j; 1 .. bucketList.length) {
117         static if (searchFor == SearchFor.notOccupied) {
118             if (bucketList[index]._state != EntryState.occupied) {
119                 return index;
120             }
121 
122             if (bucketList[index]._key == key) {
123                 return index;
124             }
125         } else {
126             static if (searchFor & SearchFor.empty) {
127                 if (bucketList[index]._state == EntryState.empty) {
128                     return index;
129                 }
130             }
131 
132             static if (searchFor & SearchFor.deleted) {
133                 if (bucketList[index]._state == EntryState.deleted
134                 && bucketList[index]._key == key) {
135                     return index;
136                 }
137             }
138 
139             static if (searchFor & SearchFor.occupied) {
140                 if (bucketList[index]._state == EntryState.occupied
141                 && bucketList[index]._key == key) {
142                     return index;
143                 }
144             }
145         }
146 
147         index = (index + j) & (bucketList.length - 1);
148     }
149 
150     assert(false, "Slot not found!");
151 }
152 
153 @trusted
154 private size_t bucketListSearch(SearchFor searchFor, K, V)
155 (ref const(Entry!(K, V)[]) bucketList, size_t hash, const(K) key)
156 if (!isHashIdentical!K) {
157     size_t index = hash & (bucketList.length - 1);
158 
159     foreach(j; 1 .. bucketList.length) {
160         static if (searchFor == SearchFor.notOccupied) {
161             if (bucketList[index]._state != EntryState.occupied) {
162                 return index;
163             }
164 
165             if (bucketList[index]._hash == hash
166             && bucketList[index]._key == key) {
167                 return index;
168             }
169         } else {
170             static if (searchFor & SearchFor.empty) {
171                 if (bucketList[index]._state == EntryState.empty) {
172                     return index;
173                 }
174             }
175 
176             static if (searchFor & SearchFor.deleted) {
177                 if (bucketList[index]._state == EntryState.deleted
178                 && bucketList[index]._hash == hash
179                 && bucketList[index]._key == key) {
180                     return index;
181                 }
182 
183             }
184 
185             static if (searchFor & SearchFor.occupied) {
186                 if (bucketList[index]._state == EntryState.occupied
187                 && bucketList[index]._hash == hash
188                 && bucketList[index]._key == key) {
189                     return index;
190                 }
191             }
192         }
193 
194         index = (index + j) & (bucketList.length - 1);
195     }
196 
197     assert(false, "Slot not found!");
198 }
199 
200 // Add an entry into the bucket list.
201 // memcpy is used here because some types have immutable members which cannot
202 // be changed, so we have to force them into the array this way.
203 @nogc @trusted pure nothrow
204 private void setEntry(K, V)(ref Entry!(K, V)[] bucketList,
205 size_t index, auto ref K key, auto ref V value) {
206     enum valueOffset = alignedSize(K.sizeof);
207 
208     // Copy the key and value into the entry.
209     memcpy(cast(void*) &bucketList[index], &key, K.sizeof);
210     memcpy(cast(void*) &bucketList[index] + valueOffset, &value, V.sizeof);
211 
212     bucketList[index]._state = EntryState.occupied;
213 }
214 
215 @nogc @trusted pure nothrow
216 private void setEntry(K, V)(ref Entry!(K, V)[] bucketList,
217 size_t index, size_t hash, auto ref K key, auto ref V value) {
218     enum valueOffset = alignedSize(K.sizeof);
219 
220     // Copy the key and value into the entry.
221     memcpy(cast(void*) &bucketList[index], &key, K.sizeof);
222     memcpy(cast(void*) &bucketList[index] + valueOffset, &value, V.sizeof);
223 
224     bucketList[index]._hash = hash;
225     bucketList[index]._state = EntryState.occupied;
226 }
227 
228 // Update just the value for an entry.
229 @nogc @trusted pure nothrow
230 private void updateEntryValue(K, V)(ref Entry!(K, V)[] bucketList,
231 size_t index, auto ref V value) {
232     enum valueOffset = alignedSize(K.sizeof);
233 
234     memcpy(cast(void*) &bucketList[index] + valueOffset, &value, V.sizeof);
235 }
236 
237 @nogc @trusted pure nothrow
238 private void zeroEntryValue(K, V)(ref Entry!(K, V)[] bucketList,
239 size_t index) {
240     enum valueOffset = alignedSize(K.sizeof);
241 
242     memset(cast(void*) &bucketList[index] + valueOffset, 0, V.sizeof);
243 }
244 
245 @nogc @trusted pure nothrow
246 private bool thresholdPassed(size_t length, size_t bucketCount) {
247     return length * 2 >= bucketCount;
248 }
249 
250 /**
251  * This struct implements a hashmap type, much like the standard associative
252  * array type.
253  *
254  * This map should be almost totally usable in @safe pure nothrow functions.
255  *
256  * An empty map will be a valid object, and will not result in any allocations.
257  */
258 struct HashMap(K, V)
259 if(isAssignmentCopyable!(Unqual!K) && isAssignmentCopyable!(Unqual!V)) {
260     alias ThisType = typeof(this);
261 
262     private Entry!(K, V)[] _bucketList;
263     private size_t _length;
264 
265     /**
266      * Construct a hashmap reserving a minimum of :minimumSize: space
267      * for the bucket list. The actual space allocated may be some number
268      * larger than the requested size, but it will be enough to fit
269      * as many items as requested without another allocation.
270      *
271      * Params:
272      * minimumSize = The minimum size for the hashmap.
273      */
274     @safe pure nothrow
275     this(size_t minimumSize) {
276         if (minimumSize == 0) {
277             // 0 is a special case.
278             return;
279         }
280 
281         if (minimumSize <= minimumBucketListSize / 2) {
282             _bucketList = new Entry!(K, V)[](minimumBucketListSize);
283         } else {
284             // Find the next largest power of two which will fit this size.
285             size_t size = 8;
286 
287             while (thresholdPassed(minimumSize, size)) {
288                 size *= 2;
289             }
290 
291             _bucketList = new Entry!(K, V)[](newBucketListSize(size));
292         }
293     }
294 
295     @trusted
296     private void copyToBucketList(ref Entry!(K, V)[] newBucketList) const {
297         foreach(ref entry; _bucketList) {
298             if (entry._state != EntryState.occupied) {
299                 // Skip holes in the container.
300                 continue;
301             }
302 
303             static if (isHashIdentical!K) {
304                 size_t index =
305                     bucketListSearch!(SearchFor.empty, K, V)
306                     (newBucketList, cast(K) entry._key);
307 
308                 newBucketList.setEntry(
309                     index,
310                     cast(K) entry._key,
311                     cast(V) entry._value
312                 );
313             } else {
314                 size_t index =
315                     bucketListSearch!(SearchFor.empty, K, V)
316                     (newBucketList, entry._hash, cast(K) entry._key);
317 
318                 newBucketList.setEntry(
319                     index,
320                     entry._hash,
321                     cast(K) entry._key,
322                     cast(V) entry._value
323                 );
324             }
325         }
326     }
327 
328     @trusted
329     private void resize(size_t newBucketListLength) in {
330         assert(newBucketListLength > _bucketList.length);
331     } body {
332         auto newBucketList = new Entry!(K, V)[](newBucketListLength);
333 
334         copyToBucketList(newBucketList);
335 
336         _bucketList = newBucketList;
337     }
338 
339     /**
340      * Set a value in the map.
341      *
342      * Params:
343      *     key = The key in the map.
344      *     value = A value to set in the map.
345      */
346     void opIndexAssign(V value, K key) {
347         static if (!isHashIdentical!K) {
348             size_t hash = computeHash(key);
349         }
350 
351         if (_bucketList.length == 0) {
352             // 0 length is a special case.
353             _length = 1;
354             resize(minimumBucketListSize);
355 
356             static if (isHashIdentical!K) {
357                 size_t index = castHash(key) & (_bucketList.length - 1);
358 
359                 _bucketList.setEntry(index, key, value);
360             } else {
361                 size_t index = hash & (_bucketList.length - 1);
362 
363                 _bucketList.setEntry(index, hash, key, value);
364             }
365 
366             return;
367         }
368 
369         static if (isHashIdentical!K) {
370             size_t index =
371                 bucketListSearch!(SearchFor.notDeleted, K, V)
372                 (_bucketList, key);
373         } else {
374             size_t index =
375                 bucketListSearch!(SearchFor.notDeleted, K, V)
376                 (_bucketList, hash, key);
377         }
378 
379         if (_bucketList[index]._state != EntryState.occupied) {
380             // This slot is not occupied, so insert the entry here.
381             static if (isHashIdentical!K) {
382                 _bucketList.setEntry(index, key, value);
383             } else {
384                 _bucketList.setEntry(index, hash, key, value);
385             }
386 
387             ++_length;
388 
389             if (thresholdPassed(_length, _bucketList.length)) {
390                 // Resize the bucketList, as it passed the threshold.
391                 resize(newBucketListSize(_bucketList.length));
392             }
393         } else {
394             // We have this key already, so update the value.
395             _bucketList.updateEntryValue(index, value);
396         }
397     }
398 
399     /**
400      * Implement the 'in' operator for a map.
401      *
402      * The in operator on a map will return a pointer to a value, which will
403      * be null when no corresponding value is set for a given key.
404      *
405      * Params:
406      *     key = The key in the map.
407      *
408      * Returns:
409      *     A pointer to a value, a null pointer if a value is not set.
410      */
411     inout(V)* opBinaryRight(string op)(K key) inout if (op == "in") {
412         static if (isHashIdentical!K) {
413             size_t index =
414                 bucketListSearch!(SearchFor.notDeleted, K, V)
415                 (_bucketList, key);
416         } else {
417             size_t index =
418                 bucketListSearch!(SearchFor.notDeleted, K, V)
419                 (_bucketList, computeHash(key), key);
420         }
421 
422         if (_bucketList[index]._state == EntryState.empty) {
423             return null;
424         }
425 
426         return &(_bucketList[index]._value);
427     }
428 
429     /**
430      * Retrieve a value from the map.
431      *
432      * If a value is not set for the given key, a RangeError will be thrown.
433      *
434      * Params:
435      *     key = The key in the map.
436      *
437      * Returns:
438      *     A value from the map.
439      */
440     ref inout(V) opIndex(K key) inout {
441         static if (isHashIdentical!K) {
442             size_t index =
443                 bucketListSearch!(SearchFor.notDeleted, K, V)
444                 (_bucketList, key);
445         } else {
446             size_t index =
447                 bucketListSearch!(SearchFor.notDeleted, K, V)
448                 (_bucketList, computeHash(key), key);
449         }
450 
451         assert(
452             _bucketList[index]._state != EntryState.empty,
453             "Key not found in HashMap!"
454         );
455 
456         return _bucketList[index]._value;
457     }
458 
459     /**
460      * Get a value from the map, or return the given default value, which
461      * is lazy-evaluated.
462      *
463      * Params:
464      *     key = The key in the map.
465      *     def = A lazy default value.
466      *
467      * Returns:
468      *     A value from the map, or the default value.
469      */
470     V get(V2)(K key, lazy V2 def) const if(is(V2 : V)) {
471         static if (isHashIdentical!K) {
472             size_t index =
473                 bucketListSearch!(SearchFor.notDeleted, K, V)
474                 (_bucketList, key);
475         } else {
476             size_t index =
477                 bucketListSearch!(SearchFor.notDeleted, K, V)
478                 (_bucketList, computeHash(key), key);
479         }
480 
481         if (_bucketList[index]._state == EntryState.empty) {
482             return def();
483         }
484 
485         return _bucketList[index]._value;
486     }
487 
488     /**
489      * Get a value from the map, or return V.init if a value is not set for
490      * a given key.
491      *
492      * Params:
493      *     key = The key in the map.
494      *
495      * Returns:
496      *     A value from the map, or the default value.
497      */
498     inout(V) get(K key) inout {
499         static if (isHashIdentical!K) {
500             size_t index =
501                 bucketListSearch!(SearchFor.notDeleted, K, V)
502                 (_bucketList, key);
503         } else {
504             size_t index =
505                 bucketListSearch!(SearchFor.notDeleted, K, V)
506                 (_bucketList, computeHash(key), key);
507         }
508 
509 
510         if (_bucketList[index]._state == EntryState.empty) {
511             return V.init;
512         }
513 
514         return _bucketList[index]._value;
515     }
516 
517     /**
518      * Get or create a value from/in the map.
519      *
520      * Given a key, and a lazy evaluated default value,
521      * attempt to retrieve a value from the map. If a value for the given
522      * key is not set, set the provided default value in the map and
523      * return that.
524      *
525      * The value will be returned by reference.
526      *
527      * Params:
528      *     key = The key in the map.
529      *     def = A lazy default value.
530      *
531      * Returns:
532      *     A reference to the value in the map.
533      */
534     ref V setDefault(V2)(K key, lazy V2 value) if (is(V2 : V)) {
535         static if (!isHashIdentical!K) {
536             size_t hash = computeHash(key);
537         }
538 
539         if (_bucketList.length == 0) {
540             // 0 length is a special case.
541             _length = 1;
542             resize(minimumBucketListSize);
543 
544             static if (isHashIdentical!K) {
545                 size_t index = castHash(key) & (_bucketList.length - 1);
546 
547                 _bucketList.setEntry(index, key, V.init);
548             } else {
549                 size_t index = hash & (_bucketList.length - 1);
550 
551                 _bucketList.setEntry(index, hash, key, V.init);
552             }
553 
554             return _bucketList[index].value;
555         }
556 
557         static if (isHashIdentical!K) {
558             size_t index =
559                 bucketListSearch!(SearchFor.notDeleted, K, V)
560                 (_bucketList, key);
561         } else {
562             size_t index =
563                 bucketListSearch!(SearchFor.notDeleted, K, V)
564                 (_bucketList, hash, key);
565         }
566 
567         if (_bucketList[index]._state == EntryState.empty) {
568             // The entry is empty, so we can insert the value here.
569             static if (isHashIdentical!K) {
570                 _bucketList.setEntry(index, key, value());
571             } else {
572                 _bucketList.setEntry(index, hash, key, value());
573             }
574 
575             ++_length;
576 
577             if (thresholdPassed(_length, _bucketList.length)) {
578                 // Resize the bucketList, as it passed the threshold.
579                 resize(newBucketListSize(_bucketList.length));
580 
581                 // Update the index, it has now changed.
582                 static if (isHashIdentical!K) {
583                     index = bucketListSearch!(SearchFor.notDeleted, K, V)
584                         (_bucketList, key);
585                 } else {
586                     index = bucketListSearch!(SearchFor.notDeleted, K, V)
587                         (_bucketList, hash, key);
588                 }
589             }
590         }
591 
592         // Return a reference to the value.
593         return _bucketList[index]._value;
594     }
595 
596     /**
597      * Get or create a value from/in a hashmap.
598      *
599      * Given a key attempt to retrieve a value from the hashmap.
600      * If a value for the given key is not set, set the value in
601      * the associative array to the default value for the value's type.
602      *
603      * The value will be returned by reference.
604      *
605      * Params:
606      *     key = The key in the map.
607      *
608      * Returns:
609      *     A reference to the value in the map.
610      */
611     ref V setDefault(K key) {
612         static if (!isHashIdentical!K) {
613             size_t hash = computeHash(key);
614         }
615 
616         if (_bucketList.length == 0) {
617             // 0 length is a special case.
618             _length = 1;
619             resize(minimumBucketListSize);
620 
621             static if (isHashIdentical!K) {
622                 size_t index = castHash(key) & (_bucketList.length - 1);
623 
624                 _bucketList.setEntry(index, key, V.init);
625             } else {
626                 size_t index = hash & (_bucketList.length - 1);
627 
628                 _bucketList.setEntry(index, hash, key, V.init);
629             }
630 
631             return _bucketList[index].value;
632         }
633 
634         static if (isHashIdentical!K) {
635             size_t index =
636                 bucketListSearch!(SearchFor.notDeleted, K, V)
637                 (_bucketList, key);
638         } else {
639             size_t index =
640                 bucketListSearch!(SearchFor.notDeleted, K, V)
641                 (_bucketList, hash, key);
642         }
643 
644         if (_bucketList[index]._state == EntryState.empty) {
645             // The entry is empty, so we can insert the value here.
646             static if (isHashIdentical!K) {
647                 _bucketList.setEntry(index, key, V.init);
648             } else {
649                 _bucketList.setEntry(index, hash, key, V.init);
650             }
651 
652             ++_length;
653 
654             if (thresholdPassed(_length, _bucketList.length)) {
655                 // Resize the bucketList, as it passed the threshold.
656                 resize(newBucketListSize(_bucketList.length));
657 
658                 // Update the index, it has now changed.
659                 static if (isHashIdentical!K) {
660                     index = bucketListSearch!(SearchFor.notDeleted, K, V)
661                         (_bucketList, key);
662                 } else {
663                     index = bucketListSearch!(SearchFor.notDeleted, K, V)
664                         (_bucketList, hash, key);
665                 }
666             }
667         }
668 
669         // Return a reference to the value.
670         return _bucketList[index]._value;
671     }
672 
673     /**
674      * Remove a entry from the map if it is set, given a key.
675      *
676      * Params:
677      *     key = The key in the map.
678      *
679      * Returns:
680      *     true if a value was removed, otherwise false.
681      */
682     bool remove(K key) {
683         static if (isHashIdentical!K) {
684             size_t index =
685                 bucketListSearch!(SearchFor.any, K, V)
686                 (_bucketList, key);
687         } else {
688             size_t index =
689                 bucketListSearch!(SearchFor.any, K, V)
690                 (_bucketList, computeHash(key), key);
691         }
692 
693         if (_bucketList[index]._state == EntryState.occupied) {
694             --_length;
695 
696             // Zero the value and mark the slot as 'deleted', which is
697             // treated often the same as 'empty', only we can skip over
698             // deleted values to search for more values.
699             _bucketList.zeroEntryValue(index);
700             _bucketList[index]._state = EntryState.deleted;
701 
702             return true;
703         }
704 
705         return false;
706     }
707 
708     /**
709      * The length of the map.
710      *
711      * Returns: The number of entries in the map, in constant time.
712      */
713     @nogc @safe pure nothrow
714     @property size_t length() const {
715         return _length;
716     }
717 
718     /**
719      * Returns: True if this map is empty.
720      */
721     @nogc @safe pure nothrow
722     @property bool empty() const {
723         return _length == 0;
724     }
725 
726     /**
727      * Implement boolean conversion for a map.
728      *
729      * Returns: True if this set is not empty.
730      */
731     @nogc @safe pure nothrow
732     bool opCast(T: bool)() const {
733         return !empty;
734     }
735 
736     static if(isDupable!K && isDupable!V) {
737         /**
738          * Copy an existing map into a new mutable map.
739          *
740          * Returns:
741          *     The fresh copy of the map.
742          */
743         @safe pure nothrow
744         HashMap!(K, V) dup() const {
745             if (_length == 0) {
746                 // Just return nothing special for length 0.
747                 return HashMap!(K, V).init;
748             }
749 
750             // Create a new map large enough to fit all of our values.
751             auto newMap = HashMap!(K, V)(_length);
752             newMap._length = _length;
753 
754             copyToBucketList(newMap._bucketList);
755 
756             return newMap;
757         }
758     }
759 
760 
761     /**
762      * Test if two maps are equal.
763      *
764      * Params:
765      *     otherMap = Another map.
766      *
767      * Returns:
768      *     true only if the maps are equal in length, keys, and values.
769      */
770     bool opEquals(ref const(HashMap!(K, V)) otherMap) const {
771         if (_length != otherMap._length) {
772             return false;
773         }
774 
775         foreach(ref entry; _bucketList) {
776             if (entry._state != EntryState.occupied) {
777                 // Skip holes in the container.
778                 continue;
779             }
780 
781             static if (isHashIdentical!K) {
782                 size_t index =
783                     bucketListSearch!(SearchFor.notDeleted, K, V)
784                     (otherMap._bucketList, entry._key);
785             } else {
786                 size_t index =
787                     bucketListSearch!(SearchFor.notDeleted, K, V)
788                     (otherMap._bucketList, entry._hash, entry._key);
789             }
790 
791             if (otherMap._bucketList[index]._state == EntryState.empty) {
792                 return false;
793             }
794         }
795 
796         return true;
797     }
798 
799     /// ditto
800     bool opEquals(const(HashMap!(K, V)) otherMap) const {
801         return opEquals(otherMap);
802     }
803 }
804 
805 template HashMapKeyType(T) {
806     alias HashMapKeyType = typeof(ElementType!(typeof(T._bucketList))._key);
807 }
808 
809 template HashMapValueType(T) {
810     alias HashMapValueType = typeof(ElementType!(typeof(T._bucketList))._value);
811 }
812 
813 // Test that is is not possible to create a map with a key or a value type
814 // which cannot be copy-assigned.
815 unittest {
816     struct NonCopyable { @disable this(this); }
817 
818     assert(!__traits(compiles, HashMap!(NonCopyable, int)));
819     assert(__traits(compiles, HashMap!(NonCopyable*, int)));
820     assert(!__traits(compiles, HashMap!(int, NonCopyable)));
821     assert(__traits(compiles, HashMap!(int, NonCopyable*)));
822 }
823 
824 // Check setting values, retrieval, removal, and lengths.
825 unittest {
826     HashMap!(int, string) map;
827 
828     map[1] = "a";
829     map[2] = "b";
830     map[3] = "c";
831 
832     map[1] = "x";
833     map[2] = "y";
834     map[3] = "z";
835 
836     assert(map.length == 3);
837 
838     assert(map[1] == "x");
839     assert(map[2] == "y");
840     assert(map[3] == "z");
841 
842     assert(map.remove(3));
843     assert(map.remove(2));
844     assert(map.remove(1));
845 
846     assert(!map.remove(1));
847 
848     assert(map.length == 0);
849 }
850 
851 unittest {
852     HashMap!(int, string) map;
853 
854     map[1] = "a";
855     map[2] = "b";
856     map[3] = "c";
857     map[4] = "d";
858     map[5] = "e";
859     map[6] = "f";
860     map[7] = "g";
861     map[8] = "h";
862     map[9] = "i";
863 
864     map[1] = "x";
865     map[2] = "y";
866     map[3] = "z";
867 
868     assert(map.length == 9);
869 
870     assert(map[1] == "x");
871     assert(map[2] == "y");
872     assert(map[3] == "z");
873     assert(map[4] == "d");
874     assert(map[5] == "e");
875     assert(map[6] == "f");
876     assert(map[7] == "g");
877     assert(map[8] == "h");
878     assert(map[9] == "i");
879 
880     assert(map.remove(3));
881     assert(map.remove(2));
882     assert(map.remove(1));
883 
884     assert(!map.remove(1));
885 
886     assert(map.length == 6);
887 }
888 
889 // Test the map with heavy collisions.
890 unittest {
891     struct BadHashObject {
892         int value;
893 
894         this(int value) {
895             this.value = value;
896         }
897 
898         @safe nothrow
899         size_t toHash() const {
900             return 0;
901         }
902 
903         @nogc @safe nothrow pure
904         bool opEquals(ref const BadHashObject other) const {
905             return value == other.value;
906         }
907     }
908 
909     HashMap!(BadHashObject, string) map;
910     enum size_t mapSize = 100;
911 
912     foreach(num; 0 .. mapSize) {
913         map[BadHashObject(cast(int) num)] = "a";
914     }
915 
916     assert(map.length == mapSize);
917 }
918 
919 // Test preallocated maps;
920 unittest {
921     auto map = HashMap!(int, string)(3);
922 
923     assert(map._bucketList.length == minimumBucketListSize);
924 }
925 
926 // Test the 'in' operator.
927 unittest {
928     // We'll test that our attributes work.
929     @safe pure nothrow
930     void runTest() {
931         HashMap!(int, string) map;
932 
933         map[1] = "a";
934         map[2] = "b";
935         map[3] = "c";
936 
937         // Test that @nogc works.
938         @nogc @safe pure nothrow
939         void runNoGCPart(typeof(map) map) {
940             assert((4 in map) is null);
941 
942             assert(*(1 in map) == "a");
943             assert(*(2 in map) == "b");
944             assert(*(3 in map) == "c");
945         }
946 
947         runNoGCPart(map);
948     }
949 
950     runTest();
951 }
952 
953 // Test the map with a weird type which makes assignment harder.
954 unittest {
955     struct WeirdType {
956     // The alignment could cause memory issues so we'll check for that.
957     align(1):
958         // This immutable member means we need to use memset above to set
959         // keys or values.
960         immutable(byte)* foo = null;
961         size_t x = 3;
962 
963         @nogc @safe pure nothrow
964         this(int value) {
965             x = value;
966         }
967 
968         @nogc @safe pure nothrow
969         size_t toHash() const {
970             return x;
971         }
972 
973         @nogc @safe pure nothrow
974         bool opEquals(ref const(WeirdType) other) const {
975             return foo == other.foo && x == other.x;
976         }
977 
978         @nogc @safe pure nothrow
979         bool opEquals(const(WeirdType) other) const {
980             return opEquals(other);
981         }
982     }
983 
984     @safe pure nothrow
985     void runTest() {
986         HashMap!(WeirdType, string) map;
987 
988         map[WeirdType(10)] = "a";
989 
990         @nogc @safe pure nothrow
991         void runNoGCPart(typeof(map) map) {
992             assert(map[WeirdType(10)] == "a");
993         }
994 
995         runNoGCPart(map);
996     }
997 
998     runTest();
999 }
1000 
1001 // Test get with default init
1002 unittest {
1003     @safe pure nothrow
1004     void runTest() {
1005         HashMap!(int, string) map;
1006 
1007         map[1] = "a";
1008 
1009         @nogc @safe pure nothrow
1010         void runNoGCPart(typeof(map) map) {
1011             assert(map.get(1) == "a");
1012             assert(map.get(2) is null);
1013         }
1014         runNoGCPart(map);
1015     }
1016 
1017     runTest();
1018 
1019 }
1020 
1021 // Test length, empty, and cast(bool)
1022 unittest {
1023     @safe pure nothrow
1024     void runTest() {
1025         HashMap!(int, string) map;
1026 
1027         @nogc @safe pure nothrow
1028         void runNoGCPart1(typeof(map) map) {
1029             assert(map.length == 0);
1030             assert(map.empty);
1031 
1032             if (map) {
1033                 assert(false, "cast(bool) failed for an empty map");
1034             }
1035         }
1036 
1037         @nogc @safe pure nothrow
1038         void runNoGCPart2(typeof(map) map) {
1039             assert(map.length == 1);
1040             assert(!map.empty);
1041 
1042             if (!map) {
1043                 assert(false, "cast(bool) failed for an non-empty map");
1044             }
1045         }
1046 
1047         @nogc @safe pure nothrow
1048         void runNoGCPart3(typeof(map) map) {
1049             assert(map.length == 0);
1050             assert(map.empty);
1051 
1052             if (map) {
1053                 assert(false, "cast(bool) failed for an empty map");
1054             }
1055         }
1056 
1057         runNoGCPart1(map);
1058         map[1] = "a";
1059         runNoGCPart2(map);
1060         map.remove(1);
1061         runNoGCPart3(map);
1062     }
1063 
1064     runTest();
1065 }
1066 
1067 // BUG: The lazy argument here cannot be made to be nothrow, @nogc, etc.
1068 // Test get with a given default.
1069 unittest {
1070     HashMap!(int, string) map;
1071 
1072     map[1] = "a";
1073 
1074     assert(map.get(1, "b") == "a");
1075     assert(map.get(2, "b") == "b");
1076 }
1077 
1078 // Test opEquals
1079 unittest {
1080     @safe pure nothrow
1081     void runTest() {
1082         HashMap!(string, string) leftMap;
1083         HashMap!(string, string) rightMap;
1084 
1085         // Give the left one a bit more, and take away from it.
1086         leftMap["a"] = "1";
1087         leftMap["b"] = "2";
1088         leftMap["c"] = "3";
1089         leftMap["d"] = "4";
1090         leftMap["e"] = "5";
1091         leftMap["f"] = "6";
1092         leftMap["g"] = "7";
1093         leftMap["h"] = "8";
1094         leftMap["i"] = "9";
1095         leftMap["j"] = "10";
1096 
1097         rightMap["a"] = "1";
1098         rightMap["b"] = "2";
1099         rightMap["c"] = "3";
1100 
1101         @nogc @safe pure nothrow
1102         void runNoGCPart(typeof(leftMap) leftMap, typeof(rightMap) rightMap) {
1103             // Remove the extra keys
1104             leftMap.remove("d");
1105             leftMap.remove("e");
1106             leftMap.remove("f");
1107             leftMap.remove("g");
1108             leftMap.remove("h");
1109             leftMap.remove("i");
1110             leftMap.remove("j");
1111 
1112             // Now the two maps should have different bucketLists, but they
1113             // should still be considered equal.
1114             assert(leftMap == rightMap);
1115         }
1116 
1117         runNoGCPart(leftMap, rightMap);
1118     }
1119 
1120     runTest();
1121 }
1122 
1123 // Test setDefault with default init
1124 unittest {
1125     @safe pure nothrow
1126     void runTest() {
1127         HashMap!(int, string) map;
1128 
1129         map[1] = "a";
1130 
1131         // setDefault for basic types with no explicit default ought to
1132         // be nothrow.
1133         assert(map.setDefault(1) == "a");
1134         assert(map.setDefault(2) is null);
1135 
1136         assert(map.length == 2);
1137 
1138         assert(map[2] is null);
1139     }
1140 
1141     runTest();
1142 }
1143 
1144 // Test setDefault with a given value.
1145 unittest {
1146     HashMap!(int, string) map;
1147 
1148     map[1] = "a";
1149 
1150     assert(map.setDefault(1, "b") == "a");
1151     assert(map.setDefault(2, "b") == "b");
1152 
1153     assert(map.length == 2);
1154 
1155     assert(map[2] == "b");
1156 }
1157 
1158 // Test setDefault with a given value which can be implicitly converted.
1159 unittest {
1160     HashMap!(int, long) map;
1161 
1162     map[1] = 2;
1163 
1164     assert(map.setDefault(1, 3) == 2);
1165 
1166     int x = 4;
1167 
1168     assert(map.setDefault(2, x) == 4);
1169 
1170     assert(map.length == 2);
1171 
1172     assert(map[2] == 4);
1173 }
1174 
1175 /**
1176  * A range through a series of items in the map.
1177  */
1178 struct KeyValueRange(K, V) {
1179 private:
1180     Entry!(K, V)[] _bucketList = null;
1181 public:
1182     @nogc @safe pure nothrow
1183     this(Entry!(K, V)[] bucketList) {
1184         foreach(index, ref entry; bucketList) {
1185             if (entry._state == EntryState.occupied) {
1186                 // Use a slice of the bucketList starting here.
1187                 _bucketList = bucketList[index .. $];
1188 
1189                 return;
1190             }
1191         }
1192     }
1193 
1194     @nogc @trusted pure nothrow
1195     this(const(Entry!(K, V)[]) bucketList) {
1196         this(cast(Entry!(K, V)[]) bucketList);
1197     }
1198 
1199     @nogc @safe pure nothrow
1200     inout(typeof(this)) save() inout {
1201         return this;
1202     }
1203 
1204     @nogc @safe pure nothrow
1205     @property
1206     bool empty() const {
1207         // We can check that the bucketList is empty to check if this range is
1208         // empty, because we will clear it after we pop the last item.
1209         return _bucketList.length == 0;
1210     }
1211 
1212     @nogc @safe pure nothrow
1213     @property
1214     ref inout(Entry!(K, V)) front() inout in {
1215         assert(!empty());
1216     } body {
1217         return _bucketList[0];
1218     }
1219 
1220     @nogc @safe pure nothrow
1221     void popFront() in {
1222         assert(!empty());
1223     } body {
1224         foreach(index; 1 .. _bucketList.length) {
1225             if (_bucketList[index]._state == EntryState.occupied) {
1226                 // Use a slice of the bucketList starting here.
1227                 _bucketList = _bucketList[index .. $];
1228 
1229                 return;
1230             }
1231         }
1232 
1233         // Clear the bucketList if we hit the end.
1234         _bucketList = null;
1235     }
1236 }
1237 
1238 /**
1239  * Produce a range through the items of a map. (A key-value pair)
1240  *
1241  * Params:
1242  *     map = A map.
1243  * Returns:
1244  *     A range running through the items in the map.
1245  */
1246 @nogc @safe pure nothrow
1247 auto byKeyValue(K, V)(auto ref HashMap!(K, V) map) {
1248     if (map.length == 0) {
1249         // Empty ranges should not have to traverse the bucketList at all.
1250         return KeyValueRange!(K, V).init;
1251     }
1252 
1253     return KeyValueRange!(K, V)(map._bucketList);
1254 }
1255 
1256 /// ditto
1257 @nogc @trusted pure nothrow
1258 auto byKeyValue(K, V)(auto ref const(HashMap!(K, V)) map) {
1259     alias RealK = HashMapKeyType!(typeof(map));
1260     alias RealV = HashMapValueType!(typeof(map));
1261 
1262     if (map.length == 0) {
1263         return KeyValueRange!(RealK, RealV).init;
1264     }
1265 
1266     return KeyValueRange!(RealK, RealV)(
1267         cast(Entry!(RealK, RealV)[])
1268         map._bucketList
1269     );
1270 }
1271 
1272 /// ditto
1273 @nogc @trusted pure nothrow
1274 auto byKeyValue(K, V)(auto ref immutable(HashMap!(K, V)) map) {
1275     alias RealK = HashMapKeyType!(typeof(map));
1276     alias RealV = HashMapValueType!(typeof(map));
1277 
1278     if (map.length == 0) {
1279         return KeyValueRange!(RealK, RealV).init;
1280     }
1281 
1282     return KeyValueRange!(RealK, RealV)(
1283         cast(Entry!(RealK, RealV)[])
1284         map._bucketList
1285     );
1286 }
1287 
1288 unittest {
1289     HashMap!(int, string) map;
1290 
1291     map[1] = "a";
1292     map[2] = "b";
1293     map[3] = "c";
1294 
1295     int[] keyList;
1296     string[] valueList;
1297 
1298     foreach(item; map.byKeyValue()) {
1299         keyList ~= item.key;
1300         valueList ~= item.value;
1301     }
1302 
1303     // From the way the bucketLists are distributed, we know we'll get this back.
1304     assert(keyList == [1, 2, 3]);
1305     assert(valueList == ["a", "b", "c"]);
1306 }
1307 
1308 unittest {
1309     HashMap!(string, string) mmap;
1310     const(HashMap!(string, string)) cmap;
1311     immutable(HashMap!(string, string)) imap;
1312 
1313     auto mItems = mmap.byKeyValue();
1314     auto cItems = cmap.byKeyValue();
1315     auto iItems = imap.byKeyValue();
1316 
1317     assert(is(typeof(mItems.front.key) == string));
1318     assert(is(typeof(cItems.front.key) == const(string)));
1319     assert(is(typeof(iItems.front.key) == immutable(string)));
1320     assert(is(typeof(mItems.front.value) == string));
1321     assert(is(typeof(cItems.front.value) == const(string)));
1322     assert(is(typeof(iItems.front.value) == immutable(string)));
1323 }
1324 
1325 // Test that the ranges can be created from r-values.
1326 unittest {
1327     auto func() {
1328         HashMap!(int, string) map;
1329 
1330         map[1] = "a";
1331         map[2] = "b";
1332         map[3] = "c";
1333 
1334         return map;
1335     }
1336 
1337     auto keyRange = func().byKey();
1338     auto valueRange = func().byValue();
1339     auto itemRange = func().byKeyValue();
1340 }
1341 
1342 /**
1343  * This is a range which runs through a series of keys in map.
1344  */
1345 struct KeyRange(K, V) {
1346 private:
1347     KeyValueRange!(K, V) _keyValueRange;
1348 public:
1349     @nogc @safe pure nothrow
1350     private this()(auto ref Entry!(K, V)[] bucketList) {
1351         _keyValueRange = KeyValueRange!(K, V)(bucketList);
1352     }
1353 
1354     ///
1355     @nogc @safe pure nothrow
1356     inout(typeof(this)) save() inout {
1357         return this;
1358     }
1359 
1360     ///
1361     @nogc @safe pure nothrow
1362     @property
1363     bool empty() const {
1364         return _keyValueRange.empty;
1365     }
1366 
1367     ///
1368     @nogc @trusted pure nothrow
1369     @property
1370     ref inout(K) front() inout {
1371         return _keyValueRange.front.key;
1372     }
1373 
1374     ///
1375     @nogc @safe pure nothrow
1376     void popFront() {
1377         _keyValueRange.popFront();
1378     }
1379 }
1380 
1381 /**
1382  * Produce a range through the keys of a map.
1383  *
1384  * Params:
1385  *     map = A map.
1386  * Returns:
1387  *     A range running through the keys in the map.
1388  */
1389 @nogc @safe pure nothrow
1390 auto byKey(K, V)(auto ref HashMap!(K, V) map) {
1391     if (map.length == 0) {
1392         return KeyRange!(K, V).init;
1393     }
1394 
1395     return KeyRange!(K, V)(map._bucketList);
1396 }
1397 
1398 /// ditto
1399 @nogc @trusted pure nothrow
1400 auto byKey(K, V)(auto ref const(HashMap!(K, V)) map) {
1401     alias RealK = HashMapKeyType!(typeof(map));
1402     alias RealV = HashMapValueType!(typeof(map));
1403 
1404     if (map.length == 0) {
1405         return KeyRange!(RealK, RealV).init;
1406     }
1407 
1408     return KeyRange!(RealK, RealV)(
1409         cast(Entry!(RealK, RealV)[])
1410         map._bucketList
1411     );
1412 }
1413 
1414 /// ditto
1415 @nogc @trusted pure nothrow
1416 auto byKey(K, V)(auto ref immutable(HashMap!(K, V)) map) {
1417     alias RealK = HashMapKeyType!(typeof(map));
1418     alias RealV = HashMapValueType!(typeof(map));
1419 
1420     if (map.length == 0) {
1421         return KeyRange!(RealK, RealV).init;
1422     }
1423 
1424     return KeyRange!(RealK, RealV)(
1425         cast(Entry!(RealK, RealV)[])
1426         map._bucketList
1427     );
1428 }
1429 
1430 unittest {
1431     HashMap!(int, string) map;
1432 
1433     map[1] = "a";
1434     map[2] = "b";
1435     map[3] = "c";
1436 
1437     int[] keyList;
1438 
1439     foreach(ref key; map.byKey()) {
1440         keyList ~= key;
1441     }
1442 
1443     // From the way the bucketLists are distributed, we know we'll get this back.
1444     assert(keyList == [1, 2, 3]);
1445 }
1446 
1447 unittest {
1448     HashMap!(string, string) mmap;
1449     const(HashMap!(string, string)) cmap;
1450     immutable(HashMap!(string, string)) imap;
1451 
1452     auto mKeys = mmap.byKey();
1453     auto cKeys = cmap.byKey();
1454     auto iKeys = imap.byKey();
1455 
1456     assert(is(typeof(mKeys.front) == string));
1457     assert(is(typeof(cKeys.front) == const(string)));
1458     assert(is(typeof(iKeys.front) == immutable(string)));
1459 }
1460 
1461 /**
1462  * This is a range which runs through a series of values in a map.
1463  */
1464 struct ValueRange(K, V) {
1465 private:
1466     KeyValueRange!(K, V) _keyValueRange;
1467 public:
1468     @nogc @safe pure nothrow
1469     private this()(auto ref Entry!(K, V)[] bucketList) {
1470         _keyValueRange = KeyValueRange!(K, V)(bucketList);
1471     }
1472 
1473     ///
1474     @nogc @safe pure nothrow
1475     inout(typeof(this)) save() inout {
1476         return this;
1477     }
1478 
1479     ///
1480     @nogc @safe pure nothrow
1481     @property
1482     bool empty() const {
1483         return _keyValueRange.empty;
1484     }
1485 
1486     ///
1487     @nogc @trusted pure nothrow
1488     @property
1489     ref inout(V) front() inout {
1490         return _keyValueRange.front.value;
1491     }
1492 
1493     ///
1494     @nogc @safe pure nothrow
1495     void popFront() {
1496         _keyValueRange.popFront();
1497     }
1498 }
1499 
1500 /**
1501  * Produce a range through the values of a map.
1502  *
1503  * Params:
1504  *     map = A map.
1505  * Returns:
1506  *     A range running through the values in the map.
1507  */
1508 @nogc @safe pure nothrow
1509 auto byValue(K, V)(auto ref HashMap!(K, V) map) {
1510     if (map.length == 0) {
1511         return ValueRange!(K, V).init;
1512     }
1513 
1514     return ValueRange!(K, V)(map._bucketList);
1515 }
1516 
1517 /// ditto
1518 @nogc @trusted pure nothrow
1519 auto byValue(K, V)(auto ref const(HashMap!(K, V)) map) {
1520     alias RealK = HashMapKeyType!(typeof(map));
1521     alias RealV = HashMapValueType!(typeof(map));
1522 
1523     if (map.length == 0) {
1524         return ValueRange!(RealK, RealV).init;
1525     }
1526 
1527     return ValueRange!(RealK, RealV)(
1528         cast(Entry!(RealK, RealV)[])
1529         map._bucketList
1530     );
1531 }
1532 
1533 /// ditto
1534 @nogc @trusted pure nothrow
1535 auto byValue(K, V)(auto ref immutable(HashMap!(K, V)) map) {
1536     alias RealK = HashMapKeyType!(typeof(map));
1537     alias RealV = HashMapValueType!(typeof(map));
1538 
1539     if (map.length == 0) {
1540         return ValueRange!(RealK, RealV).init;
1541     }
1542 
1543     return ValueRange!(RealK, RealV)(
1544         cast(Entry!(RealK, RealV)[])
1545         map._bucketList
1546     );
1547 }
1548 
1549 unittest {
1550     HashMap!(int, string) map;
1551 
1552     map[1] = "a";
1553     map[2] = "b";
1554     map[3] = "c";
1555 
1556     string[] valueList = [];
1557 
1558     foreach(ref value; map.byValue()) {
1559         valueList ~= value;
1560     }
1561 
1562     // From the way the buckets are distributed, we know we'll get this back.
1563     assert(valueList == ["a", "b", "c"]);
1564 }
1565 
1566 unittest {
1567     HashMap!(string, string) mmap;
1568     const(HashMap!(string, string)) cmap;
1569     immutable(HashMap!(string, string)) imap;
1570 
1571     auto mValues = mmap.byValue();
1572     auto cValues = cmap.byValue();
1573     auto iValues = imap.byValue();
1574 
1575     assert(is(typeof(mValues.front) == string));
1576     assert(is(typeof(cValues.front) == const(string)));
1577     assert(is(typeof(iValues.front) == immutable(string)));
1578 }
1579 
1580 unittest {
1581     const(HashMap!(int, int)) createMap() {
1582         HashMap!(int, int) map;
1583 
1584         map[3] = 4;
1585         map[4] = 7;
1586 
1587         return map;
1588     }
1589 
1590     auto map = createMap();
1591     auto newMap = map.dup;
1592     // Test r-values.
1593     auto thirdMap = createMap().dup();
1594 
1595     assert(map == newMap);
1596 }
1597 
1598 unittest {
1599     HashMap!(int, void[0]) map;
1600 
1601     auto x = map.dup;
1602 }