Skip to content

Commit c1f8bea

Browse files
author
Alex Rønne Petersen
committed
Merge pull request #592 from dawgfoto/hashTab
add HashTab container
2 parents 12c5573 + dda4847 commit c1f8bea

File tree

1 file changed

+324
-30
lines changed

1 file changed

+324
-30
lines changed

src/rt/util/container.d

+324-30
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,42 @@
88
*/
99
module rt.util.container;
1010

11+
import core.stdc.stdlib : free, malloc, realloc;
12+
1113
private void* xrealloc(void* ptr, size_t sz)
1214
{
13-
import core.stdc.stdlib, core.exception;
15+
import core.exception;
1416

15-
if (!sz) return free(ptr), null;
16-
if (auto nptr = realloc(ptr, sz)) return nptr;
17-
free(ptr), onOutOfMemoryError();
17+
if (!sz) return .free(ptr), null;
18+
if (auto nptr = .realloc(ptr, sz)) return nptr;
19+
.free(ptr), onOutOfMemoryError();
1820
assert(0);
1921
}
2022

23+
private void destroy(T)(ref T t) if (is(T == struct))
24+
{
25+
object.destroy(t);
26+
}
27+
28+
private void destroy(T)(ref T t) if (!is(T == struct))
29+
{
30+
t = T.init;
31+
}
32+
33+
private void initialize(T)(ref T t) if (is(T == struct))
34+
{
35+
import core.stdc.string;
36+
if(auto p = typeid(T).init().ptr)
37+
memcpy(&t, p, T.sizeof);
38+
else
39+
memset(&t, 0, T.sizeof);
40+
}
41+
42+
private void initialize(T)(ref T t) if (!is(T == struct))
43+
{
44+
t = T.init;
45+
}
46+
2147
struct Array(T)
2248
{
2349
@disable this(this);
@@ -39,13 +65,11 @@ struct Array(T)
3965

4066
@property void length(size_t nlength)
4167
{
42-
static if (is(T == struct))
43-
if (nlength < length)
44-
foreach (ref val; _ptr[nlength .. length]) destroy(val);
68+
if (nlength < length)
69+
foreach (ref val; _ptr[nlength .. length]) destroy(val);
4570
_ptr = cast(T*)xrealloc(_ptr, nlength * T.sizeof);
46-
static if (is(T == struct))
47-
if (nlength > length)
48-
foreach (ref val; _ptr[length .. nlength]) initialize(val);
71+
if (nlength > length)
72+
foreach (ref val; _ptr[length .. nlength]) initialize(val);
4973
_length = nlength;
5074
}
5175

@@ -101,18 +125,6 @@ struct Array(T)
101125
}
102126

103127
private:
104-
static if (is(T == struct))
105-
{
106-
void initialize(ref T t)
107-
{
108-
import core.stdc.string;
109-
if(auto p = typeid(T).init().ptr)
110-
memcpy(&t, p, T.sizeof);
111-
else
112-
memset(&t, 0, T.sizeof);
113-
}
114-
}
115-
116128
T* _ptr;
117129
size_t _length;
118130
}
@@ -155,16 +167,17 @@ unittest
155167
static assert(!__traits(compiles, foo(ary)));
156168
}
157169

158-
unittest
170+
171+
version (unittest) struct RC
159172
{
160-
static struct RC
161-
{
162-
this(size_t* cnt) { ++*(_cnt = cnt); }
163-
~this() { if (_cnt) --*_cnt; }
164-
this(this) { if (_cnt) ++*_cnt; }
165-
size_t* _cnt;
166-
}
173+
this(size_t* cnt) { ++*(_cnt = cnt); }
174+
~this() { if (_cnt) --*_cnt; }
175+
this(this) { if (_cnt) ++*_cnt; }
176+
size_t* _cnt;
177+
}
167178

179+
unittest
180+
{
168181
Array!RC ary;
169182

170183
size_t cnt;
@@ -178,3 +191,284 @@ unittest
178191
ary.popBack();
179192
assert(cnt == 0);
180193
}
194+
195+
struct HashTab(Key, Value)
196+
{
197+
static struct Node
198+
{
199+
Key _key;
200+
Value _value;
201+
Node* _next;
202+
}
203+
204+
@disable this(this);
205+
206+
~this()
207+
{
208+
reset();
209+
}
210+
211+
void reset()
212+
{
213+
foreach (p; _buckets)
214+
{
215+
while (p !is null)
216+
{
217+
auto pn = p._next;
218+
destroy(*p);
219+
.free(p);
220+
p = pn;
221+
}
222+
}
223+
_buckets.reset();
224+
_length = 0;
225+
}
226+
227+
@property size_t length() const
228+
{
229+
return _length;
230+
}
231+
232+
@property bool empty() const
233+
{
234+
return !_length;
235+
}
236+
237+
void remove(in Key key)
238+
in { assert(key in this); }
239+
body
240+
{
241+
immutable hash = hashOf(key) & mask;
242+
auto pp = &_buckets[hash];
243+
while (*pp)
244+
{
245+
auto p = *pp;
246+
if (p._key == key)
247+
{
248+
*pp = p._next;
249+
destroy(*p);
250+
.free(p);
251+
if (--_length < _buckets.length && _length >= 4)
252+
shrink();
253+
return;
254+
}
255+
else
256+
{
257+
pp = &p._next;
258+
}
259+
}
260+
assert(0);
261+
}
262+
263+
ref inout(Value) opIndex(Key key) inout
264+
{
265+
return *opIn_r(key);
266+
}
267+
268+
void opIndexAssign(Value value, Key key)
269+
{
270+
*get(key) = value;
271+
}
272+
273+
inout(Value)* opIn_r(in Key key) inout
274+
{
275+
if (_buckets.length)
276+
{
277+
immutable hash = hashOf(key) & mask;
278+
for (inout(Node)* p = _buckets[hash]; p !is null; p = p._next)
279+
{
280+
if (p._key == key)
281+
return &p._value;
282+
}
283+
}
284+
return null;
285+
}
286+
287+
int opApply(scope int delegate(ref Key, ref Value) dg)
288+
{
289+
foreach (p; _buckets)
290+
{
291+
while (p !is null)
292+
{
293+
if (auto res = dg(p._key, p._value))
294+
return res;
295+
p = p._next;
296+
}
297+
}
298+
return 0;
299+
}
300+
301+
private:
302+
303+
Value* get(Key key)
304+
{
305+
if (auto p = opIn_r(key))
306+
return p;
307+
308+
if (!_buckets.length)
309+
_buckets.length = 4;
310+
311+
immutable hash = hashOf(key) & mask;
312+
auto p = cast(Node*).malloc(Node.sizeof);
313+
initialize(*p);
314+
p._key = key;
315+
p._next = _buckets[hash];
316+
_buckets[hash] = p;
317+
if (++_length >= 2 * _buckets.length)
318+
grow();
319+
return &p._value;
320+
}
321+
322+
static hash_t hashOf(in ref Key key)
323+
{
324+
import rt.util.hash : hashOf;
325+
static if (is(Key U : U[]))
326+
return hashOf(cast(const ubyte*)key.ptr, key.length * key[0].sizeof);
327+
else
328+
return hashOf(cast(const ubyte*)&key, Key.sizeof);
329+
}
330+
331+
@property hash_t mask() const
332+
{
333+
return _buckets.length - 1;
334+
}
335+
336+
void grow()
337+
in
338+
{
339+
assert(_buckets.length);
340+
}
341+
body
342+
{
343+
immutable ocnt = _buckets.length;
344+
immutable nmask = 2 * ocnt - 1;
345+
_buckets.length = 2 * ocnt;
346+
for (size_t i = 0; i < ocnt; ++i)
347+
{
348+
auto pp = &_buckets[i];
349+
while (*pp)
350+
{
351+
auto p = *pp;
352+
353+
immutable nidx = hashOf(p._key) & nmask;
354+
if (nidx != i)
355+
{
356+
*pp = p._next;
357+
p._next = _buckets[nidx];
358+
_buckets[nidx] = p;
359+
}
360+
else
361+
{
362+
pp = &p._next;
363+
}
364+
}
365+
}
366+
}
367+
368+
void shrink()
369+
in
370+
{
371+
assert(_buckets.length >= 2);
372+
}
373+
body
374+
{
375+
immutable ocnt = _buckets.length;
376+
immutable ncnt = ocnt >> 1;
377+
immutable nmask = ncnt - 1;
378+
379+
for (size_t i = ncnt; i < ocnt; ++i)
380+
{
381+
if (auto tail = _buckets[i])
382+
{
383+
immutable nidx = i & nmask;
384+
auto pp = &_buckets[nidx];
385+
while (*pp)
386+
pp = &(*pp)._next;
387+
*pp = tail;
388+
_buckets[i] = null;
389+
}
390+
}
391+
_buckets.length = ncnt;
392+
}
393+
394+
Array!(Node*) _buckets;
395+
size_t _length;
396+
}
397+
398+
unittest
399+
{
400+
HashTab!(int, int) tab;
401+
402+
foreach(i; 0 .. 100)
403+
tab[i] = 100 - i;
404+
405+
foreach(i; 0 .. 100)
406+
assert(tab[i] == 100 - i);
407+
408+
foreach (k, v; tab)
409+
assert(v == 100 - k);
410+
411+
foreach(i; 0 .. 50)
412+
tab.remove(2 * i);
413+
414+
assert(tab.length == 50);
415+
416+
foreach(i; 0 .. 50)
417+
assert(tab[2 * i + 1] == 100 - 2 * i - 1);
418+
419+
assert(tab.length == 50);
420+
421+
tab.reset();
422+
assert(tab.empty);
423+
tab[0] = 0;
424+
assert(!tab.empty);
425+
destroy(tab);
426+
assert(tab.empty);
427+
428+
// not copyable
429+
static assert(!__traits(compiles, { HashTab!(int, int) tab2 = tab; }));
430+
HashTab!(int, int) tab2;
431+
static assert(!__traits(compiles, tab = tab2));
432+
static void foo(HashTab!(int, int) copy) {}
433+
static assert(!__traits(compiles, foo(tab)));
434+
}
435+
436+
unittest
437+
{
438+
HashTab!(string, size_t) tab;
439+
440+
tab["foo"] = 0;
441+
assert(tab["foo"] == 0);
442+
++tab["foo"];
443+
assert(tab["foo"] == 1);
444+
tab["foo"]++;
445+
assert(tab["foo"] == 2);
446+
447+
auto s = "fo";
448+
s ~= "o";
449+
assert(tab[s] == 2);
450+
assert(tab.length == 1);
451+
tab[s] -= 2;
452+
assert(tab[s] == 0);
453+
tab["foo"] = 12;
454+
assert(tab[s] == 12);
455+
456+
tab.remove("foo");
457+
assert(tab.empty);
458+
}
459+
460+
unittest
461+
{
462+
HashTab!(size_t, RC) tab;
463+
464+
size_t cnt;
465+
assert(cnt == 0);
466+
tab[0] = RC(&cnt);
467+
assert(cnt == 1);
468+
tab[1] = tab[0];
469+
assert(cnt == 2);
470+
tab.remove(0);
471+
assert(cnt == 1);
472+
tab.remove(1);
473+
assert(cnt == 0);
474+
}

0 commit comments

Comments
 (0)