Make HashTable Range return Pair
This commit is contained in:
parent
3ed46117d1
commit
d545d6900e
@ -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));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user