parent
adf2d8b689
commit
533fa3b023
@ -171,24 +171,29 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
this.length = that.length;
|
this.length = that.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property size_t bucketCount() const
|
||||||
|
{
|
||||||
|
return primes[this.lengthIndex];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 Key key) const
|
size_t locateBucket(ref const Key key) const
|
||||||
{
|
{
|
||||||
return this.array.length == 0
|
return this.array.length == 0 ? 0 : hasher(key) % bucketCount;
|
||||||
? 0
|
|
||||||
: hasher(key) % primes[this.lengthIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inserts a key into an empty or deleted bucket. If the key is
|
* If the key doesn't already exists, returns an empty bucket the key can
|
||||||
* already in there, does nothing. Returns the bucket with the key.
|
* be inserted in and adjusts the element count. Otherwise returns the
|
||||||
|
* bucket containing the key.
|
||||||
*/
|
*/
|
||||||
ref Bucket insert(ref Key key)
|
ref Bucket insert(ref Key key)
|
||||||
{
|
{
|
||||||
while ((this.lengthIndex + 1) != primes.length)
|
const newLengthIndex = this.lengthIndex + 1;
|
||||||
|
if (newLengthIndex != primes.length)
|
||||||
{
|
{
|
||||||
foreach (ref e; this.array[locateBucket(key) .. $])
|
foreach (ref e; this.array[locateBucket(key) .. $])
|
||||||
{
|
{
|
||||||
@ -203,17 +208,29 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.rehashToSize(this.lengthIndex + 1))
|
this.rehashToSize(newLengthIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (ref e; this.array[locateBucket(key) .. $])
|
||||||
{
|
{
|
||||||
++this.lengthIndex;
|
if (e == key)
|
||||||
|
{
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
else if (e.status != BucketStatus.used)
|
||||||
|
{
|
||||||
|
++this.length;
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.array.insertBack(Bucket(key));
|
|
||||||
|
this.array.length = this.array.length + 1;
|
||||||
|
++this.length;
|
||||||
return this.array[$ - 1];
|
return this.array[$ - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes an index in the primes array.
|
// Takes an index in the primes array.
|
||||||
bool rehashToSize(const size_t n)
|
void rehashToSize(const size_t n)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(n < primes.length);
|
assert(n < primes.length);
|
||||||
@ -231,15 +248,15 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
{
|
{
|
||||||
if (e2.status != BucketStatus.used) // Insert the key
|
if (e2.status != BucketStatus.used) // Insert the key
|
||||||
{
|
{
|
||||||
e2 = e1;
|
.move(e1, e2);
|
||||||
continue DataLoop;
|
continue DataLoop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false; // Rehashing failed.
|
storage.insertBack(.move(e1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.move(storage, this.array);
|
.move(storage, this.array);
|
||||||
return true;
|
this.lengthIndex = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rehash(const size_t n)
|
void rehash(const size_t n)
|
||||||
@ -252,9 +269,9 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.rehashToSize(lengthIndex))
|
if (lengthIndex > this.lengthIndex)
|
||||||
{
|
{
|
||||||
this.lengthIndex = lengthIndex;
|
this.rehashToSize(lengthIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,8 +174,6 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
invariant
|
invariant
|
||||||
{
|
{
|
||||||
assert(this.data.lengthIndex < primes.length);
|
assert(this.data.lengthIndex < primes.length);
|
||||||
assert(this.data.array.length == 0
|
|
||||||
|| this.data.array.length == primes[this.data.lengthIndex]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -440,6 +438,23 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
assert(hashTable.empty);
|
assert(hashTable.empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current bucket count in the container.
|
||||||
|
*
|
||||||
|
* Bucket count equals to the number of the elements can be saved in the
|
||||||
|
* container in the best case scenario for key distribution, i.d. every key
|
||||||
|
* has a unique hash value. In a worse case the bucket count can be less
|
||||||
|
* than the number of elements stored in the container.
|
||||||
|
*
|
||||||
|
* Returns: Current bucket count.
|
||||||
|
*
|
||||||
|
* See_Also: $(D_PSYMBOL rehash).
|
||||||
|
*/
|
||||||
|
@property size_t bucketCount() const
|
||||||
|
{
|
||||||
|
return this.data.bucketCount;
|
||||||
|
}
|
||||||
|
|
||||||
/// The maximum number of buckets the container can have.
|
/// The maximum number of buckets the container can have.
|
||||||
enum size_t maxBucketCount = primes[$ - 1];
|
enum size_t maxBucketCount = primes[$ - 1];
|
||||||
|
|
||||||
@ -645,18 +660,15 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
* Sets the number of buckets in the container to at least $(D_PARAM n)
|
* Sets the number of buckets in the container to at least $(D_PARAM n)
|
||||||
* and rearranges all the elements according to their hash values.
|
* and rearranges all the elements according to their hash values.
|
||||||
*
|
*
|
||||||
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
|
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
|
||||||
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
|
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
|
||||||
* forced.
|
* forced.
|
||||||
*
|
*
|
||||||
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
|
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
|
||||||
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
|
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
|
||||||
*
|
*
|
||||||
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
|
* If $(D_PARAM n) is less than or equal to the current
|
||||||
* is forced without resizing the container.
|
* $(D_PSYMBOL bucketCount), the function may have no effect.
|
||||||
*
|
|
||||||
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
|
|
||||||
* function may have no effect.
|
|
||||||
*
|
*
|
||||||
* Rehashing is automatically performed whenever the container needs space
|
* Rehashing is automatically performed whenever the container needs space
|
||||||
* to insert new elements.
|
* to insert new elements.
|
||||||
@ -773,3 +785,27 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
}
|
}
|
||||||
testFunc(hashTable);
|
testFunc(hashTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 53: https://github.com/caraus-ecms/tanya/issues/53
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
{
|
||||||
|
HashTable!(uint, uint) hashTable;
|
||||||
|
foreach (uint i; 0 .. 14)
|
||||||
|
{
|
||||||
|
hashTable[i + 1] = i;
|
||||||
|
}
|
||||||
|
assert(hashTable.length == 14);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
HashTable!(int, int) hashtable;
|
||||||
|
|
||||||
|
hashtable[1194250162] = 3;
|
||||||
|
hashtable[-1131293824] = 6;
|
||||||
|
hashtable[838100082] = 9;
|
||||||
|
|
||||||
|
hashtable.rehash(11);
|
||||||
|
|
||||||
|
assert(hashtable[-1131293824] == 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -432,6 +432,23 @@ if (is(typeof(hasher(T.init)) == size_t))
|
|||||||
assert(set.empty);
|
assert(set.empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current bucket count in the container.
|
||||||
|
*
|
||||||
|
* Bucket count equals to the number of the elements can be saved in the
|
||||||
|
* container in the best case scenario for key distribution, i.d. every key
|
||||||
|
* has a unique hash value. In a worse case the bucket count can be less
|
||||||
|
* than the number of elements stored in the container.
|
||||||
|
*
|
||||||
|
* Returns: Current bucket count.
|
||||||
|
*
|
||||||
|
* See_Also: $(D_PSYMBOL rehash).
|
||||||
|
*/
|
||||||
|
@property size_t bucketCount() const
|
||||||
|
{
|
||||||
|
return this.data.bucketCount;
|
||||||
|
}
|
||||||
|
|
||||||
/// The maximum number of buckets the container can have.
|
/// The maximum number of buckets the container can have.
|
||||||
enum size_t maxBucketCount = primes[$ - 1];
|
enum size_t maxBucketCount = primes[$ - 1];
|
||||||
|
|
||||||
@ -559,18 +576,15 @@ if (is(typeof(hasher(T.init)) == size_t))
|
|||||||
* Sets the number of buckets in the container to at least $(D_PARAM n)
|
* Sets the number of buckets in the container to at least $(D_PARAM n)
|
||||||
* and rearranges all the elements according to their hash values.
|
* and rearranges all the elements according to their hash values.
|
||||||
*
|
*
|
||||||
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
|
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
|
||||||
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
|
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
|
||||||
* forced.
|
* forced.
|
||||||
*
|
*
|
||||||
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
|
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
|
||||||
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
|
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
|
||||||
*
|
*
|
||||||
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
|
* If $(D_PARAM n) is less than or equal to the current
|
||||||
* is forced without resizing the container.
|
* $(D_PSYMBOL bucketCount), the function may have no effect.
|
||||||
*
|
|
||||||
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
|
|
||||||
* function may have no effect.
|
|
||||||
*
|
*
|
||||||
* Rehashing is automatically performed whenever the container needs space
|
* Rehashing is automatically performed whenever the container needs space
|
||||||
* to insert new elements.
|
* to insert new elements.
|
||||||
|
Loading…
Reference in New Issue
Block a user