Improve preconditions for the container.Set
This commit is contained in:
parent
41a8e32351
commit
53620cdddf
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user