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)
|
package struct Bucket(K, V = void)
|
||||||
{
|
{
|
||||||
private alias Key = K;
|
static if (is(V == void))
|
||||||
private alias Value = V;
|
{
|
||||||
|
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)
|
@property void key(ref K key)
|
||||||
{
|
{
|
||||||
this.key_ = key;
|
this.key() = key;
|
||||||
this.status = BucketStatus.used;
|
this.status = BucketStatus.used;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property ref inout(K) key() inout
|
@property ref inout(K) key() inout
|
||||||
{
|
{
|
||||||
return this.key_;
|
static if (is(V == void))
|
||||||
}
|
|
||||||
|
|
||||||
bool opEquals(ref K key)
|
|
||||||
{
|
|
||||||
if (this.status == BucketStatus.used && this.key == key)
|
|
||||||
{
|
{
|
||||||
return true;
|
return this.key_;
|
||||||
}
|
}
|
||||||
return false;
|
else
|
||||||
}
|
|
||||||
|
|
||||||
bool opEquals(ref const K key) const
|
|
||||||
{
|
|
||||||
if (this.status == BucketStatus.used && this.key == key)
|
|
||||||
{
|
{
|
||||||
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;
|
return key == that.key && this.status == that.status;
|
||||||
}
|
}
|
||||||
@ -96,13 +98,6 @@ package struct Bucket(K, V = void)
|
|||||||
}
|
}
|
||||||
this.status = BucketStatus.deleted;
|
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.
|
// 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)
|
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;
|
alias Buckets = Array!Bucket;
|
||||||
|
|
||||||
Array!Bucket array;
|
Buckets array;
|
||||||
size_t lengthIndex;
|
size_t lengthIndex;
|
||||||
size_t length;
|
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
|
* Returns bucket position for `hash`. `0` may mean the 0th position or an
|
||||||
* empty `buckets` array.
|
* 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;
|
return this.array.length == 0 ? 0 : hasher(key) % this.array.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inserts the value in an empty or deleted bucket. If the value is
|
* Inserts a key into an empty or deleted bucket. If the key is
|
||||||
* already in there, does nothing and returns InsertStatus.found. If the
|
* already in there, does nothing. Returns the bucket with the key.
|
||||||
* hash array is full returns InsertStatus.failed. Otherwise,
|
|
||||||
* InsertStatus.added is returned.
|
|
||||||
*/
|
*/
|
||||||
ref Bucket insert(ref K key)
|
ref Bucket insert(ref Key key)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -170,6 +165,11 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
|
|
||||||
// Takes an index in the primes array.
|
// Takes an index in the primes array.
|
||||||
bool rehashToSize(const size_t n)
|
bool rehashToSize(const size_t n)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(n < primes.length);
|
||||||
|
}
|
||||||
|
do
|
||||||
{
|
{
|
||||||
auto storage = typeof(this.array)(primes[n], this.array.allocator);
|
auto storage = typeof(this.array)(primes[n], this.array.allocator);
|
||||||
DataLoop: foreach (ref e1; this.array[])
|
DataLoop: foreach (ref e1; this.array[])
|
||||||
@ -180,7 +180,7 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
|
|
||||||
foreach (ref e2; storage[bucketPosition .. $])
|
foreach (ref e2; storage[bucketPosition .. $])
|
||||||
{
|
{
|
||||||
if (e2.status != BucketStatus.used) // Insert the value.
|
if (e2.status != BucketStatus.used) // Insert the key
|
||||||
{
|
{
|
||||||
e2 = e1;
|
e2 = e1;
|
||||||
continue DataLoop;
|
continue DataLoop;
|
||||||
@ -220,12 +220,12 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
this.length = 0;
|
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 .. $])
|
foreach (ref e; this.array[bucketPosition .. $])
|
||||||
{
|
{
|
||||||
if (e == value) // Found.
|
if (e == key) // Found.
|
||||||
{
|
{
|
||||||
e.remove();
|
e.remove();
|
||||||
--this.length;
|
--this.length;
|
||||||
@ -239,12 +239,12 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
return 0;
|
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 .. $])
|
foreach (ref e; this.array[bucketPosition .. $])
|
||||||
{
|
{
|
||||||
if (e == value) // Found.
|
if (e == key) // Found.
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -27,17 +27,18 @@ import tanya.meta.transform;
|
|||||||
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
|
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
|
||||||
*
|
*
|
||||||
* Params:
|
* 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
|
else
|
||||||
{
|
{
|
||||||
private alias DataRange = Array!(Bucket!(Unqual!E)).ConstRange;
|
private alias DataRange = T.array.ConstRange;
|
||||||
}
|
}
|
||||||
private DataRange dataRange;
|
private DataRange dataRange;
|
||||||
|
|
||||||
@ -69,63 +70,61 @@ struct Range(E)
|
|||||||
@property void popFront()
|
@property void popFront()
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(!this.dataRange.empty);
|
assert(!empty);
|
||||||
assert(this.dataRange.front.status == BucketStatus.used);
|
assert(this.dataRange.front.status == BucketStatus.used);
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
{
|
{
|
||||||
assert(this.dataRange.empty
|
assert(empty || this.dataRange.back.status == BucketStatus.used);
|
||||||
|| this.dataRange.back.status == BucketStatus.used);
|
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
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()
|
@property void popBack()
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(!this.dataRange.empty);
|
assert(!empty);
|
||||||
assert(this.dataRange.back.status == BucketStatus.used);
|
assert(this.dataRange.back.status == BucketStatus.used);
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
{
|
{
|
||||||
assert(this.dataRange.empty
|
assert(empty || this.dataRange.back.status == BucketStatus.used);
|
||||||
|| this.dataRange.back.status == BucketStatus.used);
|
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
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
|
@property ref inout(E) front() inout
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(!this.dataRange.empty);
|
assert(!empty);
|
||||||
assert(this.dataRange.front.status == BucketStatus.used);
|
assert(this.dataRange.front.status == BucketStatus.used);
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
return dataRange.front.key;
|
return this.dataRange.front.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property ref inout(E) back() inout
|
@property ref inout(E) back() inout
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(!this.dataRange.empty);
|
assert(!empty);
|
||||||
assert(this.dataRange.back.status == BucketStatus.used);
|
assert(this.dataRange.back.status == BucketStatus.used);
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
return dataRange.back.key;
|
return this.dataRange.back.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
Range opIndex()
|
Range opIndex()
|
||||||
@ -133,7 +132,7 @@ struct Range(E)
|
|||||||
return typeof(return)(this.dataRange[]);
|
return typeof(return)(this.dataRange[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Range!(const E) opIndex() const
|
Range!(const T) opIndex() const
|
||||||
{
|
{
|
||||||
return typeof(return)(this.dataRange[]);
|
return typeof(return)(this.dataRange[]);
|
||||||
}
|
}
|
||||||
@ -146,7 +145,9 @@ struct Range(E)
|
|||||||
* This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions
|
* This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions
|
||||||
* are resolved with linear probing.
|
* 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:
|
* Params:
|
||||||
* T = Element type.
|
* T = Element type.
|
||||||
@ -155,15 +156,16 @@ struct Range(E)
|
|||||||
struct Set(T, alias hasher = hash)
|
struct Set(T, alias hasher = hash)
|
||||||
if (is(typeof(hasher(T.init)) == size_t))
|
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).
|
/// The range types for $(D_PSYMBOL Set).
|
||||||
alias Range = .Range!T;
|
alias Range = .Range!HashArray;
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
alias ConstRange = .Range!(const T);
|
alias ConstRange = .Range!(const HashArray);
|
||||||
|
|
||||||
invariant
|
invariant
|
||||||
{
|
{
|
||||||
@ -200,7 +202,7 @@ if (is(typeof(hasher(T.init)) == size_t))
|
|||||||
}
|
}
|
||||||
do
|
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
|
do
|
||||||
{
|
{
|
||||||
this.data = typeof(this.data)(Buckets(init.data, allocator));
|
this.data = HashArray(Buckets(init.data, allocator));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
@ -234,7 +236,7 @@ if (is(typeof(hasher(T.init)) == size_t))
|
|||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
this.data = typeof(this.data)(Buckets(move(init.data), allocator));
|
this.data = HashArray(Buckets(move(init.data), allocator));
|
||||||
this.lengthIndex = init.lengthIndex;
|
this.lengthIndex = init.lengthIndex;
|
||||||
init.lengthIndex = 0;
|
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
|
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
|
* Returns a bidirectional range over the container.
|
||||||
* elements.
|
*
|
||||||
|
* Returns: A bidirectional range that iterates over the container.
|
||||||
*/
|
*/
|
||||||
Range opIndex()
|
Range opIndex()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user