1650ca15b42400aea9d814746eb911ab1e23d3bd
[platform/upstream/coreclr.git] / src / inc / shash.h
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5
6 #ifndef _SHASH_H_
7 #define _SHASH_H_
8
9 #include "utilcode.h" // for string hash functions
10 #include "clrtypes.h"
11 #include "check.h"
12 #include "iterator.h"
13
14 // SHash is a templated closed chaining hash table of pointers.  It provides
15 // for multiple entries under the same key, and also for deleting elements.
16
17 // Synchronization:
18 // Synchronization requirements depend on use.  There are several properties to take into account:
19 //
20 // - Lookups may be asynchronous with each other
21 // - Lookups must be exclusive with Add operations 
22 //    (@todo: this can be remedied by delaying destruction of old tables during reallocation, e.g. during GC)
23 // - Remove operations may be asynchronous with Lookup/Add, unless elements are also deallocated. (In which 
24 //    case full synchronization is required)
25
26 // Common "gotchas":
27 // - The Add method never replaces an element. The new element will be added even if an element with the same
28 //   key is already present. If you don't want this, use AddOrReplace.
29 // - You need special sentinel values for the element to represent Null and Deleted. 0 and -1 are the default
30 //   choices but you will need something else if elements can legally have any of these two values.
31 // - Deriving directly from the general purpose classes (such as SHash itself) requires implementing a
32 //   TRAITS class which can be daunting. Consider using one of the specialized classes (e.g. WStringSHash) first.
33
34 // A SHash is templated by a class of TRAITS.  These traits define the various specifics of the
35 // particular hash table.
36 // The required traits are:
37 //
38 // element_t                                    Type of elements in the hash table.  These elements are stored
39 //                                              by value in the hash table. Elements must look more or less 
40 //                                              like primitives - they must support assignment relatively 
41 //                                              efficiently.  There are 2 required sentinel values: 
42 //                                              Null and Deleted (described below).  (Note that element_t is 
43 //                                              very commonly a pointer type.)  
44 //
45 //                                              The key must be derivable from the element; if your
46 //                                              table's keys are independent of the stored values, element_t
47 //                                              should be a key/value pair.
48 //                                              
49 // key_t                                        Type of the lookup key.  The key is used for identity 
50 //                                              comparison between elements, and also as a key for lookup. 
51 //                                              This is also used by value and should support 
52 //                                              efficient assignment.
53 //
54 // count_t                                      integral type for counts.  Typically inherited by default 
55 //                                              Traits (COUNT_T).
56 //
57 // static key_t GetKey(const element_t &e)      Get key from element.  Should be stable for a given e.
58 // static BOOL Equals(key_t k1, key_t k2)       Compare 2 keys for equality.  Again, should be stable.
59 // static count_t Hash(key_t k)                 Compute hash from a key.  For efficient operation, the hashes
60 //                                              for a set of elements should have random uniform distribution.
61 //
62 // static const bool s_NoThrow                  TRUE if GetKey, Equals, and hash are NOTHROW functions.  
63 //                                              Affects the THROWS clauses of several SHash functions.
64 //                                              (Note that the Null- and Deleted-related functions below 
65 //                                              are not affected by this and must always be NOTHROW.)
66 //
67 // static element_t Null()                      Return the Null sentinal value.  May be inherited from 
68 //                                              default traits if it can be assigned from 0.
69 // static element_t Deleted()                   Return the Deleted sentinal value.  May be inherited from the
70 //                                              default traits if it can be assigned from -1.
71 // static const bool IsNull(const ELEMENT &e)   Compare element with Null sentinal value. May be inherited from
72 //                                              default traits if it can be assigned from 0.
73 // static const bool IsDeleted(const ELEMENT &e) Compare element with Deleted sentinal value. May be inherited from the
74 //                                              default traits if it can be assigned from -1.
75 //
76 // static void OnDestructPerEntryCleanupAction(ELEMENT& e) Called on every element when in hashtable destructor.
77 //                                              s_DestructPerEntryCleanupAction must be set to true if implemented.
78 //
79 // s_growth_factor_numerator
80 // s_growth_factor_denominator                  Factor to grow allocation (numerator/denominator).  
81 //                                              Typically inherited from default traits (3/2)
82 //
83 // s_density_factor_numerator                   
84 // s_density_factor_denominator                 Maxium occupied density of table before growth 
85 //                                              occurs (num/denom).  Typically inherited (3/4).
86 //
87 // s_minimum_allocation                         Minimum table allocation count (size on first growth.)  It is 
88 //                                              probably preferable to call Reallocate on initialization rather
89 //                                              than override his from the default traits. (7)
90 //
91 // s_supports_remove                            Set to false for a slightly faster implementation that does not 
92 //                                              support deletes. There is a downside to the s_supports_remove flag, 
93 //                                              in that there may be more copies of the template instantiated through 
94 //                                              the system as different variants are used.
95 //
96 // s_DestructPerEntryCleanupAction              Set to true if OnDestructPerEntryCleanupAction has non-empty implementation.
97 //
98 // DefaultHashTraits provides defaults for seldomly customized values in traits classes. 
99
100 template < typename ELEMENT > 
101 class DefaultSHashTraits
102 {
103   public:
104     typedef COUNT_T count_t;
105     typedef ELEMENT element_t;
106     typedef DPTR(element_t) PTR_element_t;   // by default SHash is DAC-aware. For RS
107                                              // only SHash use NonDacAwareSHashTraits
108                                              // (which typedefs element_t* PTR_element_t)
109
110     static const COUNT_T s_growth_factor_numerator = 3;
111     static const COUNT_T s_growth_factor_denominator = 2;
112
113     static const COUNT_T s_density_factor_numerator = 3;
114     static const COUNT_T s_density_factor_denominator = 4;
115
116     static const COUNT_T s_minimum_allocation = 7;
117
118     static const bool s_supports_remove = true;
119
120     static const ELEMENT Null() { return (const ELEMENT) 0; }
121     static const ELEMENT Deleted() { return (const ELEMENT) -1; }
122     static bool IsNull(const ELEMENT &e) { return e == (const ELEMENT) 0; }
123     static bool IsDeleted(const ELEMENT &e) { return e == (const ELEMENT) -1; }
124
125     static inline void OnDestructPerEntryCleanupAction(const ELEMENT& e) { /* Do nothing */ }
126     static const bool s_DestructPerEntryCleanupAction = false;
127
128     static const bool s_NoThrow = true;
129
130     // No defaults - must specify:
131     // 
132     // typedef key_t;
133     // static key_t GetKey(const element_t &i);
134     // static BOOL Equals(key_t k1, key_t k2);
135     // static count_t Hash(key_t k);
136 };
137
138 // Hash table class definition
139
140 template <typename TRAITS>
141 class SHash : public TRAITS
142             , private noncopyable
143 {
144     friend class VerifyLayoutsMD;  // verifies class layout doesn't accidentally change
145
146   public:
147     // explicitly declare local typedefs for these traits types, otherwise 
148     // the compiler may get confused
149     typedef typename TRAITS::element_t element_t;
150     typedef typename TRAITS::PTR_element_t PTR_element_t;
151     typedef typename TRAITS::key_t key_t;
152     typedef typename TRAITS::count_t count_t;
153
154     class Index;
155     friend class Index;
156     class Iterator;
157
158     class KeyIndex;
159     friend class KeyIndex;
160     class KeyIterator;
161
162     // Constructor/destructor.  SHash tables always start out empty, with no
163     // allocation overhead.  Call Reallocate to prime with an initial size if
164     // desired.
165
166     SHash();
167
168     ~SHash();
169
170     // Lookup an element in the table by key.  Returns NULL if no element in the table
171     // has the given key.  Note that multiple entries for the same key may be stored - 
172     // this will return the first element added.  Use KeyIterator to find all elements
173     // with a given key.
174
175     element_t Lookup(key_t key) const;
176
177     // Pointer-based flavor of Lookup (allows efficient access to tables of structures)
178
179     const element_t* LookupPtr(key_t key) const;
180
181     // Add an element to the hash table.  This will never replace an element; multiple
182     // elements may be stored with the same key.
183
184     void Add(const element_t &element);
185
186     // Add a new element to the hash table, if no element with the same key is already 
187     // there. Otherwise, it will replace the existing element. This has the effect of
188     // updating an element rather than adding a duplicate. 
189     void AddOrReplace(const element_t & element);
190
191     // Remove the first element matching the key from the hash table.  
192
193     void Remove(key_t key);
194
195     // Remove the specific element.
196
197     void Remove(Iterator& i);
198     void Remove(KeyIterator& i);
199
200     // Pointer-based flavor of Remove (allows efficient access to tables of structures)
201
202     void RemovePtr(element_t * element);
203
204     // Remove all elements in the hashtable
205
206     void RemoveAll();
207
208     // Begin and End pointers for iteration over entire table. 
209
210     Iterator Begin() const;
211     Iterator End() const;
212
213     // Begin and End pointers for iteration over all elements with a given key.
214
215     KeyIterator Begin(key_t key) const;
216     KeyIterator End(key_t key) const;
217
218     // Return the number of elements currently stored in the table
219
220     count_t GetCount() const; 
221
222     // Resizes a hash table for growth.  The new size is computed based
223     // on the current population, growth factor, and maximum density factor.
224
225     void Grow();
226
227     // Reallocates a hash table to a specific size.  The size must be big enough
228     // to hold all elements in the table appropriately.  
229     //
230     // Note that the actual table size must always be a prime number; the number
231     // passed in will be upward adjusted if necessary.
232
233     void Reallocate(count_t newTableSize);
234
235     // Makes a call on the Functor for each element in the hash table, passing
236     // the element as an argument. Functor is expected to look like this:
237     //
238     // class Functor
239     // {
240     //     public:
241     //     void operator() (element_t &element) { ... }
242     // }
243
244     template<typename Functor> void ForEach(Functor &functor);
245
246   private:
247
248     // See if it is OK to grow the hash table by one element.  If not, reallocate
249     // the hash table.
250     BOOL CheckGrowth();
251     
252     // See if it is OK to grow the hash table by one element. If not, allocate new 
253     // hash table and return it together with its size *pcNewSize (used by code:AddPhases).
254     // Returns NULL if there already is space for one element.
255     element_t * CheckGrowth_OnlyAllocateNewTable(count_t * pcNewSize);
256     
257     // Allocates new resized hash table for growth. Does not update the hash table on the object.
258     // The new size is computed based on the current population, growth factor, and maximum density factor.
259     element_t * Grow_OnlyAllocateNewTable(count_t * pcNewSize);
260     
261     // Utility function to allocate new table (does not copy the values into it yet). Returns the size of new table in 
262     // *pcNewTableSize (finds next prime).
263     // Phase 1 of code:Reallocate - it is split to support code:AddPhases.
264     element_t * AllocateNewTable(count_t requestedSize, count_t * pcNewTableSize);
265     
266     // Utility function to replace old table with newly allocated table (as allocated by 
267     // code:AllocateNewTable). Copies all 'old' values into the new table first.
268     // Returns the old table. Caller is expected to delete it (via code:DeleteOldTable).
269     // Phase 2 of code:Reallocate - it is split to support code:AddPhases.
270     element_t * ReplaceTable(element_t * newTable, count_t newTableSize);
271     
272     // Utility function to delete old table (as returned by code:ReplaceTable).
273     // Phase 3 of code:Reallocate - it is split to support code:AddPhases.
274     void DeleteOldTable(element_t * oldTable);
275     
276     // Utility function that does not call code:CheckGrowth.
277     // Add an element to the hash table.  This will never replace an element; multiple 
278     // elements may be stored with the same key.
279     void Add_GrowthChecked(const element_t & element);
280     
281     // Utility function to add a new element to the hash table.  Note that
282     // it is perfectly fine for the element to be a duplicate - if so it
283     // is added an additional time. Returns TRUE if a new empty spot was used;
284     // FALSE if an existing deleted slot.
285     static BOOL Add(element_t *table, count_t tableSize, const element_t &element);
286
287     // Utility function to add a new element to the hash table, if no element with the same key
288     // is already there. Otherwise, it will replace the existing element. This has the effect of
289     // updating an element rather than adding a duplicate. 
290     void AddOrReplace(element_t *table, count_t tableSize, const element_t &element);
291  
292     // Utility function to find the first element with the given key in 
293     // the hash table.
294
295     static const element_t* Lookup(PTR_element_t table, count_t tableSize, key_t key);
296
297     // Utility function to remove the first element with the given key
298     // in the hash table.
299
300     void Remove(element_t *table, count_t tableSize, key_t key);
301
302     // Utility function to remove the specific element.
303
304     void RemoveElement(element_t *table, count_t tableSize, element_t *element);
305
306     // Index for whole table iterator.  This is also the base for the keyed iterator.
307
308   public:
309
310     class Index
311 #ifdef _DEBUG
312         // CheckedIteratorBase is a no-op in RET builds. having it as an empty base-class
313         // causes differences in the sizeof(SHash::Iterator) in DAC vs. non-DAC builds.
314         // avoid the issue by not specifying it as a base class in RET builds
315         : public CheckedIteratorBase< SHash<TRAITS> >
316 #endif
317     {
318         friend class SHash;
319         friend class Iterator;
320         friend class Enumerator<const element_t, Iterator>;
321
322         // The methods implementation has to be here for portability
323         // Some compilers won't compile the separate implementation in shash.inl
324       protected:
325
326         PTR_element_t m_table;
327         count_t m_tableSize;
328         count_t m_index;
329
330
331         Index(const SHash *hash, BOOL begin)
332         : m_table(hash->m_table),
333             m_tableSize(hash->m_tableSize),
334             m_index(begin ? 0 : m_tableSize)
335         {
336             LIMITED_METHOD_CONTRACT;
337         }
338
339         const element_t &Get() const
340         {
341             LIMITED_METHOD_CONTRACT;
342
343             return m_table[m_index];
344         }
345
346         void First()
347         {
348             LIMITED_METHOD_CONTRACT;
349
350             if (m_index < m_tableSize)
351                 if (TRAITS::IsNull(m_table[m_index]) || TRAITS::IsDeleted(m_table[m_index]))
352                     Next();
353         }
354
355         void Next()
356         {
357             LIMITED_METHOD_CONTRACT;
358
359             if (m_index >= m_tableSize)
360                 return;
361             
362             for (;;)
363             {
364                 m_index++;
365                 if (m_index >= m_tableSize)
366                     break;
367                 if (!TRAITS::IsNull(m_table[m_index]) && !TRAITS::IsDeleted(m_table[m_index]))
368                     break;
369             }
370         }
371
372         bool Equal(const Index &i) const
373         { 
374             LIMITED_METHOD_CONTRACT;
375
376             return i.m_index == m_index; 
377         }
378
379         CHECK DoCheck() const
380         {
381             CHECK_OK;
382         }
383     };
384
385     class Iterator : public Index, public Enumerator<const element_t, Iterator>
386     {
387         friend class SHash;
388
389       public:
390         Iterator(const SHash *hash, BOOL begin)
391           : Index(hash, begin)
392         {
393         }
394     };
395
396     // Index for iterating elements with a given key.  
397     //
398     // Note that the m_index field
399     // is artificially bumped to m_tableSize when the end of iteration is reached.
400     // This allows a canonical End iterator to be used.
401
402     class KeyIndex : public Index
403     {
404         friend class SHash;
405         friend class KeyIterator;
406         friend class Enumerator<const element_t, KeyIterator>;
407
408         // The methods implementation has to be here for portability
409         // Some compilers won't compile the separate implementation in shash.inl
410       protected:
411         key_t       m_key;
412         count_t     m_increment;
413
414         KeyIndex(const SHash *hash, BOOL begin)
415         : Index(hash, begin),
416             m_increment(0)
417         {
418             LIMITED_METHOD_CONTRACT;
419         }
420
421         void SetKey(key_t key)
422         {
423             LIMITED_METHOD_CONTRACT;
424
425             if (Index::m_tableSize > 0)
426             {
427                 m_key = key;
428                 count_t hash = TRAITS::Hash(key);
429
430                 this->m_index = hash % this->m_tableSize;
431                 m_increment = (hash % (this->m_tableSize-1)) + 1;
432
433                 // Find first valid element
434                 if (TRAITS::IsNull(this->m_table[this->m_index]))
435                     this->m_index = this->m_tableSize;
436                 else if (TRAITS::IsDeleted(this->m_table[this->m_index])
437                         || !TRAITS::Equals(m_key, TRAITS::GetKey(this->m_table[this->m_index])))
438                     Next();
439             }
440         }
441
442         void Next()
443         {
444             LIMITED_METHOD_CONTRACT;
445
446             while (TRUE)
447             {
448                 this->m_index += m_increment;
449                 if (this->m_index >= this->m_tableSize)
450                     this->m_index -= this->m_tableSize;
451
452                 if (TRAITS::IsNull(this->m_table[this->m_index]))
453                 {
454                     this->m_index = this->m_tableSize;
455                     break;
456                 }
457
458                 if (!TRAITS::IsDeleted(this->m_table[this->m_index])
459                         && TRAITS::Equals(m_key, TRAITS::GetKey(this->m_table[this->m_index])))
460                 {
461                     break;
462                 }
463             }
464         }
465     };
466
467     class KeyIterator : public KeyIndex, public Enumerator<const element_t, KeyIterator>
468     {
469         friend class SHash;
470
471       public:
472
473         operator Iterator &()
474         {
475             return *(Iterator*)this;
476         }
477
478         operator const Iterator &()
479         {
480             return *(const Iterator*)this;
481         }
482
483         KeyIterator(const SHash *hash, BOOL begin)
484           : KeyIndex(hash, begin)
485         {
486         }
487     };
488
489     // Wrapper and holder for adding an element to the hash table. Useful for Add operations that have to happen 
490     // under a rare lock that does not allow call out into host.
491     // There are 3 phases:
492     //    1. code:PreallocateForAdd ... Can allocate memory (calls into host).
493     //    2. code:Add ... Adds one element (does NOT call into host).
494     //       or code:AddNothing_PublishPreallocatedTable ... Publishes the pre-allocated memory from step #1 (if any).
495     //    3. code:DeleteOldTable (or destructor) ... Can delete the old memory (calls into host).
496     // Example:
497     //      CrstHolder lockAdd(&crstLockForAdd); // Serialize all Add operations.
498     //      HostAssemblyMap::AddPhases addCall;
499     //      addCall.PreallocateForAdd(&shash); // 1. Allocates memory for one Add call (if needed). addCall serves as holder for the allocated memory.
500     //      {
501     //          // We cannot call out into host from ForbidSuspend region (i.e. no allocations/deallocations).
502     //          ForbidSuspendThreadHolder suspend; // Required by the 'special' read-lock
503     //          {
504     //              CrstHolder lock(&crstLock);
505     //              if (some_condition)
506     //              {   // 2a. Add item. This may replace SHash inner table with the one pre-allocated in step 1.
507     //                  addCall.Add(shashItem);
508     //              }
509     //              else
510     //              {   // 2b. Skip adding item. This may replace SHash inner table with the one pre-allocated in step 1.
511     //                  addCall.AddNothing_PublishPreallocatedTable();
512     //              }
513     //          }
514     //      }
515     //      addCall.DeleteOldTable(); // 3. Cleanup old table memory from shash (if it was replaced by pre-allocated table in step 2).
516     //                                // Note: addCall destructor would take care of deleting the memory as well.
517     class AddPhases
518     {
519     public:
520         AddPhases();
521         ~AddPhases();
522         
523         // Prepares object for one call to code:Add. Pre-allocates new table memory if needed, does not publish 
524         // the table yet (it is kept ready only in this holder for call to code:Add).
525         // Calls out into host.
526         void PreallocateForAdd(SHash * pHash);
527         
528         // Add an element to the hash table. This will never replace an element; multiple elements may be stored 
529         // with the same key.
530         // Will use/publish pre-allocated memory from code:PreallocateForAdd.
531         // Does not call out into host.
532         // Only one Add* method can be called once per object! (Create a new object for each call)
533         void Add(const element_t & element);
534         
535         // Element will not be added to the hash table.
536         // Will use/publish pre-allocated memory from code:PreallocateForAdd.
537         // Does not call out into host.
538         // Only one Add* method can be called once per object! (Create a new object for each call)
539         void AddNothing_PublishPreallocatedTable();
540         
541         // Deletes old table if it was replaced by call to code:Add or code:AddNothing_PublishPreallocatedTable.
542         // Calls out into host.
543         void DeleteOldTable();
544         
545     private:
546         SHash *     m_pHash;
547         element_t * m_newTable;
548         count_t     m_newTableSize;
549         element_t * m_oldTable;
550         
551     #ifdef _DEBUG
552         PTR_element_t dbg_m_table;
553         count_t       dbg_m_tableSize;
554         count_t       dbg_m_tableCount;
555         count_t       dbg_m_tableOccupied;
556         count_t       dbg_m_tableMax;
557         BOOL          dbg_m_fAddCalled;
558     #endif //_DEBUG
559     };  // class SHash::AddPhases
560
561     // Adds an entry to the hash table according to the guidelines above for
562     // avoiding a callout to the host while the read lock is held.
563     // Returns true if elem was added to the map, otherwise false.
564     // When elem was not added (false is returned), and if ppStoredElem is non-null,
565     // then it is set to point to the value in the map.
566     template <typename LockHolderT,
567               typename AddLockHolderT,
568               typename LockT,
569               typename AddLockT>
570     bool CheckAddInPhases(
571         element_t const & elem,
572         LockT & lock,
573         AddLockT & addLock,
574         IUnknown * addRefObject = nullptr);
575         
576   private:
577     
578     // Test for prime number.
579     static BOOL IsPrime(COUNT_T number);
580
581     // Find the next prime number >= the given value.  
582
583     static COUNT_T NextPrime(COUNT_T number);
584
585     // Instance members
586
587     PTR_element_t m_table;                // pointer to table
588     count_t       m_tableSize;            // allocated size of table
589     count_t       m_tableCount;           // number of elements in table
590     count_t       m_tableOccupied;        // number, includes deleted slots
591     count_t       m_tableMax;             // maximum occupied count before reallocating
592 };  // class SHash
593
594 // disables support for DAC marshaling. Useful for defining right-side only SHashes
595 template <typename PARENT>
596 class NonDacAwareSHashTraits : public PARENT
597 {
598 public:
599     typedef typename PARENT::element_t element_t;
600     typedef element_t * PTR_element_t;
601 };
602
603 // disables support for removing elements - produces slightly faster implementation
604
605 template <typename PARENT>
606 class NoRemoveSHashTraits : public PARENT
607 {
608 public:
609     // explicitly declare local typedefs for these traits types, otherwise 
610     // the compiler may get confused
611     typedef typename PARENT::element_t element_t;
612     typedef typename PARENT::count_t count_t;
613
614     static const bool s_supports_remove = false;
615     static const element_t Deleted() { UNREACHABLE(); }
616     static bool IsDeleted(const element_t &e) { LIMITED_METHOD_DAC_CONTRACT; return false; }
617 };
618
619 // PtrHashTraits is a template to provides useful defaults for pointer hash tables
620 // It relies on methods GetKey and Hash defined on ELEMENT
621
622 template <typename ELEMENT, typename KEY> 
623 class PtrSHashTraits : public DefaultSHashTraits<ELEMENT *>
624 {
625   public:
626
627     // explicitly declare local typedefs for these traits types, otherwise 
628     // the compiler may get confused
629     typedef DefaultSHashTraits<ELEMENT *> PARENT;
630     typedef typename PARENT::element_t element_t;
631     typedef typename PARENT::count_t count_t;
632
633     typedef KEY key_t;
634
635     static key_t GetKey(const element_t &e) 
636     { 
637         WRAPPER_NO_CONTRACT;
638         return e->GetKey(); 
639     }
640     static BOOL Equals(key_t k1, key_t k2) 
641     { 
642         LIMITED_METHOD_CONTRACT;
643         return k1 == k2; 
644     }
645     static count_t Hash(key_t k)
646     { 
647         WRAPPER_NO_CONTRACT;
648         return ELEMENT::Hash(k);
649     }
650 };
651
652 template <typename ELEMENT, typename KEY>
653 class PtrSHash : public SHash< PtrSHashTraits<ELEMENT, KEY> >
654 {
655 };
656
657 template <typename ELEMENT, typename KEY> 
658 class PtrSHashWithCleanupTraits
659     : public PtrSHashTraits<ELEMENT, KEY>
660 {
661 public:
662     void OnDestructPerEntryCleanupAction(ELEMENT * elem)
663     {
664         delete elem;
665     }
666     static const bool s_DestructPerEntryCleanupAction = true;
667 };
668
669 // a class that automatically deletes data referenced by the pointers (so effectively it takes ownership of the data)
670 // since I was too lazy to implement Remove() APIs properly, removing entries is disallowed
671 template <typename ELEMENT, typename KEY>
672 class PtrSHashWithCleanup : public SHash< NoRemoveSHashTraits< PtrSHashWithCleanupTraits<ELEMENT, KEY> > >
673 {
674 };
675
676 // Provides case-sensitive comparison and hashing functionality through static
677 // and functor object methods. Can be instantiated with CHAR or WCHAR.
678 template <typename CharT>
679 struct CaseSensitiveStringCompareHash
680 {
681 private:
682     typedef CharT const * str_t;
683
684     static size_t _strcmp(CHAR const *left, CHAR const *right)
685     {
686         return ::strcmp(left, right);
687     }
688
689     static size_t _strcmp(WCHAR const *left, WCHAR const *right)
690     {
691         return ::wcscmp(left, right);
692     }
693
694     static size_t _hash(CHAR const *str)
695     {
696         return HashStringA(str);
697     }
698     
699     static size_t _hash(WCHAR const *str)
700     {
701         return HashString(str);
702     }
703
704 public:
705     static size_t compare(str_t left, str_t right)
706     {
707         return _strcmp(left, right);
708     }
709
710     size_t operator()(str_t left, str_t right)
711     {
712         return compare(left, right);
713     }
714
715     static size_t hash(str_t str)
716     {
717         return _hash(str);
718     }
719
720     size_t operator()(str_t str)
721     {
722         return hash(str);
723     }
724 };
725
726 // Provides case-insensitive comparison and hashing functionality through static
727 // and functor object methods. Can be instantiated with CHAR or WCHAR.
728 template <typename CharT>
729 struct CaseInsensitiveStringCompareHash
730 {
731 private:
732     typedef CharT const * str_t;
733
734     static size_t _strcmp(str_t left, str_t right)
735     {
736         return ::SString::_tstricmp(left, right);
737     }
738
739     static size_t _hash(CHAR const *str)
740     {
741         return HashiStringA(str);
742     }
743     
744     static size_t _hash(WCHAR const *str)
745     {
746         return HashiString(str);
747     }
748
749 public:
750     static size_t compare(str_t left, str_t right)
751     {
752         return _strcmp(left, right);
753     }
754
755     size_t operator()(str_t left, str_t right)
756     {
757         return compare(left, right);
758     }
759
760     static size_t hash(str_t str)
761     {
762         return _hash(str);
763     }
764
765     size_t operator()(str_t str)
766     {
767         return hash(str);
768     }
769 };
770
771 // StringSHashTraits is a traits class useful for string-keyed
772 // pointer hash tables. 
773
774 template <typename ElementT, typename CharT, typename ComparerT = CaseSensitiveStringCompareHash<CharT> >
775 class StringSHashTraits : public PtrSHashTraits<ElementT, CharT const *>
776 {
777 public:
778     // explicitly declare local typedefs for these traits types, otherwise 
779     // the compiler may get confused
780     typedef PtrSHashTraits<ElementT, CharT const *> PARENT;
781     typedef typename PARENT::element_t element_t;
782     typedef typename PARENT::key_t key_t;
783     typedef typename PARENT::count_t count_t;
784
785     static BOOL Equals(key_t k1, key_t k2) 
786     { 
787         LIMITED_METHOD_CONTRACT;
788
789         if (k1 == NULL && k2 == NULL)
790             return TRUE;
791         if (k1 == NULL || k2 == NULL)
792             return FALSE;
793         return ComparerT::compare(k1, k2) == 0; 
794     }
795     static count_t Hash(key_t k) 
796     { 
797         LIMITED_METHOD_CONTRACT;
798
799         if (k == NULL)
800             return 0;
801         else
802             return (count_t)ComparerT::hash(k); 
803     }
804 };
805
806 template <typename COMINTERFACE, typename CharT> // Could use IUnknown but would rather take advantage of C++ type checking
807 struct StringHashElement
808 {
809     const CharT *GetKey()
810     {
811         return String;
812     }
813
814     COMINTERFACE *Object;
815     CharT *String;
816 };
817
818 template <typename COMINTERFACE, typename CharT, typename ComparerT = CaseSensitiveStringCompareHash<CharT> >
819 class StringHashWithCleanupTraits : public StringSHashTraits<StringHashElement<COMINTERFACE, CharT>, CharT, ComparerT>
820 {
821 public:
822     void OnDestructPerEntryCleanupAction(StringHashElement<COMINTERFACE, CharT> * e)
823     {
824         if (e->String != NULL)
825         {
826             delete[] e->String;
827         }
828         
829         if (e->Object != NULL)
830         {
831             e->Object->Release();
832         }
833     }
834     static const bool s_DestructPerEntryCleanupAction = true;
835 };
836
837 template <typename COMINTERFACE, typename CharT, typename ComparerT = CaseSensitiveStringCompareHash<CharT> >
838 class StringSHashWithCleanup : public SHash< StringHashWithCleanupTraits<COMINTERFACE, CharT, ComparerT> >
839 {
840 };
841
842 template <typename ELEMENT>
843 class StringSHash : public SHash< StringSHashTraits<ELEMENT, CHAR> >
844 {
845 };
846
847 template <typename ELEMENT>
848 class WStringSHash : public SHash< StringSHashTraits<ELEMENT, WCHAR> >
849 {
850 };
851
852 template <typename ELEMENT> 
853 class SStringSHashTraits : public PtrSHashTraits<ELEMENT, SString>
854 {
855   public:
856     typedef PtrSHashTraits<ELEMENT, SString> PARENT;
857     typedef typename PARENT::element_t element_t;
858     typedef typename PARENT::key_t key_t;
859     typedef typename PARENT::count_t count_t;
860
861     static const bool s_NoThrow = false;
862
863     static BOOL Equals(const key_t &k1, const key_t &k2) 
864     { 
865         WRAPPER_NO_CONTRACT;
866         return k1.Equals(k2);
867     }
868     static count_t Hash(const key_t &k) 
869     { 
870         WRAPPER_NO_CONTRACT;
871         return k.Hash();
872     }
873 };
874
875 template <typename ELEMENT>
876 class SStringSHash : public SHash< SStringSHashTraits<ELEMENT> >
877 {
878 };
879
880 template <typename ELEMENT>
881 class SetSHashTraits : public DefaultSHashTraits<ELEMENT>
882 {
883 public:
884     // explicitly declare local typedefs for these traits types, otherwise 
885     // the compiler may get confused
886     typedef typename DefaultSHashTraits<ELEMENT>::element_t element_t;
887     typedef typename DefaultSHashTraits<ELEMENT>::count_t count_t;
888
889     typedef ELEMENT key_t;
890
891     static key_t GetKey(element_t e)
892     {
893         LIMITED_METHOD_CONTRACT;
894         return e;
895     }
896     static BOOL Equals(key_t k1, key_t k2)
897     {
898         LIMITED_METHOD_CONTRACT;
899         return k1 == k2;
900     }
901     static count_t Hash(key_t k)
902     {
903         LIMITED_METHOD_CONTRACT;
904         return (count_t)(size_t)k;
905     }
906 };
907
908 template <typename ELEMENT, typename TRAITS = NoRemoveSHashTraits< SetSHashTraits <ELEMENT> > >
909 class SetSHash : public SHash< TRAITS >
910 {
911     typedef SHash<TRAITS> PARENT;
912
913 public:
914     BOOL Contains(ELEMENT key) const
915     {
916         return PARENT::LookupPtr(key) != NULL;
917     }
918 };
919
920 template <typename ELEMENT> 
921 class PtrSetSHashTraits : public SetSHashTraits<ELEMENT>
922 {
923   public:
924
925     // explicitly declare local typedefs for these traits types, otherwise 
926     // the compiler may get confused
927     typedef SetSHashTraits<ELEMENT> PARENT;
928     typedef typename PARENT::element_t element_t;
929     typedef typename PARENT::key_t key_t;
930     typedef typename PARENT::count_t count_t;
931
932     static count_t Hash(key_t k) 
933     { 
934         WRAPPER_NO_CONTRACT;
935         return (count_t)(size_t)k >> 2;
936     }
937 };
938
939 template <typename PARENT_TRAITS>
940 class DeleteElementsOnDestructSHashTraits : public PARENT_TRAITS
941 {
942 public:
943     static inline void OnDestructPerEntryCleanupAction(typename PARENT_TRAITS::element_t e)
944     {
945         delete e;
946     }
947     static const bool s_DestructPerEntryCleanupAction = true;
948 };
949
950 #if !defined(CC_JIT) // workaround: Key is redefined in JIT64
951
952 template <typename KEY, typename VALUE>
953 class KeyValuePair {
954     KEY     key;
955     VALUE   value;
956
957 public:
958     KeyValuePair()
959     {
960     }
961
962     KeyValuePair(const KEY& k, const VALUE& v)
963         : key(k), value(v)
964     {
965     }
966
967     KEY const & Key() const
968     {
969         LIMITED_METHOD_CONTRACT;
970         return key;
971     }
972
973     VALUE const & Value() const
974     {
975         LIMITED_METHOD_CONTRACT;
976         return value;
977     }
978 };
979
980 template <typename KEY, typename VALUE>
981 class MapSHashTraits : public DefaultSHashTraits< KeyValuePair<KEY,VALUE> >
982 {
983 public:
984     // explicitly declare local typedefs for these traits types, otherwise 
985     // the compiler may get confused
986     typedef typename DefaultSHashTraits< KeyValuePair<KEY,VALUE> >::element_t element_t;
987     typedef typename DefaultSHashTraits< KeyValuePair<KEY,VALUE> >::count_t count_t;
988
989     typedef KEY key_t;
990
991     static key_t GetKey(element_t e)
992     {
993         LIMITED_METHOD_CONTRACT;
994         return e.Key();
995     }
996     static BOOL Equals(key_t k1, key_t k2)
997     {
998         LIMITED_METHOD_CONTRACT;
999         return k1 == k2;
1000     }
1001     static count_t Hash(key_t k)
1002     {
1003         LIMITED_METHOD_CONTRACT;
1004         return (count_t)(size_t)k;
1005     }
1006
1007     static const element_t Null() { LIMITED_METHOD_CONTRACT; return element_t(KEY(),VALUE()); }
1008     static const element_t Deleted() { LIMITED_METHOD_CONTRACT; return element_t(KEY(-1), VALUE()); }
1009     static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e.Key() == KEY(); }
1010     static bool IsDeleted(const element_t &e) { return e.Key() == KEY(-1); }
1011 };
1012
1013 template <typename KEY, typename VALUE, typename TRAITS = NoRemoveSHashTraits< MapSHashTraits <KEY, VALUE> > >
1014 class MapSHash : public SHash< TRAITS >
1015 {
1016     typedef SHash< TRAITS > PARENT;
1017
1018 public:
1019     void Add(KEY key, VALUE value)
1020     {
1021         CONTRACTL
1022         {
1023             THROWS;
1024             GC_NOTRIGGER;
1025             PRECONDITION(key != (KEY)0);
1026         }
1027         CONTRACTL_END;
1028
1029         PARENT::Add(KeyValuePair<KEY,VALUE>(key, value));
1030     }
1031
1032     BOOL Lookup(KEY key, VALUE* pValue) const
1033     {
1034         CONTRACTL
1035         {
1036             NOTHROW;
1037             GC_NOTRIGGER;
1038             PRECONDITION(key != (KEY)0);
1039         }
1040         CONTRACTL_END;
1041
1042         const KeyValuePair<KEY,VALUE> *pRet = PARENT::LookupPtr(key);
1043         if (pRet == NULL)
1044             return FALSE;
1045
1046         *pValue = pRet->Value();
1047         return TRUE;
1048     }
1049 };
1050
1051 template <typename KEY, typename VALUE>
1052 class MapSHashWithRemove : public SHash< MapSHashTraits <KEY, VALUE> >
1053 {
1054     typedef SHash< MapSHashTraits <KEY, VALUE> > PARENT;
1055
1056 public:
1057     void Add(KEY key, VALUE value)
1058     {
1059         CONTRACTL
1060         {
1061             THROWS;
1062             GC_NOTRIGGER;
1063             PRECONDITION(key != (KEY)0 && key != (KEY)-1);
1064         }
1065         CONTRACTL_END;
1066
1067         PARENT::Add(KeyValuePair<KEY,VALUE>(key, value));
1068     }
1069
1070     BOOL Lookup(KEY key, VALUE* pValue) const
1071     {
1072         CONTRACTL
1073         {
1074             NOTHROW;
1075             GC_NOTRIGGER;
1076             PRECONDITION(key != (KEY)0 && key != (KEY)-1);
1077         }
1078         CONTRACTL_END;
1079
1080         const KeyValuePair<KEY,VALUE> *pRet = PARENT::LookupPtr(key);
1081         if (pRet == NULL)
1082             return FALSE;
1083
1084         *pValue = pRet->Value();
1085         return TRUE;
1086     }
1087
1088     void Remove(KEY key)
1089     {
1090         CONTRACTL
1091         {
1092             NOTHROW;
1093             GC_NOTRIGGER;
1094             PRECONDITION(key != (KEY)0 && key != (KEY)-1);
1095         }
1096         CONTRACTL_END;
1097
1098         PARENT::Remove(key);
1099     }
1100 };
1101
1102 #endif // CC_JIT
1103
1104 #include "shash.inl"
1105
1106 #endif // _SHASH_H_