8
8
*/
9
9
module rt.util.container ;
10
10
11
+ import core.stdc.stdlib : free, malloc, realloc;
12
+
11
13
private void * xrealloc (void * ptr, size_t sz)
12
14
{
13
- import core.stdc.stdlib , core. exception ;
15
+ import core.exception ;
14
16
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();
18
20
assert (0 );
19
21
}
20
22
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
+
21
47
struct Array (T)
22
48
{
23
49
@disable this (this );
@@ -39,13 +65,11 @@ struct Array(T)
39
65
40
66
@property void length(size_t nlength)
41
67
{
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);
45
70
_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);
49
73
_length = nlength;
50
74
}
51
75
@@ -101,18 +125,6 @@ struct Array(T)
101
125
}
102
126
103
127
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
-
116
128
T* _ptr;
117
129
size_t _length;
118
130
}
@@ -155,16 +167,17 @@ unittest
155
167
static assert (! __traits(compiles, foo(ary)));
156
168
}
157
169
158
- unittest
170
+
171
+ version (unittest ) struct RC
159
172
{
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
+ }
167
178
179
+ unittest
180
+ {
168
181
Array! RC ary;
169
182
170
183
size_t cnt;
@@ -178,3 +191,284 @@ unittest
178
191
ary.popBack();
179
192
assert (cnt == 0 );
180
193
}
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