@@ -10,7 +10,7 @@ module containers.hashmap;
1010private import containers.internal.hash : generateHash;
1111private import containers.internal.node : shouldAddGCRange;
1212private import stdx.allocator.mallocator : Mallocator;
13- private import std.traits : isBasicType;
13+ private import std.traits : isBasicType, Unqual ;
1414
1515/**
1616 * Associative array / hash map.
@@ -37,7 +37,7 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
3737 /**
3838 * Use the given `allocator` for allocations.
3939 */
40- this (Allocator allocator)
40+ this (Allocator allocator) pure nothrow @nogc @safe
4141 in
4242 {
4343 assert (allocator ! is null , " Allocator must not be null" );
@@ -85,12 +85,24 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
8585 }
8686 }
8787
88- ~this ()
88+ ~this () nothrow
89+ {
90+ scope (failure) assert (false );
91+ clear();
92+ }
93+
94+ /**
95+ * Removes all items from the map
96+ */
97+ void clear ()
8998 {
9099 import stdx.allocator : dispose;
100+
91101 static if (useGC)
92102 GC .removeRange(buckets.ptr);
93103 allocator.dispose(buckets);
104+ buckets = null ;
105+ _length = 0 ;
94106 }
95107
96108 /**
@@ -206,7 +218,7 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
206218 /**
207219 * Supports $(B aa[key] = value;) syntax.
208220 */
209- void opIndexAssign (V value, K key)
221+ void opIndexAssign (V value, const K key)
210222 {
211223 insert(key, value);
212224 }
@@ -217,7 +229,7 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
217229 * Returns: pointer to the value corresponding to the given key,
218230 * or null if the key is not present in the HashMap.
219231 */
220- inout (V)* opBinaryRight (string op)(K key) inout nothrow if (op == " in" )
232+ inout (V)* opBinaryRight (string op)(K key) inout nothrow @trusted if (op == " in" )
221233 {
222234 if (_length == 0 )
223235 return null ;
@@ -274,6 +286,14 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
274286 return _length == 0 ;
275287 }
276288
289+ /**
290+ * Returns: a range of the keys in this map.
291+ */
292+ auto byKey (this This)() inout @trusted
293+ {
294+ return Slice! (This, IterType.key)(cast (Unqual! (This)* ) &this );
295+ }
296+
277297 /**
278298 * Returns: a GC-allocated array filled with the keys contained in this map.
279299 */
@@ -294,6 +314,16 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
294314 return app.data;
295315 }
296316
317+
318+ /**
319+ * Returns: a range of the values in this map.
320+ */
321+ auto byValue (this This)() inout @trusted
322+ {
323+ return Slice! (This, IterType.value)(cast (Unqual! (This)* ) &this );
324+ }
325+ alias opSlice = byValue ;
326+
297327 /**
298328 * Returns: a GC-allocated array containing the values contained in this map.
299329 */
@@ -309,20 +339,29 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
309339 foreach (ref const bucket; buckets)
310340 {
311341 foreach (item; bucket.range)
312- app.put(item.value);
342+ app.put(cast (ContainerElementType ! (This, V)) item.value);
313343 }
314344 return app.data;
315345 }
316346
347+ /**
348+ * Returns: a range of the kev/value pairs in this map. The element type of
349+ * this range is a struct with `key` and `value` fields.
350+ */
351+ auto byKeyValue (this This)() inout @trusted
352+ {
353+ return Slice! (This, IterType.both)(cast (Unqual! (This)* ) &this );
354+ }
355+
317356 /**
318357 * Support for $(D foreach(key, value; aa) { ... }) syntax;
319358 */
320- int opApply (D)(D del) if (isOpApplyDelegate! (D, const (K), V))
359+ int opApply (D)(D del) // if(isOpApplyDelegate!(D, const(K), V))
321360 {
322361 int result = 0 ;
323362 foreach (ref bucket; buckets)
324363 {
325- foreach (ref node; bucket.range )
364+ foreach (ref node; bucket[] )
326365 {
327366 result = del(node.key, node.value);
328367 if (result != 0 )
@@ -338,7 +377,7 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
338377 int result = 0 ;
339378 foreach (const ref bucket; buckets)
340379 {
341- foreach (const ref node; bucket.range )
380+ foreach (const ref node; bucket[] )
342381 {
343382 result = del(node.key, node.value);
344383 if (result != 0 )
@@ -350,7 +389,7 @@ struct HashMap(K, V, Allocator = Mallocator, alias hashFunction = generateHash!K
350389
351390private :
352391
353- import stdx.allocator : make;
392+ import stdx.allocator : make, makeArray ;
354393 import containers.unrolledlist : UnrolledList;
355394 import containers.internal.storage_type : ContainerStorageType;
356395 import containers.internal.element_type : ContainerElementType;
@@ -360,6 +399,93 @@ private:
360399 enum bool useGC = supportGC && (shouldAddGCRange! K || shouldAddGCRange! V);
361400 alias Hash = typeof ({ K k = void ; return hashFunction (k); }());
362401
402+ enum IterType: ubyte
403+ {
404+ key, value, both
405+ }
406+
407+ static struct Slice (MapType, IterType Type)
408+ {
409+ static if (Type == IterType.both)
410+ {
411+ struct FrontType
412+ {
413+ ContainerElementType! (MapType, K) key;
414+ ContainerElementType! (MapType, V) value;
415+ }
416+ }
417+ else static if (Type == IterType.value)
418+ alias FrontType = ContainerElementType! (MapType, V);
419+ else static if (Type == IterType.key)
420+ alias FrontType = ContainerElementType! (MapType, K);
421+ else
422+ static assert (false );
423+
424+ Unqual! (MapType)* hm;
425+ size_t bucketIndex;
426+ typeof (hm.buckets[0 ].opSlice ()) bucketRange;
427+ bool _empty;
428+
429+ this (Unqual! (MapType)* hm)
430+ {
431+ this .hm = hm;
432+ this .bucketIndex = 0 ;
433+ bucketRange = typeof (bucketRange).init;
434+ this ._empty = false ;
435+
436+ while (true )
437+ {
438+ if (bucketIndex >= hm.buckets.length)
439+ {
440+ _empty = true ;
441+ break ;
442+ }
443+ bucketRange = hm.buckets[bucketIndex][];
444+ if (bucketRange.empty)
445+ bucketIndex++ ;
446+ else
447+ break ;
448+ }
449+ }
450+
451+ FrontType front ()
452+ {
453+ static if (Type == IterType.both)
454+ return FrontType (cast (ContainerElementType! (MapType, K)) bucketRange.front.key,
455+ cast (ContainerElementType! (MapType, V)) bucketRange.front.value);
456+ else static if (Type == IterType.value)
457+ return cast (ContainerElementType! (MapType, V)) bucketRange.front.value;
458+ else static if (Type == IterType.key)
459+ return cast (ContainerElementType! (MapType, K)) bucketRange.front.key;
460+ else
461+ static assert (false );
462+ }
463+
464+ bool empty () const pure nothrow @nogc @property
465+ {
466+ return _empty;
467+ }
468+
469+ void popFront () pure nothrow @nogc
470+ {
471+ bucketRange.popFront();
472+ if (bucketRange.empty)
473+ {
474+ while (bucketRange.empty)
475+ {
476+ bucketIndex++ ;
477+ if (bucketIndex >= hm.buckets.length)
478+ {
479+ _empty = true ;
480+ break ;
481+ }
482+ else
483+ bucketRange = hm.buckets[bucketIndex][];
484+ }
485+ }
486+ }
487+ }
488+
363489 template isOpApplyDelegate (D, KT , VT )
364490 {
365491 import std.traits : isDelegate, isImplicitlyConvertible, isIntegral, Parameters, ReturnType;
@@ -374,8 +500,9 @@ private:
374500 void initialize (size_t bucketCount = 4 )
375501 {
376502 import std.conv : emplace;
503+ assert ((bucketCount & (bucketCount - 1 )) == 0 , " bucketCount must be a power of two" );
377504
378- buckets = ( cast ( Bucket* ) allocator.allocate(bucketCount * Bucket.sizeof))[ 0 .. bucketCount] ;
505+ buckets = makeArray ! Bucket(allocator, bucketCount) ;
379506 static if (useGC)
380507 GC .addRange(buckets.ptr, buckets.length * Bucket.sizeof);
381508 foreach (ref bucket; buckets)
@@ -387,7 +514,7 @@ private:
387514 }
388515 }
389516
390- Node* insert (K key, V value)
517+ Node* insert (const K key, V value)
391518 {
392519 if (buckets.length == 0 )
393520 initialize();
@@ -436,7 +563,6 @@ private:
436563 */
437564 void rehash () @trusted
438565 {
439- // import stdx.allocator : make, dispose;
440566 import std.conv : emplace;
441567 immutable size_t newLength = buckets.length << 1 ;
442568 immutable size_t newSize = newLength * Bucket.sizeof;
@@ -533,6 +659,8 @@ private:
533659unittest
534660{
535661 import std.uuid : randomUUID;
662+ import std.algorithm.iteration : walkLength;
663+
536664 auto hm = HashMap! (string , int )(16 );
537665 assert (hm.length == 0 );
538666 assert (! hm.remove(" abc" ));
@@ -554,6 +682,12 @@ unittest
554682 assert (hm.length == 1001 );
555683 assert (hm.keys ().length == hm.length);
556684 assert (hm.values ().length == hm.length);
685+ () @nogc {
686+ assert (hm.byKey ().walkLength == hm.length);
687+ assert (hm.byValue ().walkLength == hm.length);
688+ assert (hm[].walkLength == hm.length);
689+ assert (hm.byKeyValue ().walkLength == hm.length);
690+ }();
557691 foreach (const ref string k, ref int v; hm) {}
558692
559693 auto hm2 = HashMap! (char , char )(4 );
0 commit comments