10
10
using System . Diagnostics . CodeAnalysis ;
11
11
using System . Linq ;
12
12
using System . Runtime . CompilerServices ;
13
+ using BTDB . Locks ;
13
14
14
15
namespace BTDB . Collections ;
15
16
@@ -32,8 +33,10 @@ public class RefDictionary<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey
32
33
static readonly Entry [ ] InitialEntries = new Entry [ 1 ] ;
33
34
34
35
int _count ;
36
+
35
37
// 0-based index into _entries of head of free chain: -1 means empty
36
38
int _freeList = - 1 ;
39
+
37
40
// 1-based index into _entries; 0 means empty
38
41
int [ ] _buckets ;
39
42
Entry [ ] _entries ;
@@ -44,7 +47,9 @@ public class RefDictionary<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey
44
47
struct Entry
45
48
{
46
49
public TKey key ;
50
+
47
51
public TValue value ;
52
+
48
53
// 0-based index of next entry in chain: -1 means end of chain
49
54
// also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3,
50
55
// so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc.
@@ -78,7 +83,8 @@ public bool ContainsKey(TKey key)
78
83
var entries = _entries ;
79
84
var collisionCount = 0 ;
80
85
for ( var i = _buckets [ key . GetHashCode ( ) & ( _buckets . Length - 1 ) ] - 1 ;
81
- ( uint ) i < ( uint ) entries . Length ; i = entries [ i ] . next )
86
+ ( uint ) i < ( uint ) entries . Length ;
87
+ i = entries [ i ] . next )
82
88
{
83
89
if ( key . Equals ( entries [ i ] . key ) )
84
90
return true ;
@@ -88,6 +94,7 @@ public bool ContainsKey(TKey key)
88
94
// Break out of the loop and throw, rather than looping forever.
89
95
HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
90
96
}
97
+
91
98
collisionCount ++ ;
92
99
}
93
100
@@ -100,26 +107,76 @@ public bool TryGetValue(TKey key, out TValue value)
100
107
var entries = _entries ;
101
108
var collisionCount = 0 ;
102
109
for ( var i = _buckets [ key . GetHashCode ( ) & ( _buckets . Length - 1 ) ] - 1 ;
103
- ( uint ) i < ( uint ) entries . Length ; i = entries [ i ] . next )
110
+ ( uint ) i < ( uint ) entries . Length ;
111
+ i = entries [ i ] . next )
104
112
{
105
113
if ( key . Equals ( entries [ i ] . key ) )
106
114
{
107
115
value = entries [ i ] . value ;
108
116
return true ;
109
117
}
118
+
110
119
if ( collisionCount == entries . Length )
111
120
{
112
121
// The chain of entries forms a loop; which means a concurrent update has happened.
113
122
// Break out of the loop and throw, rather than looping forever.
114
123
HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
115
124
}
125
+
116
126
collisionCount ++ ;
117
127
}
118
128
119
129
value = default ;
120
130
return false ;
121
131
}
122
132
133
+ public bool TryGetValueSeqLock ( TKey key , out TValue value , ref SeqLock seqLock )
134
+ {
135
+ if ( key == null ) HashHelpers . ThrowKeyArgumentNullException ( ) ;
136
+ var hash = key . GetHashCode ( ) ;
137
+ var seqCounter = seqLock . StartRead ( ) ;
138
+ retry :
139
+ try
140
+ {
141
+ var entries = _entries ;
142
+ var collisionCount = 0 ;
143
+ for ( var i = _buckets [ hash & ( _buckets . Length - 1 ) ] - 1 ;
144
+ ( uint ) i < ( uint ) entries . Length ;
145
+ i = entries [ i ] . next )
146
+ {
147
+ if ( key . Equals ( entries [ i ] . key ) )
148
+ {
149
+ value = entries [ i ] . value ;
150
+ if ( seqLock . RetryRead ( ref seqCounter ) ) goto retry ;
151
+ return true ;
152
+ }
153
+
154
+ if ( collisionCount == entries . Length )
155
+ {
156
+ if ( seqLock . RetryRead ( ref seqCounter ) ) goto retry ;
157
+ // The chain of entries forms a loop; which means a concurrent update has happened.
158
+ // Break out of the loop and throw, rather than looping forever.
159
+ HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
160
+ }
161
+ else if ( ( collisionCount & 0xF ) == 4 )
162
+ {
163
+ if ( seqLock . RetryRead ( ref seqCounter ) ) goto retry ;
164
+ }
165
+
166
+ collisionCount ++ ;
167
+ }
168
+
169
+ if ( seqLock . RetryRead ( ref seqCounter ) ) goto retry ;
170
+ value = default ;
171
+ return false ;
172
+ }
173
+ catch
174
+ {
175
+ if ( seqLock . RetryRead ( ref seqCounter ) ) goto retry ;
176
+ throw ;
177
+ }
178
+ }
179
+
123
180
public bool Remove ( TKey key )
124
181
{
125
182
if ( key == null ) HashHelpers . ThrowKeyArgumentNullException ( ) ;
@@ -135,11 +192,13 @@ public bool Remove(TKey key)
135
192
if ( candidate . key . Equals ( key ) )
136
193
{
137
194
if ( lastIndex != - 1 )
138
- { // Fixup preceding element in chain to point to next (if any)
195
+ {
196
+ // Fixup preceding element in chain to point to next (if any)
139
197
entries [ lastIndex ] . next = candidate . next ;
140
198
}
141
199
else
142
- { // Fixup bucket to new head (if any)
200
+ {
201
+ // Fixup bucket to new head (if any)
143
202
_buckets [ bucketIndex ] = candidate . next + 1 ;
144
203
}
145
204
@@ -151,6 +210,7 @@ public bool Remove(TKey key)
151
210
_count -- ;
152
211
return true ;
153
212
}
213
+
154
214
lastIndex = entryIndex ;
155
215
entryIndex = candidate . next ;
156
216
@@ -160,6 +220,7 @@ public bool Remove(TKey key)
160
220
// Break out of the loop and throw, rather than looping forever.
161
221
HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
162
222
}
223
+
163
224
collisionCount ++ ;
164
225
}
165
226
@@ -174,7 +235,8 @@ public ref TValue GetOrFakeValueRef(TKey key)
174
235
var collisionCount = 0 ;
175
236
var bucketIndex = key . GetHashCode ( ) & ( _buckets . Length - 1 ) ;
176
237
for ( var i = _buckets [ bucketIndex ] - 1 ;
177
- ( uint ) i < ( uint ) entries . Length ; i = entries [ i ] . next )
238
+ ( uint ) i < ( uint ) entries . Length ;
239
+ i = entries [ i ] . next )
178
240
{
179
241
if ( key . Equals ( entries [ i ] . key ) )
180
242
return ref entries [ i ] . value ;
@@ -184,6 +246,7 @@ public ref TValue GetOrFakeValueRef(TKey key)
184
246
// Break out of the loop and throw, rather than looping forever.
185
247
HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
186
248
}
249
+
187
250
collisionCount ++ ;
188
251
}
189
252
@@ -197,19 +260,22 @@ public ref TValue GetOrFakeValueRef(TKey key, out bool found)
197
260
var collisionCount = 0 ;
198
261
var bucketIndex = key . GetHashCode ( ) & ( _buckets . Length - 1 ) ;
199
262
for ( var i = _buckets [ bucketIndex ] - 1 ;
200
- ( uint ) i < ( uint ) entries . Length ; i = entries [ i ] . next )
263
+ ( uint ) i < ( uint ) entries . Length ;
264
+ i = entries [ i ] . next )
201
265
{
202
266
if ( key . Equals ( entries [ i ] . key ) )
203
267
{
204
268
found = true ;
205
269
return ref entries [ i ] . value ;
206
270
}
271
+
207
272
if ( collisionCount == entries . Length )
208
273
{
209
274
// The chain of entries forms a loop; which means a concurrent update has happened.
210
275
// Break out of the loop and throw, rather than looping forever.
211
276
HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
212
277
}
278
+
213
279
collisionCount ++ ;
214
280
}
215
281
@@ -225,7 +291,8 @@ public ref TValue GetOrAddValueRef(TKey key)
225
291
var collisionCount = 0 ;
226
292
var bucketIndex = key . GetHashCode ( ) & ( _buckets . Length - 1 ) ;
227
293
for ( var i = _buckets [ bucketIndex ] - 1 ;
228
- ( uint ) i < ( uint ) entries . Length ; i = entries [ i ] . next )
294
+ ( uint ) i < ( uint ) entries . Length ;
295
+ i = entries [ i ] . next )
229
296
{
230
297
if ( key . Equals ( entries [ i ] . key ) )
231
298
return ref entries [ i ] . value ;
@@ -235,12 +302,39 @@ public ref TValue GetOrAddValueRef(TKey key)
235
302
// Break out of the loop and throw, rather than looping forever.
236
303
HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
237
304
}
305
+
238
306
collisionCount ++ ;
239
307
}
240
308
241
309
return ref AddKey ( key , bucketIndex ) ;
242
310
}
243
311
312
+ public bool TryAdd ( TKey key , in TValue value )
313
+ {
314
+ if ( key == null ) HashHelpers . ThrowKeyArgumentNullException ( ) ;
315
+ var entries = _entries ;
316
+ var collisionCount = 0 ;
317
+ var bucketIndex = key . GetHashCode ( ) & ( _buckets . Length - 1 ) ;
318
+ for ( var i = _buckets [ bucketIndex ] - 1 ;
319
+ ( uint ) i < ( uint ) entries . Length ;
320
+ i = entries [ i ] . next )
321
+ {
322
+ if ( key . Equals ( entries [ i ] . key ) )
323
+ return false ;
324
+ if ( collisionCount == entries . Length )
325
+ {
326
+ // The chain of entries forms a loop; which means a concurrent update has happened.
327
+ // Break out of the loop and throw, rather than looping forever.
328
+ HashHelpers . ThrowInvalidOperationException_ConcurrentOperationsNotSupported ( ) ;
329
+ }
330
+
331
+ collisionCount ++ ;
332
+ }
333
+
334
+ AddKey ( key , bucketIndex ) = value ;
335
+ return true ;
336
+ }
337
+
244
338
[ MethodImpl ( MethodImplOptions . NoInlining ) ]
245
339
ref TValue AddKey ( TKey key , int bucketIndex )
246
340
{
@@ -259,6 +353,7 @@ ref TValue AddKey(TKey key, int bucketIndex)
259
353
bucketIndex = key . GetHashCode ( ) & ( _buckets . Length - 1 ) ;
260
354
// entry indexes were not changed by Resize
261
355
}
356
+
262
357
entryIndex = _count ;
263
358
}
264
359
@@ -312,13 +407,16 @@ public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
312
407
entry . key ,
313
408
entry . value ) ;
314
409
}
410
+
315
411
i ++ ;
316
412
}
317
413
}
318
414
319
415
public Enumerator GetEnumerator ( ) => new Enumerator ( this ) ; // avoid boxing
416
+
320
417
IEnumerator < KeyValuePair < TKey , TValue > > IEnumerable < KeyValuePair < TKey , TValue > > . GetEnumerator ( ) =>
321
418
new Enumerator ( this ) ;
419
+
322
420
IEnumerator IEnumerable . GetEnumerator ( ) => new Enumerator ( this ) ;
323
421
324
422
public struct Enumerator : IEnumerator < KeyValuePair < TKey , TValue > >
@@ -365,7 +463,9 @@ void IEnumerator.Reset()
365
463
_count = _dictionary . _count ;
366
464
}
367
465
368
- public void Dispose ( ) { }
466
+ public void Dispose ( )
467
+ {
468
+ }
369
469
}
370
470
371
471
@@ -386,7 +486,10 @@ public ref TValue ValueRef(uint index)
386
486
return ref _entries [ ( int ) index ] . value ;
387
487
}
388
488
389
- public IndexEnumerator Index { get => new IndexEnumerator ( this ) ; }
489
+ public IndexEnumerator Index
490
+ {
491
+ get => new IndexEnumerator ( this ) ;
492
+ }
390
493
391
494
public struct IndexEnumerator : IEnumerable < uint >
392
495
{
@@ -444,9 +547,10 @@ void IEnumerator.Reset()
444
547
_count = _dictionary . _count ;
445
548
}
446
549
447
- public void Dispose ( ) { }
550
+ public void Dispose ( )
551
+ {
552
+ }
448
553
}
449
-
450
554
}
451
555
}
452
556
@@ -462,9 +566,6 @@ public RefDictionaryDebugView(RefDictionary<K, V> dictionary)
462
566
[ DebuggerBrowsable ( DebuggerBrowsableState . RootHidden ) ]
463
567
public KeyValuePair < K , V > [ ] Items
464
568
{
465
- get
466
- {
467
- return _dictionary . ToArray ( ) ;
468
- }
569
+ get { return _dictionary . ToArray ( ) ; }
469
570
}
470
571
}
0 commit comments