diff options
| author | Eugen Wissner <belka@caraus.de> | 2018-06-29 20:42:25 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2018-06-29 20:43:05 +0200 |
| commit | d54e06f43c57ea15adb8100f898ed09eae181e27 (patch) | |
| tree | 8c13b77b7d232cdffcffc911186757c12e7a4c1b /source | |
| parent | 5e901f505c2334e8c7494649fa43b367a0d22755 (diff) | |
| download | tanya-d54e06f43c57ea15adb8100f898ed09eae181e27.tar.gz | |
Iterate hash table by key or by value
Diffstat (limited to 'source')
| -rw-r--r-- | source/tanya/container/hashtable.d | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/source/tanya/container/hashtable.d b/source/tanya/container/hashtable.d index fc1c668..08f395f 100644 --- a/source/tanya/container/hashtable.d +++ b/source/tanya/container/hashtable.d @@ -140,6 +140,236 @@ struct Range(T) } /** + * Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable). + * + * Params: + * T = Type of the internal hash storage. + */ +struct ByKey(T) +{ + private alias Key = CopyConstness!(T, T.Key); + static if (isMutable!T) + { + private alias DataRange = T.array.Range; + } + else + { + private alias DataRange = T.array.ConstRange; + } + private DataRange dataRange; + + @disable this(); + + private this(DataRange dataRange) + { + while (!dataRange.empty && dataRange.front.status != BucketStatus.used) + { + dataRange.popFront(); + } + while (!dataRange.empty && dataRange.back.status != BucketStatus.used) + { + dataRange.popBack(); + } + this.dataRange = dataRange; + } + + @property ByKey save() + { + return this; + } + + @property bool empty() const + { + return this.dataRange.empty(); + } + + @property void popFront() + in + { + assert(!empty); + assert(this.dataRange.front.status == BucketStatus.used); + } + out + { + assert(empty || this.dataRange.back.status == BucketStatus.used); + } + do + { + do + { + this.dataRange.popFront(); + } + while (!empty && dataRange.front.status != BucketStatus.used); + } + + @property void popBack() + in + { + assert(!empty); + assert(this.dataRange.back.status == BucketStatus.used); + } + out + { + assert(empty || this.dataRange.back.status == BucketStatus.used); + } + do + { + do + { + this.dataRange.popBack(); + } + while (!empty && dataRange.back.status != BucketStatus.used); + } + + @property ref inout(Key) front() inout + in + { + assert(!empty); + assert(this.dataRange.front.status == BucketStatus.used); + } + do + { + return this.dataRange.front.key; + } + + @property ref inout(Key) back() inout + in + { + assert(!empty); + assert(this.dataRange.back.status == BucketStatus.used); + } + do + { + return this.dataRange.back.key; + } + + ByKey opIndex() + { + return typeof(return)(this.dataRange[]); + } + + ByKey!(const T) opIndex() const + { + return typeof(return)(this.dataRange[]); + } +} + +/** + * Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable). + * + * Params: + * T = Type of the internal hash storage. + */ +struct ByValue(T) +{ + private alias Value = CopyConstness!(T, T.Value); + static if (isMutable!T) + { + private alias DataRange = T.array.Range; + } + else + { + private alias DataRange = T.array.ConstRange; + } + private DataRange dataRange; + + @disable this(); + + private this(DataRange dataRange) + { + while (!dataRange.empty && dataRange.front.status != BucketStatus.used) + { + dataRange.popFront(); + } + while (!dataRange.empty && dataRange.back.status != BucketStatus.used) + { + dataRange.popBack(); + } + this.dataRange = dataRange; + } + + @property ByValue save() + { + return this; + } + + @property bool empty() const + { + return this.dataRange.empty(); + } + + @property void popFront() + in + { + assert(!empty); + assert(this.dataRange.front.status == BucketStatus.used); + } + out + { + assert(empty || this.dataRange.back.status == BucketStatus.used); + } + do + { + do + { + this.dataRange.popFront(); + } + while (!empty && dataRange.front.status != BucketStatus.used); + } + + @property void popBack() + in + { + assert(!empty); + assert(this.dataRange.back.status == BucketStatus.used); + } + out + { + assert(empty || this.dataRange.back.status == BucketStatus.used); + } + do + { + do + { + this.dataRange.popBack(); + } + while (!empty && dataRange.back.status != BucketStatus.used); + } + + @property ref inout(Value) front() inout + in + { + assert(!empty); + assert(this.dataRange.front.status == BucketStatus.used); + } + do + { + return this.dataRange.front.kv.value; + } + + @property ref inout(Value) back() inout + in + { + assert(!empty); + assert(this.dataRange.back.status == BucketStatus.used); + } + do + { + return this.dataRange.back.kv.value; + } + + ByValue opIndex() + { + return typeof(return)(this.dataRange[]); + } + + ByValue!(const T) opIndex() const + { + return typeof(return)(this.dataRange[]); + } +} + +/** * Hash table is a data structure that stores pairs of keys and values without * any particular order. * @@ -172,6 +402,15 @@ if (is(typeof(((Key k) => hasher(k))(Key.init)) == size_t)) /// ditto alias ConstRange = .Range!(const HashArray); + /// ditto + alias ByKey = .ByKey!(const HashArray); + + /// ditto + alias ByValue = .ByValue!HashArray; + + /// ditto + alias ConstByValue = .ByValue!(const HashArray); + invariant { assert(this.data.lengthIndex < primes.length); @@ -742,6 +981,86 @@ if (is(typeof(((Key k) => hasher(k))(Key.init)) == size_t)) assert(hashTable[].front == hashTable.KeyValue("Iguanodon", 9)); assert(hashTable[].back == hashTable.KeyValue("Iguanodon", 9)); } + + /** + * Returns a bidirectional range that iterats over the keys of this + * $(D_PSYMBOL HashTable). + * + * This function always returns a $(D_KEYWORD const) range, since changing + * a key of a hash table would probably change its hash value and require + * rehashing. + * + * Returns: $(D_KEYWORD const) bidirectional range that iterates over the + * keys of the container. + * + * See_Also: $(D_PSYMBOL byValue). + */ + ByKey byKey() const + { + return typeof(return)(this.data.array[]); + } + + /// + @nogc nothrow pure @safe unittest + { + HashTable!(string, int) hashTable; + hashTable["one"] = 1; + hashTable["two"] = 2; + + auto byKey = hashTable.byKey(); + assert(!byKey.empty); + + assert(byKey.front == "one" || byKey.front == "two"); + assert(byKey.back == "one" || byKey.back == "two"); + assert(byKey.front != byKey.back); + + byKey.popFront(); + assert(byKey.front == byKey.back); + + byKey.popBack(); + assert(byKey.empty); + } + + /** + * Returns a bidirectional range that iterats over the values of this + * $(D_PSYMBOL HashTable). + * + * Returns: A bidirectional range that iterates over the values of the + * container. + * + * See_Also: $(D_PSYMBOL byKey). + */ + ByValue byValue() + { + return typeof(return)(this.data.array[]); + } + + /// ditto + ConstByValue byValue() const + { + return typeof(return)(this.data.array[]); + } + + /// + @nogc nothrow pure @safe unittest + { + HashTable!(string, int) hashTable; + hashTable["one"] = 1; + hashTable["two"] = 2; + + auto byValue = hashTable.byValue(); + assert(!byValue.empty); + + assert(byValue.front == 1 || byValue.front == 2); + assert(byValue.back == 1 || byValue.back == 2); + assert(byValue.front != byValue.back); + + byValue.popFront(); + assert(byValue.front == byValue.back); + + byValue.popBack(); + assert(byValue.empty); + } } @nogc nothrow pure @safe unittest |
