diff --git a/README.md b/README.md index 1658b0b..ba478c7 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Tanya consists of the following packages and (top-level) modules: * `algorithm`: Collection of generic algorithms. * `async`: Event loop (epoll, kqueue and IOCP). * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 -string, Hash table. +string, Set, Hash table. * `conv`: This module provides functions for converting between different types. * `encoding`: This package provides tools to work with text encodings. diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index 89bbc8b..ed508bd 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -16,7 +16,9 @@ module tanya.container.entry; import tanya.algorithm.mutation; import tanya.container.array; +import tanya.memory.allocator; import tanya.meta.trait; +import tanya.meta.transform; import tanya.typecons; package struct SEntry(T) @@ -119,6 +121,49 @@ package struct HashArray(alias hasher, K, V = void) size_t lengthIndex; size_t length; + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + do + { + this.array = Buckets(allocator); + } + + this(T)(ref T data, shared Allocator allocator) + if (is(Unqual!T == HashArray)) + in + { + assert(allocator !is null); + } + do + { + this.array = Buckets(data.array, allocator); + this.lengthIndex = data.lengthIndex; + this.length = data.length; + } + + // Move constructor + void move(ref HashArray data, shared Allocator allocator) + in + { + assert(allocator !is null); + } + do + { + this.array = Buckets(.move(data.array), allocator); + this.lengthIndex = data.lengthIndex; + this.length = data.length; + } + + void swap(ref HashArray data) + { + .swap(this.array, data.array); + .swap(this.lengthIndex, data.lengthIndex); + .swap(this.length, data.length); + } + /* * Returns bucket position for `hash`. `0` may mean the 0th position or an * empty `buckets` array. @@ -189,7 +234,7 @@ package struct HashArray(alias hasher, K, V = void) return false; // Rehashing failed. } } - move(storage, this.array); + .move(storage, this.array); return true; } diff --git a/source/tanya/container/hashtable.d b/source/tanya/container/hashtable.d index 951d9ef..b7224fa 100644 --- a/source/tanya/container/hashtable.d +++ b/source/tanya/container/hashtable.d @@ -151,7 +151,7 @@ struct Range(T) * Params: * Key = Key type. * Value = Value type. - * hasher = Hash function for $(D_PARAM K). + * hasher = Hash function for $(D_PARAM Key). */ struct HashTable(Key, Value, alias hasher = hash) if (is(typeof(hasher(Key.init)) == size_t)) @@ -170,6 +170,13 @@ if (is(typeof(hasher(Key.init)) == size_t)) /// ditto alias ConstRange = .Range!(const HashArray); + invariant + { + assert(this.data.lengthIndex < primes.length); + assert(this.data.array.length == 0 + || this.data.array.length == primes[this.data.lengthIndex]); + } + /** * Constructor. * @@ -190,6 +197,13 @@ if (is(typeof(hasher(Key.init)) == size_t)) rehash(n); } + /// + @nogc nothrow pure @safe unittest + { + auto hashTable = HashTable!(string, int)(5); + assert(hashTable.capacity == 7); + } + /// ditto this(shared Allocator allocator) in @@ -198,7 +212,7 @@ if (is(typeof(hasher(Key.init)) == size_t)) } do { - this.data = HashArray(Buckets(allocator)); + this.data = HashArray(allocator); } /** @@ -220,7 +234,7 @@ if (is(typeof(hasher(Key.init)) == size_t)) } do { - this.data = HashArray(Buckets(init.data, allocator)); + this.data = HashArray(init.data, allocator); } /// ditto @@ -232,9 +246,7 @@ if (is(typeof(hasher(Key.init)) == size_t)) } do { - this.data = HashArray(Buckets(move(init.data), allocator)); - this.lengthIndex = init.lengthIndex; - init.lengthIndex = 0; + this.data.move(init.data, allocator); } /** @@ -261,8 +273,7 @@ if (is(typeof(hasher(Key.init)) == size_t)) ref typeof(this) opAssign(S)(S that) @trusted if (is(S == HashTable)) { - swap(this.data, that.data); - swap(this.lengthIndex, that.lengthIndex); + this.data.swap(that.data); return this; } @@ -576,3 +587,27 @@ if (is(typeof(hasher(Key.init)) == size_t)) static assert(is(const HashTable!(string, int))); static assert(isForwardRange!(HashTable!(string, int).Range)); } + +// Constructs by reference +@nogc nothrow pure @safe unittest +{ + auto hashTable1 = HashTable!(string, int)(7); + auto hashTable2 = HashTable!(string, int)(hashTable1); + assert(hashTable1.length == hashTable2.length); + assert(hashTable1.capacity == hashTable2.capacity); +} + +// Constructs by value +@nogc nothrow pure @safe unittest +{ + auto hashTable = HashTable!(string, int)(HashTable!(string, int)(7)); + assert(hashTable.capacity == 7); +} + +// Assigns by value +@nogc nothrow pure @safe unittest +{ + HashTable!(string, int) hashTable; + hashTable = HashTable!(string, int)(7); + assert(hashTable.capacity == 7); +} diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d index ea3d3be..2ac3e2e 100644 --- a/source/tanya/container/set.d +++ b/source/tanya/container/set.d @@ -194,6 +194,13 @@ if (is(typeof(hasher(T.init)) == size_t)) rehash(n); } + /// + @nogc nothrow pure @safe unittest + { + auto set = Set!int(5); + assert(set.capacity == 7); + } + /// ditto this(shared Allocator allocator) in @@ -202,7 +209,7 @@ if (is(typeof(hasher(T.init)) == size_t)) } do { - this.data = HashArray(Buckets(allocator)); + this.data = HashArray(allocator); } /** @@ -224,7 +231,7 @@ if (is(typeof(hasher(T.init)) == size_t)) } do { - this.data = HashArray(Buckets(init.data, allocator)); + this.data = HashArray(init.data, allocator); } /// ditto @@ -236,9 +243,7 @@ if (is(typeof(hasher(T.init)) == size_t)) } do { - this.data = HashArray(Buckets(move(init.data), allocator)); - this.lengthIndex = init.lengthIndex; - init.lengthIndex = 0; + this.data.move(init.data, allocator); } /** @@ -257,7 +262,7 @@ if (is(typeof(hasher(T.init)) == size_t)) if (is(Unqual!S == Set)) { this.data = that.data; - this.lengthIndex = that.lengthIndex; + this.data.lengthIndex = that.data.lengthIndex; return this; } @@ -265,8 +270,7 @@ if (is(typeof(hasher(T.init)) == size_t)) ref typeof(this) opAssign(S)(S that) @trusted if (is(S == Set)) { - swap(this.data, that.data); - swap(this.lengthIndex, that.lengthIndex); + this.data.swap(that.data); return this; } @@ -598,3 +602,27 @@ if (is(typeof(hasher(T.init)) == size_t)) auto set = Set!int(8); assert(set.capacity == 13); } + +// Constructs by reference +@nogc nothrow pure @safe unittest +{ + auto set1 = Set!int(7); + auto set2 = Set!int(set1); + assert(set1.length == set2.length); + assert(set1.capacity == set2.capacity); +} + +// Constructs by value +@nogc nothrow pure @safe unittest +{ + auto set = Set!int(Set!int(7)); + assert(set.capacity == 7); +} + +// Assigns by value +@nogc nothrow pure @safe unittest +{ + Set!int set; + set = Set!int(7); + assert(set.capacity == 7); +}