Improve preconditions for the container.Set

This commit is contained in:
Eugen Wissner 2018-05-11 05:43:14 +02:00
parent 41a8e32351
commit 53620cdddf
2 changed files with 77 additions and 74 deletions

View File

@ -46,44 +46,46 @@ package enum BucketStatus : byte
package struct Bucket(K, V = void)
{
private alias Key = K;
private alias Value = V;
static if (is(V == void))
{
K key_;
}
else
{
alias KV = Pair!(K, "key", V, "value");
KV kv;
}
BucketStatus status = BucketStatus.empty;
this(ref K key)
{
this.key = key;
}
@property void key(ref K key)
{
this.key_ = key;
this.key() = key;
this.status = BucketStatus.used;
}
@property ref inout(K) key() inout
{
return this.key_;
}
bool opEquals(ref K key)
{
if (this.status == BucketStatus.used && this.key == key)
static if (is(V == void))
{
return true;
return this.key_;
}
return false;
}
bool opEquals(ref const K key) const
{
if (this.status == BucketStatus.used && this.key == key)
else
{
return true;
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;
}

View File

@ -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()
{