mirror of
https://github.com/bitwiseworks/gcc-os2.git
synced 2026-02-14 06:04:44 +00:00
Source URL: git://gcc.gnu.org/git/gcc.git Source Commit: 3e7b85061947bdc7c7465743ba90734566860821
330 lines
6.6 KiB
D
330 lines
6.6 KiB
D
/**
|
|
* HashTab container for internal usage.
|
|
*
|
|
* Copyright: Copyright Martin Nowak 2013.
|
|
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
* Authors: Martin Nowak
|
|
*/
|
|
module rt.util.container.hashtab;
|
|
|
|
import rt.util.container.array;
|
|
static import common = rt.util.container.common;
|
|
|
|
struct HashTab(Key, Value)
|
|
{
|
|
static struct Node
|
|
{
|
|
Key _key;
|
|
Value _value;
|
|
Node* _next;
|
|
}
|
|
|
|
@disable this(this);
|
|
|
|
~this()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
foreach (p; _buckets)
|
|
{
|
|
while (p !is null)
|
|
{
|
|
auto pn = p._next;
|
|
common.destroy(*p);
|
|
common.free(p);
|
|
p = pn;
|
|
}
|
|
}
|
|
_buckets.reset();
|
|
_length = 0;
|
|
}
|
|
|
|
@property size_t length() const
|
|
{
|
|
return _length;
|
|
}
|
|
|
|
@property bool empty() const
|
|
{
|
|
return !_length;
|
|
}
|
|
|
|
void remove(in Key key)
|
|
in { assert(key in this); }
|
|
body
|
|
{
|
|
ensureNotInOpApply();
|
|
|
|
immutable hash = hashOf(key) & mask;
|
|
auto pp = &_buckets[hash];
|
|
while (*pp)
|
|
{
|
|
auto p = *pp;
|
|
if (p._key == key)
|
|
{
|
|
*pp = p._next;
|
|
common.destroy(*p);
|
|
common.free(p);
|
|
if (--_length < _buckets.length && _length >= 4)
|
|
shrink();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
pp = &p._next;
|
|
}
|
|
}
|
|
assert(0);
|
|
}
|
|
|
|
ref inout(Value) opIndex(Key key) inout
|
|
{
|
|
return *opIn_r(key);
|
|
}
|
|
|
|
void opIndexAssign(Value value, Key key)
|
|
{
|
|
*get(key) = value;
|
|
}
|
|
|
|
inout(Value)* opIn_r(in Key key) inout
|
|
{
|
|
if (_buckets.length)
|
|
{
|
|
immutable hash = hashOf(key) & mask;
|
|
for (inout(Node)* p = _buckets[hash]; p !is null; p = p._next)
|
|
{
|
|
if (p._key == key)
|
|
return &p._value;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
int opApply(scope int delegate(ref Key, ref Value) dg)
|
|
{
|
|
immutable save = _inOpApply;
|
|
_inOpApply = true;
|
|
scope (exit) _inOpApply = save;
|
|
foreach (p; _buckets)
|
|
{
|
|
while (p !is null)
|
|
{
|
|
if (auto res = dg(p._key, p._value))
|
|
return res;
|
|
p = p._next;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
|
|
Value* get(Key key)
|
|
{
|
|
if (auto p = opIn_r(key))
|
|
return p;
|
|
|
|
ensureNotInOpApply();
|
|
|
|
if (!_buckets.length)
|
|
_buckets.length = 4;
|
|
|
|
immutable hash = hashOf(key) & mask;
|
|
auto p = cast(Node*)common.xmalloc(Node.sizeof);
|
|
common.initialize(*p);
|
|
p._key = key;
|
|
p._next = _buckets[hash];
|
|
_buckets[hash] = p;
|
|
if (++_length >= 2 * _buckets.length)
|
|
grow();
|
|
return &p._value;
|
|
}
|
|
|
|
static hash_t hashOf(in ref Key key) @trusted
|
|
{
|
|
static if (is(Key U : U[]))
|
|
return .hashOf(key, 0);
|
|
else
|
|
return .hashOf((&key)[0 .. 1], 0);
|
|
}
|
|
|
|
@property hash_t mask() const
|
|
{
|
|
return _buckets.length - 1;
|
|
}
|
|
|
|
void grow()
|
|
in
|
|
{
|
|
assert(_buckets.length);
|
|
}
|
|
body
|
|
{
|
|
immutable ocnt = _buckets.length;
|
|
immutable nmask = 2 * ocnt - 1;
|
|
_buckets.length = 2 * ocnt;
|
|
for (size_t i = 0; i < ocnt; ++i)
|
|
{
|
|
auto pp = &_buckets[i];
|
|
while (*pp)
|
|
{
|
|
auto p = *pp;
|
|
|
|
immutable nidx = hashOf(p._key) & nmask;
|
|
if (nidx != i)
|
|
{
|
|
*pp = p._next;
|
|
p._next = _buckets[nidx];
|
|
_buckets[nidx] = p;
|
|
}
|
|
else
|
|
{
|
|
pp = &p._next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void shrink()
|
|
in
|
|
{
|
|
assert(_buckets.length >= 2);
|
|
}
|
|
body
|
|
{
|
|
immutable ocnt = _buckets.length;
|
|
immutable ncnt = ocnt >> 1;
|
|
immutable nmask = ncnt - 1;
|
|
|
|
for (size_t i = ncnt; i < ocnt; ++i)
|
|
{
|
|
if (auto tail = _buckets[i])
|
|
{
|
|
immutable nidx = i & nmask;
|
|
auto pp = &_buckets[nidx];
|
|
while (*pp)
|
|
pp = &(*pp)._next;
|
|
*pp = tail;
|
|
_buckets[i] = null;
|
|
}
|
|
}
|
|
_buckets.length = ncnt;
|
|
}
|
|
|
|
void ensureNotInOpApply()
|
|
{
|
|
if (_inOpApply)
|
|
assert(0, "Invalid HashTab manipulation during opApply iteration.");
|
|
}
|
|
|
|
Array!(Node*) _buckets;
|
|
size_t _length;
|
|
bool _inOpApply;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
HashTab!(int, int) tab;
|
|
|
|
foreach (i; 0 .. 100)
|
|
tab[i] = 100 - i;
|
|
|
|
foreach (i; 0 .. 100)
|
|
assert(tab[i] == 100 - i);
|
|
|
|
foreach (k, v; tab)
|
|
assert(v == 100 - k);
|
|
|
|
foreach (i; 0 .. 50)
|
|
tab.remove(2 * i);
|
|
|
|
assert(tab.length == 50);
|
|
|
|
foreach (i; 0 .. 50)
|
|
assert(tab[2 * i + 1] == 100 - 2 * i - 1);
|
|
|
|
assert(tab.length == 50);
|
|
|
|
tab.reset();
|
|
assert(tab.empty);
|
|
tab[0] = 0;
|
|
assert(!tab.empty);
|
|
destroy(tab);
|
|
assert(tab.empty);
|
|
|
|
// not copyable
|
|
static assert(!__traits(compiles, { HashTab!(int, int) tab2 = tab; }));
|
|
HashTab!(int, int) tab2;
|
|
static assert(!__traits(compiles, tab = tab2));
|
|
static void foo(HashTab!(int, int) copy) {}
|
|
static assert(!__traits(compiles, foo(tab)));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
HashTab!(string, size_t) tab;
|
|
|
|
tab["foo"] = 0;
|
|
assert(tab["foo"] == 0);
|
|
++tab["foo"];
|
|
assert(tab["foo"] == 1);
|
|
tab["foo"]++;
|
|
assert(tab["foo"] == 2);
|
|
|
|
auto s = "fo";
|
|
s ~= "o";
|
|
assert(tab[s] == 2);
|
|
assert(tab.length == 1);
|
|
tab[s] -= 2;
|
|
assert(tab[s] == 0);
|
|
tab["foo"] = 12;
|
|
assert(tab[s] == 12);
|
|
|
|
tab.remove("foo");
|
|
assert(tab.empty);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
alias RC = common.RC!();
|
|
HashTab!(size_t, RC) tab;
|
|
|
|
size_t cnt;
|
|
assert(cnt == 0);
|
|
tab[0] = RC(&cnt);
|
|
assert(cnt == 1);
|
|
tab[1] = tab[0];
|
|
assert(cnt == 2);
|
|
tab.remove(0);
|
|
assert(cnt == 1);
|
|
tab.remove(1);
|
|
assert(cnt == 0);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import core.exception;
|
|
|
|
HashTab!(uint, uint) tab;
|
|
foreach (i; 0 .. 5)
|
|
tab[i] = i;
|
|
bool thrown;
|
|
foreach (k, v; tab)
|
|
{
|
|
try
|
|
{
|
|
if (k == 3) tab.remove(k);
|
|
}
|
|
catch (AssertError e)
|
|
{
|
|
thrown = true;
|
|
}
|
|
}
|
|
assert(thrown);
|
|
assert(tab[3] == 3);
|
|
}
|