summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2018-05-11 05:43:14 +0200
committerEugen Wissner <belka@caraus.de>2018-05-11 05:43:14 +0200
commit53620cdddfd40ad997af8c9c273ce1a68a284881 (patch)
treec5c1cc7a7d66369205f9c1bde7e308520e4e42fd
parent41a8e323518c3b390eeb246c1a37ed86b4b35988 (diff)
downloadtanya-53620cdddfd40ad997af8c9c273ce1a68a284881.tar.gz
Improve preconditions for the container.Set
-rw-r--r--source/tanya/container/entry.d86
-rw-r--r--source/tanya/container/set.d65
2 files changed, 77 insertions, 74 deletions
diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d
index a3cc9ee..6b6fd2a 100644
--- a/source/tanya/container/entry.d
+++ b/source/tanya/container/entry.d
@@ -46,44 +46,46 @@ package enum BucketStatus : byte
package struct Bucket(K, V = void)
{
- private alias Key = K;
- private alias Value = V;
-
- @property void key(ref K key)
+ static if (is(V == void))
{
- this.key_ = key;
- this.status = BucketStatus.used;
+ K key_;
}
+ else
+ {
+ alias KV = Pair!(K, "key", V, "value");
+ KV kv;
+ }
+ BucketStatus status = BucketStatus.empty;
- @property ref inout(K) key() inout
+ this(ref K key)
{
- return this.key_;
+ this.key = key;
}
- bool opEquals(ref K key)
+ @property void key(ref K key)
{
- if (this.status == BucketStatus.used && this.key == key)
- {
- return true;
- }
- return false;
+ this.key() = key;
+ this.status = BucketStatus.used;
}
- bool opEquals(ref const K key) const
+ @property ref inout(K) key() inout
{
- if (this.status == BucketStatus.used && this.key == key)
+ static if (is(V == void))
{
- return true;
+ return this.key_;
+ }
+ else
+ {
+ return this.kv.key;
}
- return false;
}
- bool opEquals(ref typeof(this) that)
+ bool opEquals(ref inout(K) key) inout
{
- return key == that.key && this.status == that.status;
+ return this.status == BucketStatus.used && this.key == key;
}
- bool opEquals(ref typeof(this) that) const
+ bool opEquals(ref inout(typeof(this)) that) inout
{
return key == that.key && this.status == that.status;
}
@@ -96,13 +98,6 @@ package struct Bucket(K, V = void)
}
this.status = BucketStatus.deleted;
}
-
- private K key_;
- static if (!is(V == void))
- {
- V value;
- }
- BucketStatus status = BucketStatus.empty;
}
// Possible sizes for the hash-based containers.
@@ -115,10 +110,12 @@ package static immutable size_t[33] primes = [
package struct HashArray(alias hasher, K, V = void)
{
- alias Bucket = .Bucket!(K, V);
+ alias Key = K;
+ alias Value = V;
+ alias Bucket = .Bucket!(Key, Value);
alias Buckets = Array!Bucket;
- Array!Bucket array;
+ Buckets array;
size_t lengthIndex;
size_t length;
@@ -126,18 +123,16 @@ package struct HashArray(alias hasher, K, V = void)
* Returns bucket position for `hash`. `0` may mean the 0th position or an
* empty `buckets` array.
*/
- size_t locateBucket(ref const K key) const
+ size_t locateBucket(ref const Key key) const
{
return this.array.length == 0 ? 0 : hasher(key) % this.array.length;
}
/*
- * Inserts the value in an empty or deleted bucket. If the value is
- * already in there, does nothing and returns InsertStatus.found. If the
- * hash array is full returns InsertStatus.failed. Otherwise,
- * InsertStatus.added is returned.
+ * Inserts a key into an empty or deleted bucket. If the key is
+ * already in there, does nothing. Returns the bucket with the key.
*/
- ref Bucket insert(ref K key)
+ ref Bucket insert(ref Key key)
{
while (true)
{
@@ -170,6 +165,11 @@ package struct HashArray(alias hasher, K, V = void)
// Takes an index in the primes array.
bool rehashToSize(const size_t n)
+ in
+ {
+ assert(n < primes.length);
+ }
+ do
{
auto storage = typeof(this.array)(primes[n], this.array.allocator);
DataLoop: foreach (ref e1; this.array[])
@@ -180,7 +180,7 @@ package struct HashArray(alias hasher, K, V = void)
foreach (ref e2; storage[bucketPosition .. $])
{
- if (e2.status != BucketStatus.used) // Insert the value.
+ if (e2.status != BucketStatus.used) // Insert the key
{
e2 = e1;
continue DataLoop;
@@ -220,12 +220,12 @@ package struct HashArray(alias hasher, K, V = void)
this.length = 0;
}
- size_t remove(ref K value)
+ size_t remove(ref Key key)
{
- auto bucketPosition = locateBucket(value);
+ auto bucketPosition = locateBucket(key);
foreach (ref e; this.array[bucketPosition .. $])
{
- if (e == value) // Found.
+ if (e == key) // Found.
{
e.remove();
--this.length;
@@ -239,12 +239,12 @@ package struct HashArray(alias hasher, K, V = void)
return 0;
}
- bool find(ref const K value) const
+ bool canFind(ref const Key key) const
{
- auto bucketPosition = locateBucket(value);
+ auto bucketPosition = locateBucket(key);
foreach (ref e; this.array[bucketPosition .. $])
{
- if (e == value) // Found.
+ if (e == key) // Found.
{
return true;
}
diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d
index 6e158f1..207f485 100644
--- a/source/tanya/container/set.d
+++ b/source/tanya/container/set.d
@@ -27,17 +27,18 @@ import tanya.meta.transform;
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
*
* Params:
- * E = Element type.
+ * T = Type of the internal hash storage.
*/
-struct Range(E)
+struct Range(T)
{
- static if (isMutable!E)
+ private alias E = CopyConstness!(T, T.Key);
+ static if (isMutable!T)
{
- private alias DataRange = Array!(Bucket!(Unqual!E)).Range;
+ private alias DataRange = T.array.Range;
}
else
{
- private alias DataRange = Array!(Bucket!(Unqual!E)).ConstRange;
+ private alias DataRange = T.array.ConstRange;
}
private DataRange dataRange;
@@ -69,63 +70,61 @@ struct Range(E)
@property void popFront()
in
{
- assert(!this.dataRange.empty);
+ assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
- assert(this.dataRange.empty
- || this.dataRange.back.status == BucketStatus.used);
+ assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
- dataRange.popFront();
+ this.dataRange.popFront();
}
- while (!dataRange.empty && dataRange.front.status != BucketStatus.used);
+ while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
in
{
- assert(!this.dataRange.empty);
+ assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
- assert(this.dataRange.empty
- || this.dataRange.back.status == BucketStatus.used);
+ assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
- dataRange.popBack();
+ this.dataRange.popBack();
}
- while (!dataRange.empty && dataRange.back.status != BucketStatus.used);
+ while (!empty && dataRange.back.status != BucketStatus.used);
}
@property ref inout(E) front() inout
in
{
- assert(!this.dataRange.empty);
+ assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{
- return dataRange.front.key;
+ return this.dataRange.front.key;
}
@property ref inout(E) back() inout
in
{
- assert(!this.dataRange.empty);
+ assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{
- return dataRange.back.key;
+ return this.dataRange.back.key;
}
Range opIndex()
@@ -133,7 +132,7 @@ struct Range(E)
return typeof(return)(this.dataRange[]);
}
- Range!(const E) opIndex() const
+ Range!(const T) opIndex() const
{
return typeof(return)(this.dataRange[]);
}
@@ -146,7 +145,9 @@ struct Range(E)
* This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions
* are resolved with linear probing.
*
- * Currently works only with integral types.
+ * $(D_PARAM T) should be hashable with $(D_PARAM hasher). $(D_PARAM hasher) is
+ * a callable that accepts an argument of type $(D_PARAM T) and returns a hash
+ * value for it ($(D_KEYWORD size_t)).
*
* Params:
* T = Element type.
@@ -155,15 +156,16 @@ struct Range(E)
struct Set(T, alias hasher = hash)
if (is(typeof(hasher(T.init)) == size_t))
{
- private HashArray!(hasher, T) data;
+ private alias HashArray = .HashArray!(hasher, T);
+ private alias Buckets = HashArray.Buckets;
- private alias Buckets = typeof(this.data).Buckets;
+ private HashArray data;
/// The range types for $(D_PSYMBOL Set).
- alias Range = .Range!T;
+ alias Range = .Range!HashArray;
/// ditto
- alias ConstRange = .Range!(const T);
+ alias ConstRange = .Range!(const HashArray);
invariant
{
@@ -200,7 +202,7 @@ if (is(typeof(hasher(T.init)) == size_t))
}
do
{
- this.data = typeof(this.data)(Buckets(allocator));
+ this.data = HashArray(Buckets(allocator));
}
/**
@@ -222,7 +224,7 @@ if (is(typeof(hasher(T.init)) == size_t))
}
do
{
- this.data = typeof(this.data)(Buckets(init.data, allocator));
+ this.data = HashArray(Buckets(init.data, allocator));
}
/// ditto
@@ -234,7 +236,7 @@ if (is(typeof(hasher(T.init)) == size_t))
}
do
{
- this.data = typeof(this.data)(Buckets(move(init.data), allocator));
+ this.data = HashArray(Buckets(move(init.data), allocator));
this.lengthIndex = init.lengthIndex;
init.lengthIndex = 0;
}
@@ -443,7 +445,7 @@ if (is(typeof(hasher(T.init)) == size_t))
*/
bool opBinaryRight(string op : "in")(auto ref const T value) const
{
- return this.data.find(value);
+ return this.data.canFind(value);
}
///
@@ -486,8 +488,9 @@ if (is(typeof(hasher(T.init)) == size_t))
}
/**
- * Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s
- * elements.
+ * Returns a bidirectional range over the container.
+ *
+ * Returns: A bidirectional range that iterates over the container.
*/
Range opIndex()
{