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 | /*++--------------------------------------------------------------------------------------- |
7 | |
8 | Module Name: |
9 | |
10 | hash.h |
11 | |
12 | Abstract: |
13 | |
14 | Fast hash table classes, |
15 | --*/ |
16 | |
17 | #ifndef _HASH_H_ |
18 | #define _HASH_H_ |
19 | |
20 | #ifndef ASSERT |
21 | #define ASSERT _ASSERTE |
22 | #endif |
23 | |
24 | |
25 | #include "crst.h" |
26 | |
27 | // #define HASHTABLE_PROFILE |
28 | |
29 | // Track collision chains of up to length X |
30 | const unsigned int HASHTABLE_LOOKUP_PROBES_DATA = 20; |
31 | |
32 | //------------------------------------------------------- |
33 | // enums for special Key values used in hash table |
34 | // |
35 | enum |
36 | { |
37 | EMPTY = 0, |
38 | DELETED = 1, |
39 | INVALIDENTRY = ~0 |
40 | }; |
41 | |
42 | typedef ULONG_PTR UPTR; |
43 | |
44 | //------------------------------------------------------------------------------ |
45 | // classes in use |
46 | //------------------------------------------------------------------------------ |
47 | class Bucket; |
48 | class HashMap; |
49 | |
50 | //------------------------------------------------------- |
51 | // class Bucket |
52 | // used by hash table implementation |
53 | // |
54 | typedef DPTR(class Bucket) PTR_Bucket; |
55 | class Bucket |
56 | { |
57 | public: |
58 | UPTR m_rgKeys[4]; |
59 | UPTR m_rgValues[4]; |
60 | |
61 | #define VALUE_MASK (sizeof(LPVOID) == 4 ? 0x7FFFFFFF : I64(0x7FFFFFFFFFFFFFFF)) |
62 | |
63 | void SetValue (UPTR value, UPTR i) |
64 | { |
65 | LIMITED_METHOD_CONTRACT; |
66 | |
67 | ASSERT(value <= VALUE_MASK); |
68 | m_rgValues[i] = (UPTR) ((m_rgValues[i] & ~VALUE_MASK) | value); |
69 | } |
70 | |
71 | UPTR GetValue (UPTR i) |
72 | { |
73 | LIMITED_METHOD_DAC_CONTRACT; |
74 | |
75 | return (UPTR)(m_rgValues[i] & VALUE_MASK); |
76 | } |
77 | |
78 | UPTR IsCollision() // useful sentinel for fast fail of lookups |
79 | { |
80 | LIMITED_METHOD_CONTRACT; |
81 | |
82 | return (UPTR) (m_rgValues[0] & ~VALUE_MASK); |
83 | } |
84 | |
85 | void SetCollision() |
86 | { |
87 | LIMITED_METHOD_CONTRACT; |
88 | |
89 | m_rgValues[0] |= ~VALUE_MASK; // set collision bit |
90 | m_rgValues[1] &= VALUE_MASK; // reset has free slots bit |
91 | } |
92 | |
93 | BOOL HasFreeSlots() |
94 | { |
95 | WRAPPER_NO_CONTRACT; |
96 | |
97 | // check for free slots available in the bucket |
98 | // either there is no collision or a free slot has been during |
99 | // compaction |
100 | return (!IsCollision() || (m_rgValues[1] & ~VALUE_MASK)); |
101 | } |
102 | |
103 | void SetFreeSlots() |
104 | { |
105 | LIMITED_METHOD_CONTRACT; |
106 | |
107 | m_rgValues[1] |= ~VALUE_MASK; // set has free slots bit |
108 | } |
109 | |
110 | BOOL InsertValue(const UPTR key, const UPTR value); |
111 | }; |
112 | |
113 | |
114 | //------------------------------------------------------------------------------ |
115 | // bool (*CompareFnPtr)(UPTR,UPTR); pointer to a function that takes 2 UPTRs |
116 | // and returns a boolean, provide a function with this signature to the HashTable |
117 | // to use for comparing Values during lookup |
118 | //------------------------------------------------------------------------------ |
119 | typedef BOOL (*CompareFnPtr)(UPTR,UPTR); |
120 | |
121 | class Compare |
122 | { |
123 | protected: |
124 | Compare() |
125 | { |
126 | LIMITED_METHOD_CONTRACT; |
127 | |
128 | m_ptr = NULL; |
129 | } |
130 | public: |
131 | CompareFnPtr m_ptr; |
132 | |
133 | Compare(CompareFnPtr ptr) |
134 | { |
135 | LIMITED_METHOD_CONTRACT; |
136 | |
137 | _ASSERTE(ptr != NULL); |
138 | m_ptr = ptr; |
139 | } |
140 | |
141 | virtual ~Compare() |
142 | { |
143 | LIMITED_METHOD_CONTRACT; |
144 | } |
145 | |
146 | virtual UPTR CompareHelper(UPTR val1, UPTR storedval) |
147 | { |
148 | WRAPPER_NO_CONTRACT; |
149 | |
150 | #ifndef _DEBUG |
151 | CONTRACTL |
152 | { |
153 | DISABLED(THROWS); // This is not a bug, we cannot decide, since the function ptr called may be either. |
154 | DISABLED(GC_NOTRIGGER); // This is not a bug, we cannot decide, since the function ptr called may be either. |
155 | } |
156 | CONTRACTL_END; |
157 | #endif // !_DEBUG |
158 | |
159 | return (*m_ptr)(val1,storedval); |
160 | } |
161 | }; |
162 | |
163 | class ComparePtr : public Compare |
164 | { |
165 | public: |
166 | ComparePtr (CompareFnPtr ptr) |
167 | { |
168 | LIMITED_METHOD_CONTRACT; |
169 | |
170 | _ASSERTE(ptr != NULL); |
171 | m_ptr = ptr; |
172 | } |
173 | |
174 | virtual UPTR CompareHelper(UPTR val1, UPTR storedval) |
175 | { |
176 | WRAPPER_NO_CONTRACT; |
177 | |
178 | #ifndef _DEBUG |
179 | CONTRACTL |
180 | { |
181 | DISABLED(THROWS); // This is not a bug, we cannot decide, since the function ptr called may be either. |
182 | DISABLED(GC_NOTRIGGER); // This is not a bug, we cannot decide, since the function ptr called may be either. |
183 | } |
184 | CONTRACTL_END; |
185 | #endif // !_DEBUG |
186 | |
187 | storedval <<=1; |
188 | return (*m_ptr)(val1,storedval); |
189 | } |
190 | }; |
191 | |
192 | //------------------------------------------------------------------------------ |
193 | // Class HashMap |
194 | // Fast Hash table, for concurrent use, |
195 | // stores a 4 byte Key and a 4 byte Value for each slot. |
196 | // Duplicate keys are allowed, (keys are compared as 4 byte UPTRs) |
197 | // Duplicate values are allowed,(values are compared using comparison fn. provided) |
198 | // but if no comparison function is provided then the values should be unique |
199 | // |
200 | // Lookup's don't require to take locks, unless you specify fAsyncMode. |
201 | // Insert and Delete operations require locks |
202 | // Inserting a duplicate value will assert in DEBUG mode, the PROPER way to perform inserts |
203 | // is to take a lock, do a lookup and if the lookup fails then Insert |
204 | // |
205 | // In async mode, deleted slots are not immediately reclaimed (until a rehash), and |
206 | // accesses to the hash table cause a transition to cooperative GC mode, and reclamation of old |
207 | // hash maps (after a rehash) are deferred until GC time. |
208 | // In sync mode, none of this is necessary; however calls to LookupValue must be synchronized as well. |
209 | // |
210 | // Algorithm: |
211 | // The Hash table is an array of buckets, each bucket can contain 4 key/value pairs |
212 | // Special key values are used to identify EMPTY and DELETED slots |
213 | // Hash function uses the current size of the hash table and a SEED based on the key |
214 | // to choose the bucket, seed starts of being the key and gets refined every time |
215 | // the hash function is re-applied. |
216 | // |
217 | // Inserts choose an empty slot in the current bucket for new entries, if the current bucket |
218 | // is full, then the seed is refined and a new bucket is choosen, if an empty slot is not found |
219 | // after 8 retries, the hash table is expanded, this causes the current array of buckets to |
220 | // be put in a free list and a new array of buckets is allocated and all non-deleted entries |
221 | // from the old hash table are rehashed to the new array |
222 | // The old arrays are reclaimed during Compact phase, which should only be called during GC or |
223 | // any other time it is guaranteed that no Lookups are taking place. |
224 | // Concurrent Insert and Delete operations need to be serialized |
225 | // |
226 | // Delete operations, mark the Key in the slot as DELETED, the value is not removed and inserts |
227 | // don't reuse these slots, they get reclaimed during expansion and compact phases. |
228 | // |
229 | //------------------------------------------------------------------------------ |
230 | |
231 | class HashMap |
232 | { |
233 | public: |
234 | |
235 | //@constructor |
236 | HashMap() DAC_EMPTY(); |
237 | //destructor |
238 | ~HashMap() DAC_EMPTY(); |
239 | |
240 | // Init |
241 | void Init(BOOL fAsyncMode, LockOwner *pLock) |
242 | { |
243 | WRAPPER_NO_CONTRACT; |
244 | |
245 | Init(0, (Compare *)NULL,fAsyncMode, pLock); |
246 | } |
247 | // Init |
248 | void Init(DWORD cbInitialSize, BOOL fAsyncMode, LockOwner *pLock) |
249 | { |
250 | WRAPPER_NO_CONTRACT; |
251 | |
252 | Init(cbInitialSize, (Compare*)NULL, fAsyncMode, pLock); |
253 | } |
254 | // Init |
255 | void Init(CompareFnPtr ptr, BOOL fAsyncMode, LockOwner *pLock) |
256 | { |
257 | WRAPPER_NO_CONTRACT; |
258 | |
259 | Init(0, ptr, fAsyncMode, pLock); |
260 | } |
261 | |
262 | // Init method |
263 | void Init(DWORD cbInitialSize, CompareFnPtr ptr, BOOL fAsyncMode, LockOwner *pLock); |
264 | |
265 | |
266 | //Init method |
267 | void Init(DWORD cbInitialSize, Compare* pCompare, BOOL fAsyncMode, LockOwner *pLock); |
268 | |
269 | // check to see if the value is already in the Hash Table |
270 | // key should be > DELETED |
271 | // if provided, uses the comparison function ptr to compare values |
272 | // returns INVALIDENTRY if not found |
273 | UPTR LookupValue(UPTR key, UPTR value); |
274 | |
275 | // Insert if the value is not already present |
276 | // it is illegal to insert duplicate values in the hash map |
277 | // do a lookup to verify the value is not already present |
278 | |
279 | void InsertValue(UPTR key, UPTR value); |
280 | |
281 | // Replace the value if present |
282 | // returns the previous value, or INVALIDENTRY if not present |
283 | // does not insert a new value under any circumstances |
284 | |
285 | UPTR ReplaceValue(UPTR key, UPTR value); |
286 | |
287 | // mark the entry as deleted and return the stored value |
288 | // returns INVALIDENTRY, if not found |
289 | UPTR DeleteValue (UPTR key, UPTR value); |
290 | |
291 | // for unique keys, use this function to get the value that is |
292 | // stored in the hash table, returns INVALIDENTRY if key not found |
293 | UPTR Gethash(UPTR key); |
294 | |
295 | // Called only when all threads are frozed, like during GC |
296 | // for a SINGLE user mode, call compact after every delete |
297 | // operation on the hash table |
298 | void Compact(); |
299 | |
300 | // Remove all entries from the hash tablex |
301 | void Clear(); |
302 | |
303 | #ifdef DACCESS_COMPILE |
304 | void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
305 | #endif |
306 | |
307 | // inline helper, in non HASHTABLE_PROFILE mode becomes a NO-OP |
308 | void ProfileLookup(UPTR ntry, UPTR retValue); |
309 | // data members used for profiling |
310 | #ifdef HASHTABLE_PROFILE |
311 | unsigned m_cbRehash; // number of times rehashed |
312 | unsigned m_cbRehashSlots; // number of slots that were rehashed |
313 | unsigned m_cbObsoleteTables; |
314 | unsigned m_cbTotalBuckets; |
315 | unsigned m_cbInsertProbesGt8; // inserts that needed more than 8 probes |
316 | LONG m_rgLookupProbes[HASHTABLE_LOOKUP_PROBES_DATA]; // lookup probes |
317 | UPTR maxFailureProbe; // cost of failed lookup |
318 | |
319 | void DumpStatistics(); |
320 | #endif // HASHTABLE_PROFILE |
321 | |
322 | #if 0 // Test-only code for debugging this class. |
323 | #ifndef DACCESS_COMPILE |
324 | static void LookupPerfTest(HashMap * table, const unsigned int MinThreshold); |
325 | static void HashMapTest(); |
326 | #endif // !DACCESS_COMPILE |
327 | #endif // 0 // Test-only code for debugging this class. |
328 | |
329 | protected: |
330 | // static helper function |
331 | static UPTR PutEntry (Bucket* rgBuckets, UPTR key, UPTR value); |
332 | private: |
333 | |
334 | DWORD GetNearestIndex(DWORD cbInitialSize); |
335 | |
336 | #ifdef _DEBUG |
337 | static void Enter(HashMap *); // check valid to enter |
338 | static void Leave(HashMap *); // check valid to leave |
339 | |
340 | typedef Holder<HashMap *, HashMap::Enter, HashMap::Leave> SyncAccessHolder; |
341 | BOOL m_fInSyncCode; // test for non-synchronus access |
342 | #else // !_DEBUG |
343 | // in non DEBUG mode use a no-op helper |
344 | typedef NoOpBaseHolder<HashMap *> SyncAccessHolder; |
345 | #endif // !_DEBUG |
346 | |
347 | // compute the new size, based on the number of free slots |
348 | // available, compact or expand |
349 | UPTR NewSize(); |
350 | // create a new bucket array and rehash the non-deleted entries |
351 | void Rehash(); |
352 | static DWORD GetSize(PTR_Bucket rgBuckets); |
353 | static void SetSize(Bucket* rgBuckets, size_t size); |
354 | PTR_Bucket Buckets(); |
355 | UPTR CompareValues(const UPTR value1, const UPTR value2); |
356 | |
357 | // For double hashing, compute the second hash function once, then add. |
358 | // H(key, i) = H1(key) + i * H2(key), where 0 <= i < numBuckets |
359 | static void HashFunction(const UPTR key, const UINT numBuckets, UINT &seed, UINT &incr); |
360 | |
361 | Compare* m_pCompare; // compare object to be used in lookup |
362 | SIZE_T m_iPrimeIndex; // current size (index into prime array) |
363 | PTR_Bucket m_rgBuckets; // array of buckets |
364 | |
365 | // track the number of inserts and deletes |
366 | SIZE_T m_cbPrevSlotsInUse; |
367 | SIZE_T m_cbInserts; |
368 | SIZE_T m_cbDeletes; |
369 | // mode of operation, synchronus or single user |
370 | bool m_fAsyncMode; |
371 | |
372 | #ifdef _DEBUG |
373 | LPVOID m_lockData; |
374 | FnLockOwner m_pfnLockOwner; |
375 | EEThreadId m_writerThreadId; |
376 | #endif // _DEBUG |
377 | |
378 | #ifdef _DEBUG |
379 | // A thread must own a lock for a hash if it is a writer. |
380 | BOOL OwnLock(); |
381 | #endif // _DEBUG |
382 | |
383 | public: |
384 | ///---------Iterator---------------- |
385 | |
386 | // Iterator, |
387 | class Iterator |
388 | { |
389 | PTR_Bucket m_pBucket; |
390 | PTR_Bucket m_pSentinel; |
391 | int m_id; |
392 | BOOL m_fEnd; |
393 | |
394 | public: |
395 | |
396 | // Constructor |
397 | Iterator(Bucket* pBucket) : |
398 | m_pBucket(dac_cast<PTR_Bucket>(pBucket)), |
399 | m_id(-1), m_fEnd(false) |
400 | { |
401 | SUPPORTS_DAC; |
402 | WRAPPER_NO_CONTRACT; |
403 | |
404 | if (!m_pBucket) { |
405 | m_pSentinel = NULL; |
406 | m_fEnd = true; |
407 | return; |
408 | } |
409 | size_t cbSize = (PTR_size_t(m_pBucket))[0]; |
410 | m_pBucket++; |
411 | m_pSentinel = m_pBucket+cbSize; |
412 | MoveNext(); // start |
413 | } |
414 | |
415 | Iterator(const Iterator& iter) |
416 | { |
417 | LIMITED_METHOD_CONTRACT; |
418 | |
419 | m_pBucket = iter.m_pBucket; |
420 | m_pSentinel = iter.m_pSentinel; |
421 | m_id = iter.m_id; |
422 | m_fEnd = iter.m_fEnd; |
423 | |
424 | } |
425 | |
426 | //destructor |
427 | ~Iterator(){ LIMITED_METHOD_DAC_CONTRACT; }; |
428 | |
429 | // friend operator== |
430 | friend bool operator == (const Iterator& lhs, const Iterator& rhs) |
431 | { |
432 | LIMITED_METHOD_CONTRACT; |
433 | |
434 | return (lhs.m_pBucket == rhs.m_pBucket && lhs.m_id == rhs.m_id); |
435 | } |
436 | // operator = |
437 | inline Iterator& operator= (const Iterator& iter) |
438 | { |
439 | LIMITED_METHOD_CONTRACT; |
440 | |
441 | m_pBucket = iter.m_pBucket; |
442 | m_pSentinel = iter.m_pSentinel; |
443 | m_id = iter.m_id; |
444 | m_fEnd = iter.m_fEnd; |
445 | return *this; |
446 | } |
447 | |
448 | // operator ++ |
449 | inline void operator++ () |
450 | { |
451 | WRAPPER_NO_CONTRACT; |
452 | SUPPORTS_DAC; |
453 | |
454 | _ASSERTE(!m_fEnd); // check we are not alredy at end |
455 | MoveNext(); |
456 | } |
457 | // operator -- |
458 | |
459 | |
460 | |
461 | //accessors : GetDisc() , returns the discriminator |
462 | inline UPTR GetKey() |
463 | { |
464 | LIMITED_METHOD_CONTRACT; |
465 | |
466 | _ASSERTE(!m_fEnd); // check we are not alredy at end |
467 | return m_pBucket->m_rgKeys[m_id]; |
468 | } |
469 | //accessors : SetDisc() , sets the discriminator |
470 | |
471 | |
472 | //accessors : GetValue(), |
473 | // returns the pointer that corresponds to the discriminator |
474 | inline UPTR GetValue() |
475 | { |
476 | WRAPPER_NO_CONTRACT; |
477 | SUPPORTS_DAC; |
478 | |
479 | _ASSERTE(!m_fEnd); // check we are not alredy at end |
480 | return m_pBucket->GetValue(m_id); |
481 | } |
482 | |
483 | |
484 | // end(), check if the iterator is at the end of the bucket |
485 | inline BOOL end() const |
486 | { |
487 | LIMITED_METHOD_DAC_CONTRACT; |
488 | |
489 | return m_fEnd; |
490 | } |
491 | |
492 | protected: |
493 | |
494 | void MoveNext() |
495 | { |
496 | LIMITED_METHOD_DAC_CONTRACT; |
497 | |
498 | for (;m_pBucket < m_pSentinel; m_pBucket++) |
499 | { //loop thru all buckets |
500 | for (m_id = m_id+1; m_id < 4; m_id++) |
501 | { //loop through all slots |
502 | if (m_pBucket->m_rgKeys[m_id] > DELETED) |
503 | { |
504 | return; |
505 | } |
506 | } |
507 | m_id = -1; |
508 | } |
509 | m_fEnd = true; |
510 | } |
511 | |
512 | }; |
513 | |
514 | inline Bucket* firstBucket() |
515 | { |
516 | WRAPPER_NO_CONTRACT; |
517 | SUPPORTS_DAC; |
518 | |
519 | return m_rgBuckets; |
520 | } |
521 | |
522 | // return an iterator, positioned at the beginning of the bucket |
523 | inline Iterator begin() |
524 | { |
525 | WRAPPER_NO_CONTRACT; |
526 | |
527 | return Iterator(m_rgBuckets); |
528 | } |
529 | |
530 | inline SIZE_T GetCount() |
531 | { |
532 | LIMITED_METHOD_CONTRACT; |
533 | |
534 | return m_cbInserts-m_cbDeletes; |
535 | } |
536 | }; |
537 | |
538 | //--------------------------------------------------------------------------------------- |
539 | // class PtrHashMap |
540 | // Wrapper class for using Hash table to store pointer values |
541 | // HashMap class requires that high bit is always reset |
542 | // The allocator used within the runtime, always allocates objects 8 byte aligned |
543 | // so we can shift right one bit, and store the result in the hash table |
544 | class PtrHashMap |
545 | { |
546 | HashMap m_HashMap; |
547 | |
548 | // key really acts as a hash code. Sanitize it from special values used by the underlying HashMap. |
549 | inline static UPTR SanitizeKey(UPTR key) |
550 | { |
551 | return (key > DELETED) ? key : (key + 100); |
552 | } |
553 | |
554 | public: |
555 | #ifndef DACCESS_COMPILE |
556 | void *operator new(size_t size, LoaderHeap *pHeap); |
557 | void operator delete(void *p); |
558 | #endif // !DACCESS_COMPILE |
559 | |
560 | // Init |
561 | void Init(BOOL fAsyncMode, LockOwner *pLock) |
562 | { |
563 | WRAPPER_NO_CONTRACT; |
564 | |
565 | Init(0,NULL,fAsyncMode,pLock); |
566 | } |
567 | // Init |
568 | void Init(DWORD cbInitialSize, BOOL fAsyncMode, LockOwner *pLock) |
569 | { |
570 | WRAPPER_NO_CONTRACT; |
571 | |
572 | Init(cbInitialSize, NULL, fAsyncMode,pLock); |
573 | } |
574 | // Init |
575 | void Init(CompareFnPtr ptr, BOOL fAsyncMode, LockOwner *pLock) |
576 | { |
577 | WRAPPER_NO_CONTRACT; |
578 | |
579 | Init(0, ptr, fAsyncMode,pLock); |
580 | } |
581 | |
582 | // Init method |
583 | void Init(DWORD cbInitialSize, CompareFnPtr ptr, BOOL fAsyncMode, LockOwner *pLock); |
584 | |
585 | // check to see if the value is already in the Hash Table |
586 | LPVOID LookupValue(UPTR key, LPVOID pv) |
587 | { |
588 | WRAPPER_NO_CONTRACT; |
589 | |
590 | key = SanitizeKey(key); |
591 | |
592 | // gmalloc allocator, always allocates 8 byte aligned |
593 | // so we can shift out the lowest bit |
594 | // ptr right shift by 1 |
595 | UPTR value = (UPTR)pv; |
596 | _ASSERTE((value & 0x1) == 0); |
597 | value>>=1; |
598 | UPTR val = m_HashMap.LookupValue (key, value); |
599 | if (val != (UPTR) INVALIDENTRY) |
600 | { |
601 | val<<=1; |
602 | } |
603 | return (LPVOID)val; |
604 | } |
605 | |
606 | // Insert if the value is not already present |
607 | // it is illegal to insert duplicate values in the hash map |
608 | // users should do a lookup to verify the value is not already present |
609 | |
610 | void InsertValue(UPTR key, LPVOID pv) |
611 | { |
612 | WRAPPER_NO_CONTRACT; |
613 | |
614 | key = SanitizeKey(key); |
615 | |
616 | // gmalloc allocator, always allocates 8 byte aligned |
617 | // so we can shift out the lowest bit |
618 | // ptr right shift by 1 |
619 | UPTR value = (UPTR)pv; |
620 | _ASSERTE((value & 0x1) == 0); |
621 | value>>=1; |
622 | m_HashMap.InsertValue (key, value); |
623 | } |
624 | |
625 | // Replace the value if present |
626 | // returns the previous value, or INVALIDENTRY if not present |
627 | // does not insert a new value under any circumstances |
628 | |
629 | LPVOID ReplaceValue(UPTR key, LPVOID pv) |
630 | { |
631 | WRAPPER_NO_CONTRACT; |
632 | |
633 | key = SanitizeKey(key); |
634 | |
635 | // gmalloc allocator, always allocates 8 byte aligned |
636 | // so we can shift out the lowest bit |
637 | // ptr right shift by 1 |
638 | UPTR value = (UPTR)pv; |
639 | _ASSERTE((value & 0x1) == 0); |
640 | value>>=1; |
641 | UPTR val = m_HashMap.ReplaceValue (key, value); |
642 | if (val != (UPTR) INVALIDENTRY) |
643 | { |
644 | val<<=1; |
645 | } |
646 | return (LPVOID)val; |
647 | } |
648 | |
649 | // mark the entry as deleted and return the stored value |
650 | // returns INVALIDENTRY if not found |
651 | LPVOID DeleteValue (UPTR key,LPVOID pv) |
652 | { |
653 | WRAPPER_NO_CONTRACT; |
654 | |
655 | key = SanitizeKey(key); |
656 | |
657 | UPTR value = (UPTR)pv; |
658 | _ASSERTE((value & 0x1) == 0); |
659 | value >>=1 ; |
660 | UPTR val = m_HashMap.DeleteValue(key, value); |
661 | if (val != (UPTR) INVALIDENTRY) |
662 | { |
663 | val <<= 1; |
664 | } |
665 | return (LPVOID)val; |
666 | } |
667 | |
668 | // for unique keys, use this function to get the value that is |
669 | // stored in the hash table, returns INVALIDENTRY if key not found |
670 | LPVOID Gethash(UPTR key) |
671 | { |
672 | WRAPPER_NO_CONTRACT; |
673 | |
674 | key = SanitizeKey(key); |
675 | |
676 | UPTR val = m_HashMap.Gethash(key); |
677 | if (val != (UPTR) INVALIDENTRY) |
678 | { |
679 | val <<= 1; |
680 | } |
681 | return (LPVOID)val; |
682 | } |
683 | |
684 | void Compact() |
685 | { |
686 | WRAPPER_NO_CONTRACT; |
687 | |
688 | m_HashMap.Compact(); |
689 | } |
690 | |
691 | void Clear() |
692 | { |
693 | WRAPPER_NO_CONTRACT; |
694 | |
695 | m_HashMap.Clear(); |
696 | } |
697 | |
698 | class PtrIterator |
699 | { |
700 | HashMap::Iterator iter; |
701 | |
702 | public: |
703 | PtrIterator(HashMap& hashMap) : iter(hashMap.begin()) |
704 | { |
705 | LIMITED_METHOD_DAC_CONTRACT; |
706 | } |
707 | PtrIterator(Bucket* bucket) : iter(bucket) |
708 | { |
709 | LIMITED_METHOD_DAC_CONTRACT; |
710 | } |
711 | |
712 | ~PtrIterator() |
713 | { |
714 | LIMITED_METHOD_DAC_CONTRACT; |
715 | } |
716 | |
717 | BOOL end() |
718 | { |
719 | WRAPPER_NO_CONTRACT; |
720 | SUPPORTS_DAC; |
721 | |
722 | return iter.end(); |
723 | } |
724 | |
725 | UPTR GetKey() |
726 | { |
727 | WRAPPER_NO_CONTRACT; |
728 | SUPPORTS_DAC; |
729 | |
730 | return iter.GetKey(); |
731 | } |
732 | |
733 | PTR_VOID GetValue() |
734 | { |
735 | WRAPPER_NO_CONTRACT; |
736 | SUPPORTS_DAC; |
737 | |
738 | UPTR val = iter.GetValue(); |
739 | if (val != (UPTR) INVALIDENTRY) |
740 | { |
741 | val <<= 1; |
742 | } |
743 | return PTR_VOID(val); |
744 | } |
745 | |
746 | void operator++() |
747 | { |
748 | WRAPPER_NO_CONTRACT; |
749 | SUPPORTS_DAC; |
750 | |
751 | iter.operator++(); |
752 | } |
753 | }; |
754 | |
755 | inline Bucket* firstBucket() |
756 | { |
757 | WRAPPER_NO_CONTRACT; |
758 | SUPPORTS_DAC; |
759 | |
760 | return m_HashMap.firstBucket(); |
761 | } |
762 | |
763 | // return an iterator, positioned at the beginning of the bucket |
764 | inline PtrIterator begin() |
765 | { |
766 | WRAPPER_NO_CONTRACT; |
767 | |
768 | return PtrIterator(m_HashMap); |
769 | } |
770 | |
771 | inline SIZE_T GetCount() |
772 | { |
773 | LIMITED_METHOD_CONTRACT; |
774 | |
775 | return m_HashMap.GetCount(); |
776 | } |
777 | |
778 | #ifdef DACCESS_COMPILE |
779 | void EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
780 | { |
781 | SUPPORTS_DAC; |
782 | m_HashMap.EnumMemoryRegions(flags); |
783 | } |
784 | #endif // DACCESS_COMPILE |
785 | }; |
786 | |
787 | //--------------------------------------------------------------------- |
788 | // inline Bucket*& NextObsolete (Bucket* rgBuckets) |
789 | // get the next obsolete bucket in the chain |
790 | inline |
791 | Bucket*& NextObsolete (Bucket* rgBuckets) |
792 | { |
793 | LIMITED_METHOD_CONTRACT; |
794 | |
795 | return *(Bucket**)&((size_t*)rgBuckets)[1]; |
796 | } |
797 | |
798 | #endif // !_HASH_H_ |
799 | |