@@ -9,8 +9,8 @@ module vibe.container.hashmap;
9
9
10
10
import vibe.container.seahash : seaHash;
11
11
import vibe.container.internal.utilallocator;
12
+ import vibe.container.internal.rctable;
12
13
13
- import std.conv : emplace;
14
14
import std.traits ;
15
15
16
16
@@ -86,12 +86,6 @@ struct HashMap(TKey, TValue, Traits = DefaultHashMapTraits!TKey, Allocator = IAl
86
86
alias Key = TKey;
87
87
alias Value = TValue;
88
88
89
- Allocator AW (Allocator a) { return a; }
90
- alias AllocatorType = AffixAllocator! (Allocator, int );
91
- static if (is (typeof (AllocatorType.instance)))
92
- alias AllocatorInstanceType = typeof (AllocatorType.instance);
93
- else alias AllocatorInstanceType = AllocatorType;
94
-
95
89
struct TableEntry {
96
90
UnConst! Key key = Traits.clearValue;
97
91
Value value;
@@ -105,42 +99,19 @@ struct HashMap(TKey, TValue, Traits = DefaultHashMapTraits!TKey, Allocator = IAl
105
99
else this .value = value;
106
100
}
107
101
}
102
+
103
+ alias Table = RCTable! (TableEntry, Allocator);
104
+
108
105
private {
109
- TableEntry[] m_table; // NOTE: capacity is always POT
106
+ Table m_table;
110
107
size_t m_length;
111
- static if (! is (typeof (Allocator.instance)))
112
- AllocatorInstanceType m_allocator;
113
108
bool m_resizing;
114
109
}
115
110
116
111
static if (! is (typeof (Allocator.instance))) {
117
112
this (Allocator allocator)
118
113
{
119
- m_allocator = typeof (m_allocator)(AW (allocator));
120
- }
121
- }
122
-
123
- ~this ()
124
- {
125
- int rc;
126
- try rc = m_table is null ? 1 : () @trusted { return -- allocator.prefix(m_table); } ();
127
- catch (Exception e) assert (false , e.msg);
128
-
129
- if (rc == 0 ) {
130
- clear();
131
- if (m_table.ptr ! is null ) () @trusted {
132
- static if (hasIndirections! TableEntry) GC .removeRange(m_table.ptr);
133
- try allocator.dispose(m_table);
134
- catch (Exception e) assert (false , e.msg);
135
- } ();
136
- }
137
- }
138
-
139
- this (this )
140
- @trusted {
141
- if (m_table.ptr) {
142
- try allocator.prefix(m_table)++ ;
143
- catch (Exception e) assert (false , e.msg);
114
+ m_table = Table(allocator);
144
115
}
145
116
}
146
117
@@ -267,21 +238,6 @@ struct HashMap(TKey, TValue, Traits = DefaultHashMapTraits!TKey, Allocator = IAl
267
238
private auto bySlot () { return m_table[].filter! ((ref e) => ! Traits.equals(e.key, Traits.clearValue)); }
268
239
private auto bySlot () const { return m_table[].filter! ((ref e) => ! Traits.equals(e.key, Traits.clearValue)); }
269
240
270
- private @property AllocatorInstanceType allocator()
271
- {
272
- static if (is (typeof (Allocator.instance)))
273
- return AllocatorType.instance;
274
- else {
275
- if (! m_allocator._parent) {
276
- static if (is (Allocator == IAllocator)) {
277
- try m_allocator = typeof (m_allocator)(AW (vibeThreadAllocator()));
278
- catch (Exception e) assert (false , e.msg);
279
- } else assert (false , " Allocator not initialized." );
280
- }
281
- return m_allocator;
282
- }
283
- }
284
-
285
241
private size_t findIndex (Key key)
286
242
const {
287
243
if (m_length == 0 ) return size_t .max;
@@ -321,24 +277,9 @@ struct HashMap(TKey, TValue, Traits = DefaultHashMapTraits!TKey, Allocator = IAl
321
277
322
278
private void makeUnique ()
323
279
@trusted {
324
- if (m_table is null ) return ;
325
-
326
- int rc;
327
- try rc = allocator.prefix(m_table);
328
- catch (Exception e) assert (false , e.msg);
329
- if (rc > 1 ) {
330
- // enforce copy-on-write
331
- auto oldtable = m_table;
332
- try {
333
- m_table = allocator.makeArray! TableEntry(m_table.length);
334
- m_table[] = oldtable;
335
- allocator.prefix(oldtable)-- ;
336
- assert (allocator.prefix(oldtable) > 0 );
337
- allocator.prefix(m_table) = 1 ;
338
- } catch (Exception e) {
339
- assert (false , e.msg);
340
- }
341
- }
280
+ if (m_table.isUnique) return ;
281
+
282
+ m_table = m_table.dup ;
342
283
}
343
284
344
285
private void resize (size_t new_size)
@@ -357,26 +298,25 @@ struct HashMap(TKey, TValue, Traits = DefaultHashMapTraits!TKey, Allocator = IAl
357
298
auto oldtable = m_table;
358
299
359
300
// allocate the new array, automatically initializes with empty entries (Traits.clearValue)
360
- try {
361
- m_table = allocator.makeArray! TableEntry(new_size);
362
- allocator.prefix(m_table) = 1 ;
363
- } catch (Exception e) assert (false , e.msg);
364
- static if (hasIndirections! TableEntry) GC .addRange(m_table.ptr, m_table.length * TableEntry.sizeof);
365
- // perform a move operation of all non-empty elements from the old array to the new one
366
- foreach (ref el; oldtable)
367
- if (! Traits.equals(el.key, Traits.clearValue)) {
368
- auto idx = findInsertIndex(el.key);
369
- (cast (ubyte [])(&m_table[idx])[0 .. 1 ])[] = (cast (ubyte [])(&el)[0 .. 1 ])[];
370
- }
301
+ m_table = m_table.createNew(new_size);
302
+
303
+ if (oldtable.isUnique) {
304
+ // perform a move operation of all non-empty elements from the old array to the new one
305
+ foreach (ref el; oldtable)
306
+ if (! Traits.equals(el.key, Traits.clearValue)) {
307
+ auto idx = findInsertIndex(el.key);
308
+ (cast (ubyte [])(&m_table[idx])[0 .. 1 ])[] = (cast (ubyte [])(&el)[0 .. 1 ])[];
309
+ }
371
310
372
- // all elements have been moved to the new array, so free the old one without calling destructors
373
- int rc;
374
- try rc = oldtable is null ? 1 : -- allocator.prefix(oldtable);
375
- catch (Exception e) assert (false , e.msg);
376
- if (rc == 0 ) {
377
- static if (hasIndirections! TableEntry) GC .removeRange(oldtable.ptr);
378
- try allocator.deallocate(oldtable);
379
- catch (Exception e) assert (false , e.msg);
311
+ // free the old table without calling destructors
312
+ oldtable.deallocate();
313
+ } else {
314
+ // perform a copy operation of all non-empty elements from the old array to the new one
315
+ foreach (ref el; oldtable)
316
+ if (! Traits.equals(el.key, Traits.clearValue)) {
317
+ auto idx = findInsertIndex(el.key);
318
+ m_table[idx] = el;
319
+ }
380
320
}
381
321
}
382
322
}
@@ -509,6 +449,35 @@ unittest { // test for proper use of constructor/post-blit/destructor
509
449
assert (Test.constructedCounter == 0 );
510
450
}
511
451
452
+ unittest { // large alignment test;
453
+ align (32 ) static struct S { int i; }
454
+
455
+ HashMap! (int , S)[] amaps;
456
+ // NOTE: forcing new allocations to increase the likelyhood of getting a misaligned allocation from the GC
457
+ foreach (i; 0 .. 100 ) {
458
+ HashMap! (int , S) a;
459
+ a[1 ] = S(42 );
460
+ a[2 ] = S(43 );
461
+ assert (a[1 ] == S(42 ));
462
+ assert (a[2 ] == S(43 ));
463
+ assert (cast (size_t )cast (void * )&a[1 ] % S.alignof == 0 );
464
+ assert (cast (size_t )cast (void * )&a[2 ] % S.alignof == 0 );
465
+ amaps ~= a;
466
+ }
467
+
468
+ HashMap! (S, int )[] bmaps;
469
+ foreach (i; 0 .. 100 ) {
470
+ HashMap! (S, int ) b;
471
+ b[S(1 )] = 42 ;
472
+ b[S(2 )] = 43 ;
473
+ assert (b[S(1 )] == 42 );
474
+ assert (b[S(2 )] == 43 );
475
+ assert (cast (size_t )cast (void * )&b[S(1 )] % S.alignof == 0 );
476
+ assert (cast (size_t )cast (void * )&b[S(2 )] % S.alignof == 0 );
477
+ bmaps ~= b;
478
+ }
479
+ }
480
+
512
481
private template UnConst (T) {
513
482
static if (is (T U == const (U))) {
514
483
alias UnConst = U;
0 commit comments