Make HashTable Range return Pair

This commit is contained in:
Eugen Wissner 2018-05-11 05:44:46 +02:00
parent 3ed46117d1
commit d545d6900e
1 changed files with 71 additions and 44 deletions

View File

@ -20,22 +20,25 @@ import tanya.hash.lookup;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.typecons;
/** /**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values. * Bidirectional range whose element type is a tuple of a key and the
* respective value.
* *
* Params: * Params:
* E = Element type. * T = Type of the internal hash storage.
*/ */
struct Range(E) struct Range(T)
{ {
static if (isMutable!E) private alias KV = CopyConstness!(T, T.Bucket.KV);
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;
@ -67,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(KV) 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.kv;
} }
@property ref inout(E) back() inout @property ref inout(KV) 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.kv;
} }
Range opIndex() Range opIndex()
@ -131,40 +132,41 @@ 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[]);
} }
} }
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive : isForwardRange;
static assert(is(HashTable!(string, int) a));
static assert(is(const HashTable!(string, int)));
static assert(isForwardRange!(HashTable!(string, int).Range));
}
/** /**
* Hash table. * Hash table is a data structure that stores pairs of keys and values without
* any particular order.
*
* This $(D_PSYMBOL HashTable) is implemented using closed hashing. Hash
* collisions are resolved with linear probing.
*
* $(D_PARAM Key) should be hashable with $(D_PARAM hasher). $(D_PARAM hasher)
* is a callable that accepts an argument of type $(D_PARAM Key) and returns a
* hash value for it ($(D_KEYWORD size_t)).
* *
* Params: * Params:
* Key = Key type. * Key = Key type.
* Value = Value type. * Value = Value type.
* hasher = Hash function for $(D_PARAM Key). * hasher = Hash function for $(D_PARAM K).
*/ */
struct HashTable(Key, Value, alias hasher = hash) struct HashTable(Key, Value, alias hasher = hash)
if (is(typeof(hasher(Key.init)) == size_t)) if (is(typeof(hasher(Key.init)) == size_t))
{ {
private HashArray!(hasher, Key, Value) data; private alias HashArray = .HashArray!(hasher, Key, Value);
private alias Buckets = HashArray.Buckets;
private alias Buckets = typeof(this.data).Buckets; private HashArray data;
/// The range types for $(D_PSYMBOL HashTable). /// The range types for $(D_PSYMBOL HashTable).
alias Range = .Range!Key; alias Range = .Range!HashArray;
/// ditto /// ditto
alias ConstRange = .Range!(const Key); alias ConstRange = .Range!(const HashArray);
/** /**
* Constructor. * Constructor.
@ -194,7 +196,7 @@ if (is(typeof(hasher(Key.init)) == size_t))
} }
do do
{ {
this.data = typeof(this.data)(Buckets(allocator)); this.data = HashArray(Buckets(allocator));
} }
/** /**
@ -216,7 +218,7 @@ if (is(typeof(hasher(Key.init)) == size_t))
} }
do do
{ {
this.data = typeof(this.data)(Buckets(init.data, allocator)); this.data = HashArray(Buckets(init.data, allocator));
} }
/// ditto /// ditto
@ -228,7 +230,7 @@ if (is(typeof(hasher(Key.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;
} }
@ -378,8 +380,8 @@ if (is(typeof(hasher(Key.init)) == size_t))
{ {
e.key = key; e.key = key;
} }
e.value = value; e.kv.value = value;
return e.value; return e.kv.value;
} }
/** /**
@ -400,7 +402,7 @@ if (is(typeof(hasher(Key.init)) == size_t))
{ {
if (key == range.front.key) if (key == range.front.key)
{ {
return range.front.value; return range.front.kv.value;
} }
} }
assert(false, "Range violation"); assert(false, "Range violation");
@ -435,7 +437,7 @@ if (is(typeof(hasher(Key.init)) == size_t))
*/ */
bool opBinaryRight(string op : "in")(Key key) bool opBinaryRight(string op : "in")(Key key)
{ {
return this.data.find(key); return this.data.canFind(key);
} }
/** /**
@ -465,6 +467,23 @@ if (is(typeof(hasher(Key.init)) == size_t))
{ {
this.data.rehash(n); this.data.rehash(n);
} }
/**
* Returns a bidirectional range whose element type is a tuple of a key and
* the respective value.
*
* Returns: A bidirectional range that iterates over the container.
*/
Range opIndex()
{
return typeof(return)(this.data.array[]);
}
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.data.array[]);
}
} }
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
@ -501,3 +520,11 @@ if (is(typeof(hasher(Key.init)) == size_t))
dinos.clear(); dinos.clear();
assert(dinos.empty); assert(dinos.empty);
} }
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive : isForwardRange;
static assert(is(HashTable!(string, int) a));
static assert(is(const HashTable!(string, int)));
static assert(isForwardRange!(HashTable!(string, int).Range));
}