71
71
// default traits if it can be assigned from 0.
72
72
// static const bool IsDeleted(const ELEMENT &e) Compare element with Deleted sentinal value. May be inherited from the
73
73
// default traits if it can be assigned from -1.
74
+ // static bool ShouldDelete(const ELEMENT &e) Called in addition to IsDeleted() when s_supports_autoremove is true, see more
75
+ // information there.
74
76
//
75
77
// static void OnDestructPerEntryCleanupAction(ELEMENT& e) Called on every element when in hashtable destructor.
76
78
// s_DestructPerEntryCleanupAction must be set to true if implemented.
79
+ // static void OnRemovePerEntryCleanupAction(ELEMENT& e) Called when an element is removed from the hashtable, including when
80
+ // the hashtable is destructed. s_RemovePerEntryCleanupAction must be set to true
81
+ // if implemented.
77
82
//
78
83
// s_growth_factor_numerator
79
84
// s_growth_factor_denominator Factor to grow allocation (numerator/denominator).
92
97
// in that there may be more copies of the template instantiated through
93
98
// the system as different variants are used.
94
99
//
100
+ // s_supports_autoremove When autoremove is supported, ShouldDelete() is called in addition to
101
+ // IsDeleted() in any situation that involves walking the table's elements, to
102
+ // determine if an element should be deleted. It enables the hash table to
103
+ // self-clean elements whose underlying lifetime may be controlled externally. Note
104
+ // that since some lookup/iteration operations are const (can operate on a
105
+ // "const SHash"), when autoremove is supported, any such const operation may still
106
+ // modify the hash table. If this is set to true, s_supports_remove must also be
107
+ // true.
108
+ //
95
109
// s_DestructPerEntryCleanupAction Set to true if OnDestructPerEntryCleanupAction has non-empty implementation.
110
+ // s_RemovePerEntryCleanupAction Set to true if OnRemovePerEntryCleanupAction has non-empty implementation.
111
+ // Only one of s_DestructPerEntryCleanupAction and s_RemovePerEntryCleanupAction
112
+ // may be set to true.
96
113
//
97
114
// DefaultHashTraits provides defaults for seldomly customized values in traits classes.
98
115
@@ -115,15 +132,20 @@ class DefaultSHashTraits
115
132
static const COUNT_T s_minimum_allocation = 7 ;
116
133
117
134
static const bool s_supports_remove = true ;
135
+ static const bool s_supports_autoremove = false ;
118
136
119
137
static ELEMENT Null () { return (ELEMENT)(TADDR)0 ; }
120
138
static ELEMENT Deleted () { return (ELEMENT)(TADDR)-1 ; }
121
139
static bool IsNull (const ELEMENT &e) { return e == (ELEMENT)(TADDR)0 ; }
122
140
static bool IsDeleted (const ELEMENT &e) { return e == (ELEMENT)(TADDR)-1 ; }
141
+ static bool ShouldDelete (const ELEMENT &e) { return false ; }
123
142
124
143
static inline void OnDestructPerEntryCleanupAction (const ELEMENT& e) { /* Do nothing */ }
125
144
static const bool s_DestructPerEntryCleanupAction = false ;
126
145
146
+ static void OnRemovePerEntryCleanupAction (const ELEMENT &e) {}
147
+ static const bool s_RemovePerEntryCleanupAction = false ;
148
+
127
149
static const bool s_NoThrow = true ;
128
150
129
151
// No defaults - must specify:
@@ -177,6 +199,10 @@ class EMPTY_BASES_DECL SHash : public TRAITS
177
199
178
200
const element_t * LookupPtr (key_t key) const ;
179
201
202
+ // Pointer-based flavor to replace an existing element (allows efficient access to tables of structures)
203
+
204
+ void ReplacePtr (const element_t *elementPtr, const element_t &newElement, bool invokeCleanupAction = true );
205
+
180
206
// Add an element to the hash table. This will never replace an element; multiple
181
207
// elements may be stored with the same key.
182
208
@@ -298,7 +324,7 @@ class EMPTY_BASES_DECL SHash : public TRAITS
298
324
// it is perfectly fine for the element to be a duplicate - if so it
299
325
// is added an additional time. Returns TRUE if a new empty spot was used;
300
326
// FALSE if an existing deleted slot.
301
- static BOOL Add (element_t *table, count_t tableSize, const element_t &element);
327
+ BOOL Add (element_t *table, count_t tableSize, const element_t &element);
302
328
303
329
// Utility function to add a new element to the hash table, if no element with the same key
304
330
// is already there. Otherwise, it will replace the existing element. This has the effect of
@@ -308,7 +334,7 @@ class EMPTY_BASES_DECL SHash : public TRAITS
308
334
// Utility function to find the first element with the given key in
309
335
// the hash table.
310
336
311
- static const element_t * Lookup (PTR_element_t table, count_t tableSize, key_t key);
337
+ const element_t * Lookup (PTR_element_t table, count_t tableSize, key_t key) const ;
312
338
313
339
// Utility function to remove the first element with the given key
314
340
// in the hash table.
@@ -334,13 +360,17 @@ class EMPTY_BASES_DECL SHash : public TRAITS
334
360
// Some compilers won't compile the separate implementation in shash.inl
335
361
protected:
336
362
363
+ SHash *m_hash;
337
364
PTR_element_t m_table;
338
365
count_t m_tableSize;
339
366
count_t m_index;
340
367
341
-
368
+ // Iteration may modify the hash table if s_supports_autoremove is true. Since it typically does not modify the hash
369
+ // table, it should be possible to iterate over a "const SHash". So this takes a "const SHash *" and casts away the
370
+ // const to support autoremove.
342
371
Index (const SHash *hash, BOOL begin)
343
- : m_table(hash->m_table),
372
+ : m_hash(const_cast <SHash *>(hash)),
373
+ m_table (hash->m_table),
344
374
m_tableSize(hash->m_tableSize),
345
375
m_index(begin ? 0 : m_tableSize)
346
376
{
@@ -358,9 +388,24 @@ class EMPTY_BASES_DECL SHash : public TRAITS
358
388
{
359
389
LIMITED_METHOD_CONTRACT;
360
390
361
- if (m_index < m_tableSize)
362
- if (TRAITS::IsNull (m_table[m_index]) || TRAITS::IsDeleted (m_table[m_index]))
363
- Next ();
391
+ if (m_index >= m_tableSize)
392
+ {
393
+ return ;
394
+ }
395
+
396
+ if (!TRAITS::IsNull (m_table[m_index]) && !TRAITS::IsDeleted (m_table[m_index]))
397
+ {
398
+ if (TRAITS::s_supports_autoremove && TRAITS::ShouldDelete (m_table[m_index]))
399
+ {
400
+ m_hash->RemoveElement (m_table, m_tableSize, &m_table[m_index]);
401
+ }
402
+ else
403
+ {
404
+ return ;
405
+ }
406
+ }
407
+
408
+ Next ();
364
409
}
365
410
366
411
void Next ()
@@ -376,7 +421,16 @@ class EMPTY_BASES_DECL SHash : public TRAITS
376
421
if (m_index >= m_tableSize)
377
422
break ;
378
423
if (!TRAITS::IsNull (m_table[m_index]) && !TRAITS::IsDeleted (m_table[m_index]))
379
- break ;
424
+ {
425
+ if (TRAITS::s_supports_autoremove && TRAITS::ShouldDelete (m_table[m_index]))
426
+ {
427
+ m_hash->RemoveElement (m_table, m_tableSize, &m_table[m_index]);
428
+ }
429
+ else
430
+ {
431
+ break ;
432
+ }
433
+ }
380
434
}
381
435
}
382
436
@@ -442,11 +496,26 @@ class EMPTY_BASES_DECL SHash : public TRAITS
442
496
m_increment = (hash % (this ->m_tableSize -1 )) + 1 ;
443
497
444
498
// Find first valid element
499
+
445
500
if (TRAITS::IsNull (this ->m_table [this ->m_index ]))
501
+ {
446
502
this ->m_index = this ->m_tableSize ;
447
- else if (TRAITS::IsDeleted (this ->m_table [this ->m_index ])
448
- || !TRAITS::Equals (m_key, TRAITS::GetKey (this ->m_table [this ->m_index ])))
449
- Next ();
503
+ return ;
504
+ }
505
+
506
+ if (!TRAITS::IsDeleted (this ->m_table [this ->m_index ]))
507
+ {
508
+ if (TRAITS::s_supports_autoremove && TRAITS::ShouldDelete (this ->m_table [this ->m_index ]))
509
+ {
510
+ this ->m_hash ->RemoveElement (this ->m_table , this ->m_tableSize , &this ->m_table [this ->m_index ]);
511
+ }
512
+ else if (TRAITS::Equals (m_key, TRAITS::GetKey (this ->m_table [this ->m_index ])))
513
+ {
514
+ return ;
515
+ }
516
+ }
517
+
518
+ Next ();
450
519
}
451
520
}
452
521
@@ -466,8 +535,18 @@ class EMPTY_BASES_DECL SHash : public TRAITS
466
535
break ;
467
536
}
468
537
469
- if (!TRAITS::IsDeleted (this ->m_table [this ->m_index ])
470
- && TRAITS::Equals (m_key, TRAITS::GetKey (this ->m_table [this ->m_index ])))
538
+ if (TRAITS::IsDeleted (this ->m_table [this ->m_index ]))
539
+ {
540
+ continue ;
541
+ }
542
+
543
+ if (TRAITS::s_supports_autoremove && TRAITS::ShouldDelete (this ->m_table [this ->m_index ]))
544
+ {
545
+ this ->m_hash ->RemoveElement (this ->m_table , this ->m_tableSize , &this ->m_table [this ->m_index ]);
546
+ continue ;
547
+ }
548
+
549
+ if (TRAITS::Equals (m_key, TRAITS::GetKey (this ->m_table [this ->m_index ])))
471
550
{
472
551
break ;
473
552
}
0 commit comments