@@ -113,15 +113,24 @@ GSHashTableSetShouldCount (GSHashTableRef table)
113
113
114
114
115
115
116
+ typedef enum
117
+ {
118
+ _kGSHashTableRetrieve , // retrieval or deletion
119
+ _kGSHashTableInsert // insertion only
120
+ } _kGSHashTableOperation ;
121
+
122
+ static CFIndex _kGSHashTableBucketCountDeleted = -1 ;
123
+
116
124
CF_INLINE void
117
125
GSHashTableAddKeyValuePair (GSHashTableRef table ,
118
126
GSHashTableBucket * bucket , const void * key ,
119
127
const void * value )
120
128
{
121
129
GSHashTableRetainCallBack keyRetain = table -> _keyCallBacks .retain ;
122
130
GSHashTableRetainCallBack valueRetain = table -> _valueCallBacks .retain ;
131
+ CFIndex count = bucket -> count ;
123
132
124
- bucket -> count ++ ;
133
+ bucket -> count = ( count == _kGSHashTableBucketCountDeleted ) ? 1 : ( count + 1 ) ;
125
134
bucket -> key = keyRetain ? keyRetain (table -> _allocator , key ) : key ;
126
135
bucket -> value = valueRetain ? valueRetain (table -> _allocator , value ) : value ;
127
136
}
@@ -140,7 +149,8 @@ GSHashTableReplaceKeyValuePair (GSHashTableRef table,
140
149
}
141
150
142
151
CF_INLINE void
143
- GSHashTableRemoveKeyValuePair (GSHashTableRef table , GSHashTableBucket * bucket )
152
+ GSHashTableRemoveKeyValuePair (GSHashTableRef table , GSHashTableBucket * bucket ,
153
+ CFIndex bucketCountDeleted )
144
154
{
145
155
GSHashTableReleaseCallBack keyRelease = table -> _keyCallBacks .release ;
146
156
GSHashTableReleaseCallBack valueRelease = table -> _valueCallBacks .release ;
@@ -150,56 +160,75 @@ GSHashTableRemoveKeyValuePair (GSHashTableRef table, GSHashTableBucket * bucket)
150
160
if (valueRelease )
151
161
valueRelease (table -> _allocator , bucket -> value );
152
162
153
- bucket -> count = 0 ;
163
+ bucket -> count = bucketCountDeleted ;
154
164
bucket -> key = NULL ;
155
165
bucket -> value = NULL ;
156
166
}
157
167
168
+ typedef Boolean (* GSHashTableBucketIsUnoccupiedCallback ) (GSHashTableBucket bucket );
169
+
170
+ CF_INLINE Boolean
171
+ GSHashTableBucketIsUnoccupied (GSHashTableBucket bucket , _kGSHashTableOperation operation )
172
+ {
173
+ switch (operation ) {
174
+ case _kGSHashTableRetrieve :
175
+ return bucket .count == 0 ; // treat deleted buckets as occupied
176
+ case _kGSHashTableInsert :
177
+ return bucket .count <= 0 ; // treat deleted buckets as unoccupied
178
+ }
179
+ #if __has_builtin (__builtin_unreachable )
180
+ __builtin_unreachable ();
181
+ #else
182
+ abort ();
183
+ #endif
184
+ }
185
+
186
+ static Boolean
187
+ GSHashTableEqualPointers (const void * key1 , const void * key2 )
188
+ {
189
+ return key1 == key2 ;
190
+ }
191
+
158
192
static GSHashTableBucket *
159
- GSHashTableFindBucket (GSHashTableRef table , const void * key )
193
+ GSHashTableFindBucket (GSHashTableRef table , const void * key ,
194
+ _kGSHashTableOperation operation )
160
195
{
161
196
GSHashTableBucket * buckets ;
162
197
CFIndex capacity ;
163
- CFIndex idx ;
198
+ CFIndex initialIdx , idx ;
164
199
CFHashCode hash ;
165
200
Boolean matched ;
166
201
GSHashTableHashCallBack fHash = table -> _keyCallBacks .hash ;
167
202
GSHashTableEqualCallBack fEqual = table -> _keyCallBacks .equal ;
203
+
204
+ if (!fEqual )
205
+ fEqual = GSHashTableEqualPointers ;
168
206
169
207
buckets = table -> _buckets ;
170
208
capacity = table -> _capacity ;
171
209
hash = fHash ? fHash (key ) : GSHashPointer (key );
172
- idx = hash % capacity ;
173
- matched = buckets [idx ].key == NULL || (fEqual ?
174
- fEqual (key ,
175
- buckets [idx ].key ) : key ==
176
- buckets [idx ].key );
210
+ initialIdx = idx = hash % capacity ;
211
+ matched = GSHashTableBucketIsUnoccupied (buckets [idx ], operation )
212
+ || (buckets [idx ].key && fEqual (key , buckets [idx ].key ));
177
213
178
214
if (!matched )
179
215
{
180
216
CFHashCode hash2 = 1 + ((hash / capacity ) % (capacity - 1 ));
181
-
182
- if (fEqual )
183
- {
184
- do
185
- {
186
- hash += hash2 ;
187
- idx = hash % capacity ;
188
- }
189
- while (buckets [idx ].key != NULL && !fEqual (key , buckets [idx ].key ));
190
- }
191
- else
217
+
218
+ do
192
219
{
193
- do
194
- {
195
- hash += hash2 ;
196
- idx = hash % capacity ;
197
- }
198
- while (buckets [idx ].key != NULL && key != buckets [idx ].key );
220
+ hash += hash2 ;
221
+ idx = hash % capacity ;
222
+ matched = GSHashTableBucketIsUnoccupied (buckets [idx ], operation )
223
+ || (buckets [idx ].key && fEqual (key , buckets [idx ].key ));
199
224
}
225
+ while (!matched && idx != initialIdx );
200
226
}
201
227
202
- return & buckets [idx ];
228
+ // match should be NULL for _kGSHashTableRetrieve ONLY
229
+ assert (matched || operation == _kGSHashTableRetrieve );
230
+
231
+ return matched ? & buckets [idx ] : NULL ;
203
232
}
204
233
205
234
@@ -275,7 +304,8 @@ GSHashTableCreate (CFAllocatorRef alloc, CFTypeID typeID,
275
304
{
276
305
for (idx = 0 ; idx < numValues ; ++ idx )
277
306
{
278
- bucket = GSHashTableFindBucket (new , keys [idx ]);
307
+ bucket = GSHashTableFindBucket (new , keys [idx ],
308
+ _kGSHashTableInsert );
279
309
GSHashTableAddKeyValuePair (new , bucket , keys [idx ], values [idx ]);
280
310
new -> _count += 1 ;
281
311
}
@@ -304,7 +334,8 @@ GSHashTableCreateCopy (CFAllocatorRef alloc, GSHashTableRef table)
304
334
{
305
335
if (buckets [idx ].key )
306
336
{
307
- bucket = GSHashTableFindBucket (new , buckets [idx ].key );
337
+ bucket = GSHashTableFindBucket (new , buckets [idx ].key ,
338
+ _kGSHashTableInsert );
308
339
GSHashTableAddKeyValuePair (new , bucket , buckets [idx ].key ,
309
340
buckets [idx ].value );
310
341
new -> _count += 1 ;
@@ -338,16 +369,22 @@ GSHashTableEqual (GSHashTableRef table1, GSHashTableRef table2)
338
369
end = current + table1 -> _capacity ;
339
370
keyEqual = table1 -> _keyCallBacks .equal ;
340
371
valueEqual = table1 -> _valueCallBacks .equal ;
372
+
373
+ if (!keyEqual )
374
+ keyEqual = GSHashTableEqualPointers ;
375
+ if (!valueEqual )
376
+ valueEqual = GSHashTableEqualPointers ;
377
+
341
378
while (current < end )
342
379
{
343
380
if (current -> count > 0 )
344
381
{
345
- other = GSHashTableFindBucket (table2 , current -> key );
346
- if ( current -> count != other -> count
347
- || keyEqual ? ! keyEqual ( current -> key , other -> key ) :
348
- current -> key != other -> key
349
- || valueEqual ? ! valueEqual (current -> value , other -> value ) :
350
- current -> value != other -> value )
382
+ other = GSHashTableFindBucket (table2 , current -> key ,
383
+ _kGSHashTableRetrieve );
384
+ if (! other
385
+ || current -> count != other -> count
386
+ || ! keyEqual (current -> key , other -> key )
387
+ || ! valueEqual ( current -> value , other -> value ) )
351
388
return false;
352
389
}
353
390
++ current ;
@@ -369,9 +406,9 @@ Boolean
369
406
GSHashTableContainsKey (GSHashTableRef table , const void * key )
370
407
{
371
408
GSHashTableBucket * bucket ;
372
- bucket = GSHashTableFindBucket (table , key );
409
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableRetrieve );
373
410
374
- return bucket -> count > 0 ? true : false;
411
+ return bucket ? ( bucket -> count > 0 ? true : false) : false;
375
412
}
376
413
377
414
Boolean
@@ -381,12 +418,14 @@ GSHashTableContainsValue (GSHashTableRef table, const void *value)
381
418
GSHashTableBucket * buckets = table -> _buckets ;
382
419
GSHashTableEqualCallBack equal = table -> _valueCallBacks .equal ;
383
420
421
+ if (!equal )
422
+ equal = GSHashTableEqualPointers ;
423
+
384
424
for (idx = 0 ; idx < table -> _capacity ; ++ idx )
385
425
{
386
426
if (buckets [idx ].key )
387
427
{
388
- if (equal ? equal (value , buckets [idx ].value ) :
389
- value == buckets [idx ].value )
428
+ if (equal (value , buckets [idx ].value ))
390
429
return true;
391
430
}
392
431
}
@@ -402,8 +441,10 @@ GSHashTableGetCount (GSHashTableRef table)
402
441
CFIndex
403
442
GSHashTableGetCountOfKey (GSHashTableRef table , const void * key )
404
443
{
405
- GSHashTableBucket * bucket = GSHashTableFindBucket (table , key );
406
- return bucket -> count ;;
444
+ GSHashTableBucket * bucket ;
445
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableRetrieve );
446
+
447
+ return bucket ? bucket -> count : 0 ;
407
448
}
408
449
409
450
CFIndex
@@ -414,12 +455,14 @@ GSHashTableGetCountOfValue (GSHashTableRef table, const void *value)
414
455
GSHashTableBucket * buckets = table -> _buckets ;
415
456
GSHashTableEqualCallBack equal = table -> _valueCallBacks .equal ;
416
457
458
+ if (!equal )
459
+ equal = GSHashTableEqualPointers ;
460
+
417
461
for (idx = 0 ; idx < table -> _capacity ; ++ idx )
418
462
{
419
463
if (buckets [idx ].key )
420
464
{
421
- if (equal ? equal (value , buckets [idx ].value ) :
422
- value == buckets [idx ].value )
465
+ if (equal (value , buckets [idx ].value ))
423
466
count += buckets [idx ].count ;
424
467
}
425
468
}
@@ -451,9 +494,9 @@ const void *
451
494
GSHashTableGetValue (GSHashTableRef table , const void * key )
452
495
{
453
496
GSHashTableBucket * bucket ;
454
- bucket = GSHashTableFindBucket (table , key );
455
-
456
- return bucket -> value ;
497
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableRetrieve );
498
+
499
+ return bucket ? bucket -> value : NULL ;
457
500
}
458
501
459
502
@@ -478,7 +521,8 @@ GSHashTableRehash (GSHashTableRef table, CFIndex newCapacity)
478
521
{
479
522
if (oldBuckets [idx ].key )
480
523
{
481
- bucket = GSHashTableFindBucket (table , oldBuckets [idx ].key );
524
+ bucket = GSHashTableFindBucket (table , oldBuckets [idx ].key ,
525
+ _kGSHashTableInsert );
482
526
GSHashTableAddKeyValuePair (table , bucket , oldBuckets [idx ].key ,
483
527
oldBuckets [idx ].value );
484
528
}
@@ -562,7 +606,8 @@ GSHashTableCreateMutableCopy (CFAllocatorRef alloc, GSHashTableRef table,
562
606
{
563
607
if (buckets [idx ].key )
564
608
{
565
- bucket = GSHashTableFindBucket (new , buckets [idx ].key );
609
+ bucket = GSHashTableFindBucket (new , buckets [idx ].key ,
610
+ _kGSHashTableInsert );
566
611
GSHashTableAddKeyValuePair (new , bucket , buckets [idx ].key ,
567
612
buckets [idx ].value );
568
613
new -> _count += 1 ;
@@ -580,8 +625,11 @@ GSHashTableAddValue (GSHashTableRef table, const void *key, const void *value)
580
625
581
626
GSHashTableGrowIfNeeded (table );
582
627
583
- bucket = GSHashTableFindBucket (table , key );
584
- if (bucket -> count == 0 )
628
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableRetrieve );
629
+ if (!bucket )
630
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableInsert );
631
+
632
+ if (bucket -> count <= 0 )
585
633
{
586
634
GSHashTableAddKeyValuePair (table , bucket , key , value );
587
635
table -> _count += 1 ;
@@ -594,8 +642,8 @@ GSHashTableReplaceValue (GSHashTableRef table, const void *key,
594
642
{
595
643
GSHashTableBucket * bucket ;
596
644
597
- bucket = GSHashTableFindBucket (table , key );
598
- if (bucket -> count > 0 )
645
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableRetrieve );
646
+ if (bucket && bucket -> count > 0 )
599
647
GSHashTableReplaceKeyValuePair (table , bucket , key , value );
600
648
}
601
649
@@ -606,7 +654,10 @@ GSHashTableSetValue (GSHashTableRef table, const void *key, const void *value)
606
654
607
655
GSHashTableGrowIfNeeded (table );
608
656
609
- bucket = GSHashTableFindBucket (table , key );
657
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableRetrieve );
658
+ if (!bucket )
659
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableInsert );
660
+
610
661
if (bucket -> count > 0 )
611
662
{
612
663
GSHashTableReplaceKeyValuePair (table , bucket , key , value );
@@ -627,7 +678,7 @@ GSHashTableRemoveAll (GSHashTableRef table)
627
678
for (idx = 0 ; idx < table -> _capacity ; ++ idx )
628
679
{
629
680
if (buckets [idx ].count > 0 )
630
- GSHashTableRemoveKeyValuePair (table , & buckets [idx ]);
681
+ GSHashTableRemoveKeyValuePair (table , & buckets [idx ], 0 );
631
682
}
632
683
table -> _count = 0 ;
633
684
}
@@ -637,16 +688,20 @@ GSHashTableRemoveValue (GSHashTableRef table, const void *key)
637
688
{
638
689
GSHashTableBucket * bucket ;
639
690
640
- GSHashTableShrinkIfNeeded (table );
641
-
642
- bucket = GSHashTableFindBucket (table , key );
643
- if (bucket -> count > 1 )
644
- {
645
- bucket -> count -= 1 ;
646
- }
647
- else if (bucket -> count == 1 )
691
+ bucket = GSHashTableFindBucket (table , key , _kGSHashTableRetrieve );
692
+ if (bucket )
648
693
{
649
- GSHashTableRemoveKeyValuePair (table , bucket );
650
- table -> _count -= 1 ;
694
+ if (bucket -> count > 1 )
695
+ {
696
+ bucket -> count -= 1 ;
697
+ }
698
+ else if (bucket -> count == 1 )
699
+ {
700
+ GSHashTableRemoveKeyValuePair (table , bucket ,
701
+ _kGSHashTableBucketCountDeleted );
702
+ table -> _count -= 1 ;
703
+ }
704
+
705
+ GSHashTableShrinkIfNeeded (table );
651
706
}
652
707
}
0 commit comments