Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2344 lines
80 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. lkhash.h
  5. Abstract:
  6. Declares hash tables
  7. Author:
  8. Paul Larson, palarson@microsoft.com, July 1997
  9. George V. Reilly (GeorgeRe) 06-Jan-1998
  10. Environment:
  11. Win32 - User Mode
  12. Project:
  13. Internet Information Server RunTime Library
  14. Revision History:
  15. --*/
  16. #ifndef __LKHASH_H__
  17. #define __LKHASH_H__
  18. //=====================================================================
  19. // The class CLKLinearHashTable defined in this file provides dynamic hash
  20. // tables, i.e. tables that grow and shrink dynamically with
  21. // the number of records in the table.
  22. // The basic method used is linear hashing, as explained in:
  23. //
  24. // P.-A. Larson, Dynamic Hash Tables, Comm. of the ACM, 31, 4 (1988)
  25. //
  26. // This version has the following characteristics:
  27. // - It is thread-safe and uses spin locks for synchronization.
  28. // - It was designed to support very high rates of concurrent
  29. // operations (inserts/deletes/lookups). It achieves this by
  30. // (a) partitioning a CLKHashTable into a collection of CLKLinearHashTables
  31. // to reduce contention on the global table lock.
  32. // (b) minimizing the hold time on a table lock, preferring to lock
  33. // down a bucket chain instead.
  34. // - The design is L1 cache-conscious. See CNodeClump.
  35. // - It is designed for sets varying in size from a dozen
  36. // elements to a several million elements.
  37. //
  38. // Main classes:
  39. // CLKLinearHashTable: thread-safe linear hash table
  40. // CLKHashTable: collection of CLKLinearHashTables
  41. // CTypedHashTable: typesafe wrapper for CLKHashTable
  42. //
  43. //
  44. // Paul Larson, [email protected], July 1997
  45. // Original implementation with input from Murali R. Krishnan,
  46. // [email protected].
  47. //
  48. // George V. Reilly, [email protected], Dec 1997-Jan 1998
  49. // Massive cleanup and rewrite. Added templates.
  50. //=====================================================================
  51. // 1) Linear Hashing
  52. // ------------------
  53. //
  54. // Linear hash tables grow and shrink dynamically with the number of
  55. // records in the table. The growth or shrinkage is smooth: logically,
  56. // one bucket at a time but physically in larger increments
  57. // (64 buckets). An insertion (deletion) may cause an expansion
  58. // (contraction) of the table. This causes relocation of a small number
  59. // of records (at most one bucket worth). All operations (insert,
  60. // delete, lookup) take constant expected time, regardless of the
  61. // current size or the growth of the table.
  62. //
  63. // 2) LK extensions to Linear hash table
  64. // --------------------------------------
  65. //
  66. // Larson-Krishnan extensions to Linear hash tables for multiprocessor
  67. // scalability and improved cache performance.
  68. //
  69. // Traditional implementations of linear hash tables use one global lock
  70. // to prevent interference between concurrent operations
  71. // (insert/delete/lookup) on the table. The single lock easily becomes
  72. // the bottleneck in SMP scenarios when multiple threads are used.
  73. //
  74. // Traditionally, a (hash) bucket is implemented as a chain of
  75. // single-item nodes. Every operation results in chasing down a chain
  76. // looking for an item. However, pointer chasing is very slow on modern
  77. // systems because almost every jump results in a cache miss. L2 (or L3)
  78. // cache misses are very expensive in missed CPU cycles and the cost is
  79. // increasing (going to 100s of cycles in the future).
  80. //
  81. // LK extensions offer
  82. // 1) Partitioning (by hashing) of records among multiple subtables.
  83. // Each subtable has locks but there is no global lock. Each
  84. // subtable receives a much lower rate of operations, resulting in
  85. // fewer conflicts.
  86. //
  87. // 2) Improve cache locality by grouping keys and their hash values
  88. // into contigous chunks that fit exactly into one (or a few)
  89. // cache lines.
  90. //
  91. // Specifically the implementation that exists here achieves this using
  92. // following techniques.
  93. //
  94. // Class CLKHashTable is the top-level data structure that dynamically
  95. // creates m_cSubTables linear hash tables. The CLKLinearHashTables act as
  96. // the subtables to which items and accesses are fanned out. A good
  97. // hash function multiplexes requests uniformly to various subtables,
  98. // thus minimizing traffic to any single subtable. The implemenation
  99. // uses a home-grown version of bounded spinlocks, that is, a thread
  100. // does not spin on a lock indefinitely, instead yielding after a
  101. // predetermined number of loops.
  102. //
  103. // Each CLKLinearHashTable consists of a CDirEntry pointing to segments
  104. // each holding m_dwSegSize CBuckets. Each CBucket in turn consists of a
  105. // chain of CNodeClumps. Each CNodeClump contains a group of
  106. // NODES_PER_CLUMP hash values (aka hash keys or signatures) and
  107. // pointers to the associated data items. Keeping the signatures
  108. // together increases the cache locality in scans for lookup.
  109. //
  110. // Traditionally, people store a link-list element right inside the
  111. // object that is hashed and use this link-list for the chaining of data
  112. // blocks. However, keeping just the pointers to the data object and
  113. // not chaining through them limits the need for bringing in the data
  114. // object to the cache. We need to access the data object only if the
  115. // hash values match. This limits the cache-thrashing behaviour
  116. // exhibited by conventional implementations. It has the additional
  117. // benefit that the objects themselves do not need to be modified
  118. // in order to be collected in the hash table (i.e., it's non-invasive).
  119. //--------------------------------------------------------------------
  120. // TODO
  121. // * Debugging support for iisprobe and inetdbg?
  122. // * Use auto_ptrs.
  123. // * Provide ReplaceRecord and DeleteRecord methods on iterators.
  124. // * Sloppy iterators
  125. // * Provide implementations of the STL collection classes, map, set,
  126. // multimap, and multiset.
  127. // * Make exception safe.
  128. //--------------------------------------------------------------------
  129. #include <irtldbg.h>
  130. #include <locks.h>
  131. #include <hashfn.h>
  132. #include <limits.h>
  133. #ifdef __LKHASH_NAMESPACE__
  134. namespace LKHash {
  135. #endif // __LKHASH_NAMESPACE__
  136. enum LK_TABLESIZE {
  137. LK_SMALL_TABLESIZE= 1, // < 200 elements
  138. LK_MEDIUM_TABLESIZE= 2, // 200...10,000 elements
  139. LK_LARGE_TABLESIZE= 3, // 10,000+ elements
  140. };
  141. // Default values for the hashtable constructors
  142. enum {
  143. LK_DFLT_MAXLOAD= 4, // Default upperbound on average chain length.
  144. LK_DFLT_INITSIZE=LK_MEDIUM_TABLESIZE, // Default initial size of hash table
  145. LK_DFLT_NUM_SUBTBLS= 0, // Use a heuristic to choose #subtables
  146. };
  147. // build fix hack
  148. enum {
  149. DFLT_LK_MAXLOAD= LK_DFLT_MAXLOAD,
  150. DFLT_LK_INITSIZE= LK_DFLT_INITSIZE,
  151. DFLT_LK_NUM_SUBTBLS= LK_DFLT_NUM_SUBTBLS,
  152. };
  153. //--------------------------------------------------------------------
  154. // forward declarations
  155. class IRTL_DLLEXP CLKLinearHashTable;
  156. class IRTL_DLLEXP CLKHashTable;
  157. template <class _Der, class _Rcd, class _Ky, class _HT, class _Iter>
  158. class CTypedHashTable;
  159. //--------------------------------------------------------------------
  160. // Possible return codes from public member functions of
  161. // CLKLinearHashTable, CLKHashTable, and CTypedHashTable
  162. enum LK_RETCODE {
  163. // severe errors < 0
  164. LK_UNUSABLE = -99, // Table corrupted: all bets are off
  165. LK_ALLOC_FAIL, // ran out of memory
  166. LK_BAD_ITERATOR, // invalid iterator; e.g., points to another table
  167. LK_BAD_RECORD, // invalid record; e.g., NULL for InsertRecord
  168. LK_SUCCESS = 0, // everything's okay
  169. LK_KEY_EXISTS, // key already present for InsertRecord(no-overwrite)
  170. LK_NO_SUCH_KEY, // key not found
  171. LK_NO_MORE_ELEMENTS,// iterator exhausted
  172. };
  173. #define LK_SUCCEEDED(lkrc) ((lkrc) >= LK_SUCCESS)
  174. //--------------------------------------------------------------------
  175. // Return codes from PFnRecordPred.
  176. enum LK_PREDICATE {
  177. LKP_ABORT = 1, // Stop walking the table immediately
  178. LKP_NO_ACTION = 2, // No action, just keep walking
  179. LKP_PERFORM = 3, // Perform action and continue walking
  180. LKP_PERFORM_STOP = 4, // Perform action, then stop
  181. LKP_DELETE = 5, // Delete record and keep walking
  182. LKP_DELETE_STOP = 6, // Delete record, then stop
  183. };
  184. //--------------------------------------------------------------------
  185. // Return codes from PFnRecordAction.
  186. enum LK_ACTION {
  187. LKA_ABORT = 1, // Stop walking the table immediately
  188. LKA_FAILED = 2, // Action failed; continue walking the table
  189. LKA_SUCCEEDED = 3, // Action succeeded; continue walking the table
  190. };
  191. //--------------------------------------------------------------------
  192. // Parameter to Apply and ApplyIf.
  193. enum LK_LOCKTYPE {
  194. LKL_READLOCK = 1, // Lock the table for reading (for constness)
  195. LKL_WRITELOCK = 2, // Lock the table for writing
  196. };
  197. //--------------------------------------------------------------------
  198. // Global table lock code. This is only used to measure how much of a
  199. // slowdown having a global lock on the CLKHashTable causes. It is never
  200. // used in production code.
  201. // #define LKHASH_GLOBAL_LOCK CCritSec
  202. #ifdef LKHASH_GLOBAL_LOCK
  203. # define LKHASH_GLOBAL_LOCK_DECLARATIONS() \
  204. typedef LKHASH_GLOBAL_LOCK GlobalLock; \
  205. mutable GlobalLock m_lkGlobal;
  206. # define LKHASH_GLOBAL_READ_LOCK() m_lkGlobal.ReadLock()
  207. # define LKHASH_GLOBAL_WRITE_LOCK() m_lkGlobal.WriteLock()
  208. # define LKHASH_GLOBAL_READ_UNLOCK() m_lkGlobal.ReadUnlock()
  209. # define LKHASH_GLOBAL_WRITE_UNLOCK() m_lkGlobal.WriteUnlock()
  210. #else // !LKHASH_GLOBAL_LOCK
  211. # define LKHASH_GLOBAL_LOCK_DECLARATIONS()
  212. // These ones will be optimized away by the compiler
  213. # define LKHASH_GLOBAL_READ_LOCK() ((void)0)
  214. # define LKHASH_GLOBAL_WRITE_LOCK() ((void)0)
  215. # define LKHASH_GLOBAL_READ_UNLOCK() ((void)0)
  216. # define LKHASH_GLOBAL_WRITE_UNLOCK() ((void)0)
  217. #endif // !LKHASH_GLOBAL_LOCK
  218. //--------------------------------------------------------------------
  219. // Statistical information returned by GetStatistics
  220. //--------------------------------------------------------------------
  221. #ifdef LOCK_INSTRUMENTATION
  222. class IRTL_DLLEXP CAveragedLockStats : public CLockStatistics
  223. {
  224. public:
  225. int m_nItems;
  226. CAveragedLockStats()
  227. : m_nItems(1)
  228. {}
  229. };
  230. #endif // LOCK_INSTRUMENTATION
  231. class IRTL_DLLEXP CLKHashTableStats
  232. {
  233. public:
  234. int RecordCount; // number of records in the table
  235. int TableSize; // table size in number of slots
  236. int DirectorySize; // number of entries in directory
  237. int LongestChain; // longest hash chain in the table
  238. int EmptySlots; // number of unused hash slots
  239. double SplitFactor; // fraction of buckets split
  240. double AvgSearchLength; // average length of a successful search
  241. double ExpSearchLength; // theoretically expected length
  242. double AvgUSearchLength; // average length of an unsuccessful search
  243. double ExpUSearchLength; // theoretically expected length
  244. int NodeClumpSize; // number of slots in a node clump
  245. int CBucketSize; // sizeof(CBucket)
  246. #ifdef LOCK_INSTRUMENTATION
  247. CAveragedLockStats m_alsTable; // stats for table lock
  248. CAveragedLockStats m_alsBucketsAvg; // avg of stats for bucket locks
  249. CGlobalLockStatistics m_gls; // global statistics for all locks
  250. #endif // LOCK_INSTRUMENTATION
  251. enum {
  252. MAX_BUCKETS = 40,
  253. };
  254. // histogram of bucket lengths
  255. LONG m_aBucketLenHistogram[MAX_BUCKETS];
  256. CLKHashTableStats()
  257. : RecordCount(0),
  258. TableSize(0),
  259. DirectorySize(0),
  260. LongestChain(0),
  261. EmptySlots(0),
  262. SplitFactor(0.0),
  263. AvgSearchLength(0.0),
  264. ExpSearchLength(0.0),
  265. AvgUSearchLength(0.0),
  266. ExpUSearchLength(0.0),
  267. NodeClumpSize(1),
  268. CBucketSize(0)
  269. {
  270. for (int i = MAX_BUCKETS; --i >= 0; )
  271. m_aBucketLenHistogram[i] = 0;
  272. }
  273. static const LONG*
  274. BucketSizes()
  275. {
  276. static const LONG s_aBucketSizes[MAX_BUCKETS] = {
  277. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  278. 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  279. 20, 21, 22, 23, 24, 25, 30, 40, 50, 60,
  280. 70, 80, 90, 100, 200, 500, 1000,10000, 100000, LONG_MAX,
  281. };
  282. return s_aBucketSizes;
  283. }
  284. static LONG
  285. BucketSize(
  286. LONG nBucketIndex)
  287. {
  288. IRTLASSERT(0 <= nBucketIndex && nBucketIndex < MAX_BUCKETS);
  289. return BucketSizes()[nBucketIndex];
  290. }
  291. static LONG
  292. BucketIndex(
  293. LONG nBucketLength)
  294. {
  295. const LONG* palBucketSizes = BucketSizes();
  296. LONG i = 0;
  297. while (palBucketSizes[i] < nBucketLength)
  298. ++i;
  299. if (i == MAX_BUCKETS || palBucketSizes[i] > nBucketLength)
  300. --i;
  301. IRTLASSERT(0 <= i && i < MAX_BUCKETS);
  302. return i;
  303. }
  304. };
  305. //--------------------------------------------------------------------
  306. // CLKLinearHashTable deals with void* records. These typedefs
  307. // provide prototypes for functions that manipulate instances of
  308. // those records. CTypedHashTable and CStringTestHashTable (below) show a
  309. // way to encapsulate these in typesafe wrappers.
  310. //--------------------------------------------------------------------
  311. // Given a record, return its key. Assumes that the key is embedded in
  312. // the record, or at least somehow derivable from the record. For
  313. // completely unrelated keys & values, a wrapper class should use
  314. // something like STL's pair<key, value> template to aggregate them
  315. // into a record.
  316. typedef const void* (*PFnExtractKey) (const void* pvRecord);
  317. // Given a key, return its hash signature. The hashing functions in
  318. // hashfn.h (or something that builds upon them) are suggested.
  319. typedef DWORD (*PFnCalcKeyHash) (const void* pvKey);
  320. // Compare two keys for equality; e.g., _stricmp, memcmp, operator==
  321. typedef bool (*PFnEqualKeys) (const void* pvKey1, const void* pvKey2);
  322. // Increment the reference count of a record before returning it from
  323. // FindKey. It's necessary to do it in FindKey itself while the bucket
  324. // is still locked, rather than one of the wrappers, to avoid race
  325. // conditions. Similarly, the reference count is incremented in
  326. // InsertRecord and decremented in DeleteKey. Finally, if an old record
  327. // is overwritten in InsertRecord, its reference count is decremented.
  328. //
  329. // It's up to you to decrement the reference count when you're finished
  330. // with it after retrieving it via FindKey and to determine the
  331. // semantics of what this means. The hashtable itself has no notion of
  332. // reference counts; this is merely to help with the lifetime management
  333. // of the record objects.
  334. typedef void (*PFnAddRefRecord)(const void* pvRecord, int nIncr);
  335. // ApplyIf() and DeleteIf(): Does the record match the predicate?
  336. typedef LK_PREDICATE (*PFnRecordPred) (const void* pvRecord, void* pvState);
  337. // Apply() et al: Perform action on record.
  338. typedef LK_ACTION (*PFnRecordAction)(const void* pvRecord, void* pvState);
  339. //--------------------------------------------------------------------
  340. // Custom memory allocators
  341. //--------------------------------------------------------------------
  342. // #define LKHASH_ACACHE 1
  343. // #define LKHASH_MANODEL 1
  344. // #define LKHASH_MADEL 1
  345. // #define LKHASH_MEM_DEFAULT_ALIGN 32
  346. #ifndef LKHASH_MEM_DEFAULT_ALIGN
  347. # define LKHASH_MEM_DEFAULT_ALIGN 8
  348. #endif
  349. #if defined(LKHASH_ACACHE)
  350. # include <acache.hxx>
  351. typedef ALLOC_CACHE_HANDLER CAllocator;
  352. # define LKHASH_ALLOCATOR_NEW(C, N) \
  353. const ALLOC_CACHE_CONFIGURATION acc = { 1, N, sizeof(C) }; \
  354. C::sm_palloc = new ALLOC_CACHE_HANDLER("IISRTL:" #C, &acc);
  355. #elif defined(LKHASH_MANODEL)
  356. # include <manodel.hxx>
  357. typedef MEMORY_ALLOC_NO_DELETE CAllocator;
  358. # define LKHASH_ALLOCATOR_NEW(C, N) \
  359. C::sm_palloc = new MEMORY_ALLOC_NO_DELETE(sizeof(C), \
  360. LKHASH_MEM_DEFAULT_ALIGN);
  361. #elif defined(LKHASH_MADEL)
  362. # include <madel.hxx>
  363. typedef MEMORY_ALLOC_DELETE CAllocator;
  364. # define LKHASH_ALLOCATOR_NEW(C, N) \
  365. C::sm_palloc = new MEMORY_ALLOC_DELETE(sizeof(C), \
  366. LKHASH_MEM_DEFAULT_ALIGN, N);
  367. #else // no custom allocator
  368. # undef LKHASH_ALLOCATOR_NEW
  369. #endif // no custom allocator
  370. // Used to initialize and destroy custom allocators
  371. bool LKHashTableInit();
  372. void LKHashTableUninit();
  373. #ifdef LKHASH_ALLOCATOR_NEW
  374. // placed inline in the declaration of class C
  375. # define LKHASH_ALLOCATOR_DEFINITIONS(C) \
  376. protected: \
  377. static CAllocator* sm_palloc; \
  378. friend bool LKHashTableInit(); \
  379. friend void LKHashTableUninit(); \
  380. public: \
  381. static void* operator new(size_t s) \
  382. { \
  383. IRTLASSERT(s == sizeof(C)); \
  384. IRTLASSERT(sm_palloc != NULL); \
  385. return sm_palloc->Alloc(); \
  386. } \
  387. static void operator delete(void* pv) \
  388. { \
  389. IRTLASSERT(pv != NULL); \
  390. if (sm_palloc != NULL) \
  391. sm_palloc->Free(pv); \
  392. }
  393. // used in LKHashTableInit()
  394. # define LKHASH_ALLOCATOR_INIT(C, N, f) \
  395. { \
  396. if (f) \
  397. { \
  398. IRTLASSERT(C::sm_palloc == NULL); \
  399. LKHASH_ALLOCATOR_NEW(C, N); \
  400. f = (C::sm_palloc != NULL); \
  401. } \
  402. }
  403. // used in LKHashTableUninit()
  404. # define LKHASH_ALLOCATOR_UNINIT(C) \
  405. { \
  406. if (C::sm_palloc != NULL) \
  407. { \
  408. delete C::sm_palloc; \
  409. C::sm_palloc = NULL; \
  410. } \
  411. }
  412. #else // !LKHASH_ALLOCATOR_NEW
  413. # define LKHASH_ALLOCATOR_DEFINITIONS(C)
  414. # define LKHASH_ALLOCATOR_INIT(C, N, f)
  415. # define LKHASH_ALLOCATOR_UNINIT(C)
  416. #endif // !LKHASH_ALLOCATOR_NEW
  417. //--------------------------------------------------------------------
  418. // CLKLinearHashTable
  419. //
  420. // A thread-safe linear hash table.
  421. //--------------------------------------------------------------------
  422. class IRTL_DLLEXP CLKLinearHashTable
  423. {
  424. public:
  425. typedef CSpinLock TableLock;
  426. typedef CSpinLock BucketLock;
  427. class CIterator;
  428. friend class CLKLinearHashTable::CIterator;
  429. private:
  430. friend class CLKHashTable;
  431. #ifdef LKHASH_ALLOCATOR_NEW
  432. friend bool LKHashTableInit();
  433. friend void LKHashTableUninit();
  434. #endif // LKHASH_ALLOCATOR_NEW
  435. #ifdef LKHASH_INSTRUMENTATION
  436. // TODO
  437. #endif // LKHASH_INSTRUMENTATION
  438. // Class for nodes on a bucket chain. Instead of a node containing
  439. // one (signature, record-pointer, next-tuple-pointer) tuple, it
  440. // contains _N_ such tuples. (N-1 next-tuple-pointers are omitted.)
  441. // This improves locality of reference greatly; i.e., it's L1
  442. // cache-friendly. It also reduces memory fragmentation and memory
  443. // allocator overhead. It does complicate the chain traversal code
  444. // slightly, admittedly.
  445. //
  446. // This theory is beautiful. In practice, however, CNodeClumps
  447. // are *not* perfectly aligned on 32-byte boundaries by the memory
  448. // allocators. Experimental results indicate that we get a 2-3%
  449. // speed improvement by using 32-byte-aligned blocks, but this must
  450. // be considered against the average of 16 bytes wasted per block.
  451. class CNodeClump
  452. {
  453. public:
  454. // Record slots per chunk - set so a chunk matches (one or
  455. // two) cache lines. 2 ==> 28 bytes, 6 ==> 60 bytes
  456. // Note: the default max load factor is 4.0, which implies that
  457. // there will seldom be more than one node clump in a chain.
  458. enum {
  459. BUCKET_BYTE_SIZE = 64,
  460. BUCKET_OVERHEAD = sizeof(BucketLock) + sizeof(CNodeClump*),
  461. NODE_SIZE = sizeof(const void*) + sizeof(DWORD),
  462. NODES_PER_CLUMP = (BUCKET_BYTE_SIZE - BUCKET_OVERHEAD) / NODE_SIZE
  463. };
  464. DWORD m_dwKeySigs[NODES_PER_CLUMP]; // hash values computed from keys
  465. CNodeClump* m_pncNext; // next node clump on the chain
  466. const void* m_pvNode[NODES_PER_CLUMP];// pointers to records
  467. CNodeClump()
  468. {
  469. Clear();
  470. }
  471. void Clear()
  472. { memset(this, 0, sizeof(*this)); }
  473. #ifdef LKRDEBUG
  474. // Don't want overhead of calls to dtor in retail build
  475. ~CNodeClump()
  476. {
  477. IRTLASSERT(m_pncNext == NULL); // no dangling pointers
  478. for (DWORD i = 0; i < NODES_PER_CLUMP; ++i)
  479. IRTLASSERT(m_dwKeySigs[i] == 0 && m_pvNode[i] == NULL);
  480. }
  481. #endif // LKRDEBUG
  482. LKHASH_ALLOCATOR_DEFINITIONS(CNodeClump);
  483. };
  484. // Class for bucket chains of the hash table. Note that the first
  485. // nodeclump is actually included in the bucket and not dynamically
  486. // allocated, which increases space requirements slightly but does
  487. // improve performance.
  488. class CBucket
  489. {
  490. mutable BucketLock m_Lock; // lock protecting this bucket
  491. #ifdef LOCK_INSTRUMENTATION
  492. static LONG sm_cBuckets;
  493. static const char*
  494. _LockName()
  495. {
  496. LONG l = ++sm_cBuckets;
  497. // possible race condition but we don't care, as this is never
  498. // used in production code
  499. static char s_szName[CLockStatistics::L_NAMELEN];
  500. wsprintf(s_szName, "B%06x", 0xFFFFFF & l);
  501. return s_szName;
  502. }
  503. #endif // LOCK_INSTRUMENTATION
  504. public:
  505. CNodeClump m_ncFirst; // first CNodeClump of this bucket
  506. #if defined(LOCK_INSTRUMENTATION) || defined(LKRDEBUG)
  507. CBucket()
  508. #ifdef LOCK_INSTRUMENTATION
  509. : m_Lock(_LockName())
  510. #endif // LOCK_INSTRUMENTATION
  511. {
  512. #ifdef LKRDEBUG
  513. LOCK_LOCKTYPE lt = BucketLock::LockType();
  514. if (lt == LOCK_SPINLOCK || lt == LOCK_FAKELOCK)
  515. IRTLASSERT(sizeof(*this) <= 64);
  516. #endif LKRDEBUG
  517. }
  518. #endif // LOCK_INSTRUMENTATION || LKRDEBUG
  519. void WriteLock() { m_Lock.WriteLock(); }
  520. void ReadLock() const { m_Lock.ReadLock(); }
  521. void WriteUnlock() const { m_Lock.WriteUnlock(); }
  522. void ReadUnlock() const { m_Lock.ReadUnlock(); }
  523. bool IsWriteLocked() const { return m_Lock.IsWriteLocked(); }
  524. bool IsReadLocked() const { return m_Lock.IsReadLocked(); }
  525. bool IsWriteUnlocked() const { return m_Lock.IsWriteUnlocked(); }
  526. bool IsReadUnlocked() const { return m_Lock.IsReadUnlocked(); }
  527. void SetSpinCount(WORD wSpins) { m_Lock.SetSpinCount(wSpins); }
  528. WORD GetSpinCount() const { return m_Lock.GetSpinCount(); }
  529. #ifdef LOCK_INSTRUMENTATION
  530. CLockStatistics LockStats() const {return m_Lock.Statistics();}
  531. #endif // LOCK_INSTRUMENTATION
  532. };
  533. // The hash table space is divided into fixed-size segments (arrays of
  534. // CBuckets) and physically grows/shrinks one segment at a time.
  535. // We provide small, medium, and large segments to better tune the
  536. // overall memory requirements of the hash table according to the
  537. // expected usage of an instance.
  538. class CSegment
  539. {
  540. public:
  541. virtual ~CSegment() {}; // link fails if this is pure virtual
  542. virtual DWORD Bits() const = 0;
  543. virtual DWORD Size() const = 0;
  544. virtual DWORD Mask() const = 0;
  545. virtual DWORD InitSize() const = 0;
  546. virtual CBucket& Slot(DWORD i) = 0;
  547. };
  548. // Small-sized segments contain 2^3 = 8 buckets => ~0.5Kb
  549. class CSmallSegment : public CSegment
  550. {
  551. public:
  552. // Maximum table size equals MAX_DIRSIZE * SEGSIZE buckets.
  553. enum {
  554. SEGBITS = 3,// number of bits extracted from a hash
  555. // address for offset within a segment
  556. SEGSIZE = (1<<SEGBITS),// segment size
  557. SEGMASK = (SEGSIZE-1), // mask used for extracting offset bit
  558. INITSIZE = 1 * SEGSIZE, // #segments to allocate initially
  559. };
  560. private:
  561. CBucket m_bktSlots[SEGSIZE];
  562. public:
  563. virtual ~CSmallSegment() {}
  564. virtual DWORD Bits() const { return SEGBITS; }
  565. virtual DWORD Size() const { return SEGSIZE; }
  566. virtual DWORD Mask() const { return SEGMASK; }
  567. virtual DWORD InitSize() const { return INITSIZE;}
  568. virtual CBucket& Slot(DWORD i)
  569. { IRTLASSERT(i < SEGSIZE); return m_bktSlots[i]; }
  570. #ifdef LKRDEBUG
  571. CSmallSegment()
  572. {
  573. // IRTLASSERT(((ULONG_PTR) this & (LKHASH_MEM_DEFAULT_ALIGN-1)) == 0);
  574. IRTLASSERT(sizeof(*this) == SEGSIZE * sizeof(CBucket) + sizeof(void*));
  575. }
  576. #endif // LKRDEBUG
  577. LKHASH_ALLOCATOR_DEFINITIONS(CSmallSegment);
  578. };
  579. // Medium-sized segments contain 2^6 = 64 buckets => ~4Kb
  580. class CMediumSegment : public CSegment
  581. {
  582. public:
  583. enum {
  584. SEGBITS = 6,
  585. SEGSIZE = (1<<SEGBITS),
  586. SEGMASK = (SEGSIZE-1),
  587. INITSIZE = 2 * SEGSIZE,
  588. };
  589. private:
  590. CBucket m_bktSlots[SEGSIZE];
  591. public:
  592. virtual ~CMediumSegment() {}
  593. virtual DWORD Bits() const { return SEGBITS; }
  594. virtual DWORD Size() const { return SEGSIZE; }
  595. virtual DWORD Mask() const { return SEGMASK; }
  596. virtual DWORD InitSize() const { return INITSIZE;}
  597. virtual CBucket& Slot(DWORD i)
  598. { IRTLASSERT(i < SEGSIZE); return m_bktSlots[i]; }
  599. #ifdef LKRDEBUG
  600. CMediumSegment()
  601. {
  602. // IRTLASSERT(((ULONG_PTR) this & (LKHASH_MEM_DEFAULT_ALIGN-1)) == 0);
  603. IRTLASSERT(sizeof(*this) == SEGSIZE * sizeof(CBucket) + sizeof(void*));
  604. }
  605. #endif // LKRDEBUG
  606. LKHASH_ALLOCATOR_DEFINITIONS(CMediumSegment);
  607. };
  608. // Large-sized segments contain 2^9 = 512 buckets => ~32Kb
  609. class CLargeSegment : public CSegment
  610. {
  611. public:
  612. enum {
  613. SEGBITS = 9,
  614. SEGSIZE = (1<<SEGBITS),
  615. SEGMASK = (SEGSIZE-1),
  616. INITSIZE = 4 * SEGSIZE,
  617. };
  618. private:
  619. CBucket m_bktSlots[SEGSIZE];
  620. public:
  621. virtual ~CLargeSegment() {}
  622. virtual DWORD Bits() const { return SEGBITS; }
  623. virtual DWORD Size() const { return SEGSIZE; }
  624. virtual DWORD Mask() const { return SEGMASK; }
  625. virtual DWORD InitSize() const { return INITSIZE;}
  626. virtual CBucket& Slot(DWORD i)
  627. { IRTLASSERT(i < SEGSIZE); return m_bktSlots[i]; }
  628. #ifdef LKRDEBUG
  629. CLargeSegment()
  630. {
  631. // IRTLASSERT(((ULONG_PTR) this & (LKHASH_MEM_DEFAULT_ALIGN-1)) == 0);
  632. IRTLASSERT(sizeof(*this) == SEGSIZE * sizeof(CBucket) + sizeof(void*));
  633. }
  634. #endif // LKRDEBUG
  635. LKHASH_ALLOCATOR_DEFINITIONS(CLargeSegment);
  636. };
  637. // A directory keeps track of the segments comprising the hash table.
  638. // The directory is just a variable-sized array of pointers to
  639. // segments (CDirEntrys).
  640. class CDirEntry
  641. {
  642. public:
  643. // MIN_DIRSIZE and MAX_DIRSIZE can be changed independently
  644. // of anything else. Should be powers of two.
  645. enum {
  646. MIN_DIRSIZE = (1<<3), // minimum directory size
  647. MAX_DIRSIZE = (1<<16), // maximum directory size
  648. };
  649. CSegment* m_pseg;
  650. CDirEntry()
  651. : m_pseg(NULL)
  652. {}
  653. ~CDirEntry()
  654. { delete m_pseg; }
  655. };
  656. public:
  657. // aliases for convenience
  658. enum {
  659. NODES_PER_CLUMP = CNodeClump::NODES_PER_CLUMP,
  660. MIN_DIRSIZE = CDirEntry::MIN_DIRSIZE,
  661. MAX_DIRSIZE = CDirEntry::MAX_DIRSIZE,
  662. NAME_SIZE = 16,
  663. };
  664. private:
  665. // Miscellaneous helper functions
  666. // Convert a hash signature to a bucket address
  667. DWORD _BucketAddress(DWORD dwSignature) const
  668. {
  669. DWORD dwBktAddr = _H0(dwSignature);
  670. // Has this bucket been split already?
  671. if (dwBktAddr < m_iExpansionIdx)
  672. dwBktAddr = _H1(dwSignature);
  673. IRTLASSERT(dwBktAddr < m_cActiveBuckets);
  674. IRTLASSERT(dwBktAddr < (m_cDirSegs << m_dwSegBits));
  675. return dwBktAddr;
  676. }
  677. // See the Linear Hashing paper
  678. DWORD _H0(DWORD dwSignature) const
  679. { return dwSignature & m_dwBktAddrMask; }
  680. // See the Linear Hashing paper. Preserves one bit more than _H0.
  681. DWORD _H1(DWORD dwSignature) const
  682. { return dwSignature & ((m_dwBktAddrMask << 1) | 1); }
  683. // In which segment within the directory does the bucketaddress lie?
  684. // (Return type must be lvalue so that it can be assigned to.)
  685. CSegment*& _Segment(DWORD dwBucketAddr) const
  686. {
  687. const DWORD iSeg = dwBucketAddr >> m_dwSegBits;
  688. IRTLASSERT(m_paDirSegs != NULL && iSeg < m_cDirSegs);
  689. return m_paDirSegs[iSeg].m_pseg;
  690. }
  691. // Offset within the segment of the bucketaddress
  692. DWORD _SegIndex(DWORD dwBucketAddr) const
  693. { return (dwBucketAddr & m_dwSegMask); }
  694. // Convert a bucketaddress to a CBucket*
  695. CBucket* _Bucket(DWORD dwBucketAddr) const
  696. {
  697. IRTLASSERT(dwBucketAddr < m_cActiveBuckets);
  698. CSegment* const pseg = _Segment(dwBucketAddr);
  699. IRTLASSERT(pseg != NULL);
  700. return &(pseg->Slot(_SegIndex(dwBucketAddr)));
  701. }
  702. // Extract the key from a record
  703. const void* _ExtractKey(const void* pvRecord) const
  704. {
  705. IRTLASSERT(pvRecord != NULL);
  706. IRTLASSERT(m_pfnExtractKey != NULL);
  707. return (pvRecord != NULL) ? (*m_pfnExtractKey)(pvRecord) : NULL;
  708. }
  709. // Hash a key
  710. DWORD _CalcKeyHash(const void* pvKey) const
  711. {
  712. // Note pvKey==0 is acceptable, as the real key type could be an int
  713. IRTLASSERT(m_pfnCalcKeyHash != NULL);
  714. DWORD dwHash = (*m_pfnCalcKeyHash)(pvKey);
  715. // We forcibly scramble the result to help ensure a better distribution
  716. return HashScramble(dwHash);
  717. }
  718. // Compare two keys for equality
  719. bool _EqualKeys(const void* pvKey1, const void* pvKey2) const
  720. {
  721. IRTLASSERT(m_pfnEqualKeys != NULL);
  722. return (*m_pfnEqualKeys)(pvKey1, pvKey2);
  723. }
  724. // AddRef or Release a record.
  725. void _AddRefRecord(const void* pvRecord, int nIncr) const
  726. {
  727. IRTLASSERT(pvRecord != NULL && (nIncr == -1 || nIncr == +1));
  728. if (m_pfnAddRefRecord != NULL && pvRecord != NULL)
  729. (*m_pfnAddRefRecord)(pvRecord, nIncr);
  730. }
  731. // We won't expose the locking mechanism. If a wrapper class needs to
  732. // expose a global lock (not recommended), it can provide its own lock.
  733. // Lock the table (exclusively) for writing
  734. void _WriteLock()
  735. { m_Lock.WriteLock(); }
  736. // Lock the table (possibly shared) for reading
  737. void _ReadLock() const
  738. { m_Lock.ReadLock(); }
  739. // Unlock the table for writing
  740. void _WriteUnlock() const
  741. { m_Lock.WriteUnlock(); }
  742. // Unlock the table for reading
  743. void _ReadUnlock() const
  744. { m_Lock.ReadUnlock(); }
  745. // Is the table already locked for writing?
  746. bool _IsWriteLocked() const
  747. { return m_Lock.IsWriteLocked(); }
  748. // Is the table already locked for reading?
  749. bool _IsReadLocked() const
  750. { return m_Lock.IsReadLocked(); }
  751. // Is the table unlocked for writing?
  752. bool _IsWriteUnlocked() const
  753. { return m_Lock.IsWriteUnlocked(); }
  754. // Is the table unlocked for reading?
  755. bool _IsReadUnlocked() const
  756. { return m_Lock.IsReadUnlocked(); }
  757. // Set the spin count on the table lock
  758. void _SetSpinCount(WORD wSpins)
  759. { m_Lock.SetSpinCount(wSpins); }
  760. // Get the spin count on the table lock
  761. WORD _GetSpinCount() const
  762. { return m_Lock.GetSpinCount(); }
  763. #ifdef LOCK_INSTRUMENTATION
  764. static LONG sm_cTables;
  765. static const char*
  766. _LockName()
  767. {
  768. LONG l = ++sm_cTables;
  769. // possible race condition but we don't care, as this is never
  770. // used in production code
  771. static char s_szName[CLockStatistics::L_NAMELEN];
  772. wsprintf(s_szName, "LH%05x", 0xFFFFF & l);
  773. return s_szName;
  774. }
  775. // Statistics for the table lock
  776. CLockStatistics _LockStats() const
  777. { return m_Lock.Statistics(); }
  778. #endif // LOCK_INSTRUMENTATION
  779. private:
  780. // Fields are ordered so as to minimize number of cache lines touched
  781. DWORD m_dwSignature; // debugging: id & corruption check
  782. mutable TableLock m_Lock; // Lock on entire linear hash table
  783. DWORD m_dwBktAddrMask; // mask used for address calculation
  784. DWORD m_iExpansionIdx; // address of next bucket to be expanded
  785. CDirEntry* m_paDirSegs; // directory of table segments
  786. // State variables
  787. LK_TABLESIZE m_lkts; // "size" of table: small, medium, or large
  788. DWORD m_dwSegBits; // C{Small,Medium,Large}Segment::SEGBITS
  789. DWORD m_dwSegSize; // C{Small,Medium,Large}Segment::SEGSIZE
  790. DWORD m_dwSegMask; // C{Small,Medium,Large}Segment::SEGMASK
  791. LK_RETCODE m_lkrcState; // Internal state of table
  792. double m_MaxLoad; // max load factor (average chain length)
  793. DWORD m_nLevel; // number of table doublings performed
  794. DWORD m_cDirSegs; // segment directory size: varies between
  795. // MIN_DIRSIZE and MAX_DIRSIZE
  796. DWORD m_cRecords; // number of records in the table
  797. DWORD m_cActiveBuckets; // number of buckets in use (table size)
  798. WORD m_wBucketLockSpins;// default spin count for bucket locks
  799. // type-specific function pointers
  800. PFnExtractKey m_pfnExtractKey; // Extract key from record
  801. PFnCalcKeyHash m_pfnCalcKeyHash; // Calculate hash signature of key
  802. PFnEqualKeys m_pfnEqualKeys; // Compare two keys
  803. PFnAddRefRecord m_pfnAddRefRecord; // AddRef a record
  804. CHAR m_szName[NAME_SIZE]; // an identifier for debugging
  805. // Non-trivial implementation functions
  806. LK_RETCODE _InsertRecord(const void* pvRecord, DWORD dwSignature,
  807. bool fOverwrite);
  808. LK_RETCODE _DeleteKey(const void* pvKey, DWORD dwSignature);
  809. LK_RETCODE _DeleteRecord(const void* pvRecord, DWORD dwSignature);
  810. bool _DeleteNode(CBucket* pbkt, CNodeClump*& rpnc,
  811. CNodeClump*& rpncPrev, DWORD& riNode);
  812. LK_RETCODE _FindKey(const void* pvKey, DWORD dwSignature,
  813. const void** ppvRecord) const;
  814. LK_RETCODE _FindRecord(const void* pvRecord, DWORD dwSignature) const;
  815. // Predicate functions
  816. static LK_PREDICATE _PredTrue(const void* /*pvRecord*/, void* /*pvState*/)
  817. { return LKP_PERFORM; }
  818. DWORD _Apply(PFnRecordAction pfnAction, void* pvState,
  819. LK_LOCKTYPE lkl, LK_PREDICATE& rlkp);
  820. DWORD _ApplyIf(PFnRecordPred pfnPredicate,
  821. PFnRecordAction pfnAction, void* pvState,
  822. LK_LOCKTYPE lkl, LK_PREDICATE& rlkp);
  823. DWORD _DeleteIf(PFnRecordPred pfnPredicate, void* pvState,
  824. LK_PREDICATE& rlkp);
  825. void _Clear(bool fShrinkDirectory);
  826. void _SetSegVars(LK_TABLESIZE lkts);
  827. CSegment* _NewSeg() const;
  828. CBucket* _FindBucket(DWORD dwSignature, bool fLockForWrite) const;
  829. LK_RETCODE _Expand();
  830. LK_RETCODE _Contract();
  831. LK_RETCODE _SplitRecordSet(CNodeClump* pncOldTarget,
  832. CNodeClump* pncNewTarget,
  833. DWORD iExpansionIdx,
  834. DWORD dwNewBkt);
  835. LK_RETCODE _MergeRecordSets(CBucket* pbktNewTarget,
  836. CNodeClump* pncOldList);
  837. // Private copy ctor and op= to prevent compiler synthesizing them.
  838. // Must provide a (bad) implementation because we export instantiations.
  839. // TODO: implement these properly; they could be useful.
  840. CLKLinearHashTable(const CLKLinearHashTable&)
  841. #ifdef LOCK_INSTRUMENTATION
  842. : m_Lock(NULL)
  843. #endif // LOCK_INSTRUMENTATION
  844. {*(BYTE*)NULL;}
  845. CLKLinearHashTable& operator=(const CLKLinearHashTable&)
  846. {return *(CLKLinearHashTable*)NULL;}
  847. public:
  848. CLKLinearHashTable(
  849. LPCSTR pszName, // An identifier for debugging
  850. PFnExtractKey pfnExtractKey, // Extract key from record
  851. PFnCalcKeyHash pfnCalcKeyHash, // Calculate hash signature of key
  852. PFnEqualKeys pfnEqualKeys, // Compare two keys
  853. PFnAddRefRecord pfnAddRefRecord=NULL, // AddRef in FindKey, etc
  854. double maxload=LK_DFLT_MAXLOAD, // Upperbound on average chain length
  855. DWORD initsize=LK_DFLT_INITSIZE, // Initial size of hash table.
  856. DWORD num_subtbls=LK_DFLT_NUM_SUBTBLS // for signature compatiblity
  857. // with CLKHashTable
  858. );
  859. ~CLKLinearHashTable();
  860. static const char* ClassName() {return "CLKLinearHashTable";}
  861. int NumSubTables() const {return 1;}
  862. static LK_TABLESIZE NumSubTables(DWORD& rinitsize, DWORD& rnum_subtbls);
  863. // Insert a new record into hash table.
  864. // Returns LK_SUCCESS if all OK, LK_KEY_EXISTS if same key already
  865. // exists (unless fOverwrite), LK_ALLOC_FAIL if out of space,
  866. // or LK_BAD_RECORD for a bad record.
  867. LK_RETCODE InsertRecord(const void* pvRecord, bool fOverwrite=false)
  868. { return _InsertRecord(pvRecord, _CalcKeyHash(_ExtractKey(pvRecord)),
  869. fOverwrite);
  870. }
  871. // Delete record with the given key.
  872. // Returns LK_SUCCESS if all OK, or LK_NO_SUCH_KEY if not found
  873. LK_RETCODE DeleteKey(const void* pvKey)
  874. { return _DeleteKey(pvKey, _CalcKeyHash(pvKey)); }
  875. // Delete a record from the table, if present.
  876. // Returns LK_SUCCESS if all OK, or LK_NO_SUCH_KEY if not found
  877. LK_RETCODE DeleteRecord(const void* pvRecord)
  878. { return _DeleteRecord(pvRecord, _CalcKeyHash(_ExtractKey(pvRecord))); }
  879. // Find record with given key.
  880. // Returns: LK_SUCCESS, if record found (record is returned in *ppvRecord)
  881. // LK_BAD_RECORD, if ppvRecord is invalid
  882. // LK_NO_SUCH_KEY, if no record with given key value was found
  883. // LK_UNUSABLE, if hash table not in usable state
  884. // Note: the record is AddRef'd. You must decrement the reference
  885. // count when you are finished with the record (if you're implementing
  886. // refcounting semantics).
  887. LK_RETCODE FindKey(const void* pvKey,
  888. const void** ppvRecord) const
  889. { return _FindKey(pvKey, _CalcKeyHash(pvKey), ppvRecord); }
  890. // Sees if the record is contained in the table
  891. // Returns: LK_SUCCESS, if record found
  892. // LK_BAD_RECORD, if pvRecord is invalid
  893. // LK_NO_SUCH_KEY, if record is not in the table
  894. // LK_UNUSABLE, if hash table not in usable state
  895. // Note: the record is *not* AddRef'd.
  896. LK_RETCODE FindRecord(const void* pvRecord) const
  897. { return _FindRecord(pvRecord, _CalcKeyHash(_ExtractKey(pvRecord))); }
  898. // Walk the hash table, applying pfnAction to all records.
  899. // Locks the whole table for the duration with either a (possibly
  900. // shared) readlock or a writelock, according to lkl.
  901. // Loop is aborted if pfnAction returns LKA_ABORT.
  902. // Returns the number of successful applications.
  903. DWORD Apply(PFnRecordAction pfnAction,
  904. void* pvState=NULL,
  905. LK_LOCKTYPE lkl=LKL_READLOCK);
  906. // Walk the hash table, applying pfnAction to any records that match
  907. // pfnPredicate. Locks the whole table for the duration with either
  908. // a (possibly shared) readlock or a writelock, according to lkl.
  909. // Loop is aborted if pfnAction returns LKA_ABORT.
  910. // Returns the number of successful applications.
  911. DWORD ApplyIf(PFnRecordPred pfnPredicate,
  912. PFnRecordAction pfnAction,
  913. void* pvState=NULL,
  914. LK_LOCKTYPE lkl=LKL_READLOCK);
  915. // Delete any records that match pfnPredicate.
  916. // Locks the table for the duration with a writelock.
  917. // Returns the number of deletions.
  918. //
  919. // Do *not* walk the hash table by hand with an iterator and call
  920. // DeleteKey. The iterator will end up pointing to garbage.
  921. DWORD DeleteIf(PFnRecordPred pfnPredicate,
  922. void* pvState=NULL);
  923. // Check table for consistency. Returns 0 if okay, or the number of
  924. // errors otherwise.
  925. int CheckTable() const;
  926. // Prints the table (to where??)
  927. void Print() const;
  928. // Remove all data from the table
  929. void Clear()
  930. {
  931. _WriteLock();
  932. _Clear(true);
  933. _WriteUnlock();
  934. }
  935. // Number of elements in the table
  936. DWORD Size() const
  937. { return m_cRecords; }
  938. // Maximum possible number of elements in the table
  939. DWORD MaxSize() const
  940. { return static_cast<DWORD>(m_MaxLoad * MAX_DIRSIZE * m_dwSegSize); }
  941. // Get hash table statistics
  942. CLKHashTableStats GetStatistics() const;
  943. // Is the hash table consistent and correct?
  944. bool IsValid() const
  945. {
  946. return (m_lkrcState == LK_SUCCESS // serious internal failure?
  947. && m_paDirSegs != NULL
  948. && (MIN_DIRSIZE & (MIN_DIRSIZE-1)) == 0 // == (1 << N)
  949. && (MAX_DIRSIZE & (MAX_DIRSIZE-1)) == 0
  950. && MAX_DIRSIZE > MIN_DIRSIZE
  951. && MIN_DIRSIZE <= m_cDirSegs && m_cDirSegs <= MAX_DIRSIZE
  952. && (m_cDirSegs & (m_cDirSegs-1)) == 0
  953. && m_pfnExtractKey != NULL
  954. && m_pfnCalcKeyHash != NULL
  955. && m_pfnEqualKeys != NULL
  956. && m_cActiveBuckets > 0
  957. && ValidSignature()
  958. );
  959. }
  960. void SetTableLockSpinCount(WORD wSpins)
  961. { _SetSpinCount(wSpins); }
  962. WORD GetTableLockSpinCount()
  963. { return _GetSpinCount(); }
  964. void SetBucketLockSpinCount(WORD wSpins);
  965. WORD GetBucketLockSpinCount();
  966. enum {
  967. SIGNATURE = (('L') | ('K' << 8) | ('L' << 16) | ('H' << 24)),
  968. SIGNATURE_FREE = (('L') | ('K' << 8) | ('L' << 16) | ('x' << 24)),
  969. };
  970. bool
  971. ValidSignature() const
  972. { return m_dwSignature == SIGNATURE;}
  973. // LKHASH_ALLOCATOR_DEFINITIONS(CLKLinearHashTable);
  974. public:
  975. // Iterators can be used to walk the table. To ensure a consistent
  976. // view of the data, the iterator locks the whole table. This can
  977. // have a negative effect upon performance, because no other thread
  978. // can do anything with the table. Use with care.
  979. //
  980. // You should not use an iterator to walk the table, calling DeleteKey,
  981. // as the iterator will end up pointing to garbage.
  982. //
  983. // Use Apply, ApplyIf, or DeleteIf instead of iterators to safely
  984. // walk the tree.
  985. //
  986. // Note that iterators acquire a reference to the record pointed to
  987. // and release that reference as soon as the iterator is incremented.
  988. // In other words, this code is safe:
  989. // lkrc = ht.IncrementIterator(&iter);
  990. // // assume lkrc == LK_SUCCESS for the sake of this example
  991. // CMyHashTable::Record* pRec = iter.Record();
  992. // Foo(pRec); // uses pRec but doesn't hang on to it
  993. // lkrc = ht.IncrementIterator(&iter);
  994. //
  995. // But this code is not because pRec is used out of the scope of the
  996. // iterator that provided it:
  997. // lkrc = ht.IncrementIterator(&iter);
  998. // CMyHashTable::Record* pRec = iter.Record();
  999. // // BUGBUG: Should call ht.AddRefRecord(pRec, +1) here
  1000. // lkrc = ht.IncrementIterator(&iter);
  1001. // Foo(pRec);
  1002. //
  1003. // If record has no reference-counting semantics, then you can ignore
  1004. // the above remarks about scope.
  1005. class CIterator
  1006. {
  1007. protected:
  1008. friend class CLKLinearHashTable;
  1009. CLKLinearHashTable* m_plht; // which linear hash table?
  1010. DWORD m_dwBucketAddr; // bucket index
  1011. CNodeClump* m_pnc; // a CNodeClump in bucket
  1012. int m_iNode; // offset within m_pnc
  1013. LK_LOCKTYPE m_lkl; // readlock or writelock?
  1014. private:
  1015. // Private copy ctor and op= to prevent compiler synthesizing them.
  1016. // Must provide (bad) implementation because we export instantiations.
  1017. CIterator(const CIterator&) {*(BYTE*)NULL;}
  1018. CIterator& operator=(const CIterator&) {return *(CIterator*)NULL;}
  1019. public:
  1020. CIterator(
  1021. LK_LOCKTYPE lkl=LKL_WRITELOCK)
  1022. : m_plht(NULL),
  1023. m_dwBucketAddr(0),
  1024. m_pnc(NULL),
  1025. m_iNode(-1),
  1026. m_lkl(lkl)
  1027. {}
  1028. // Return the record associated with this iterator
  1029. const void* Record() const
  1030. {
  1031. IRTLASSERT(IsValid());
  1032. return ((m_pnc != NULL
  1033. && m_iNode >= 0 && m_iNode < CLKLinearHashTable::NODES_PER_CLUMP)
  1034. ? m_pnc->m_pvNode[m_iNode]
  1035. : NULL);
  1036. }
  1037. // Return the key associated with this iterator
  1038. const void* Key() const
  1039. {
  1040. IRTLASSERT(m_plht != NULL);
  1041. const void* pRec = Record();
  1042. return ((pRec != NULL && m_plht != NULL)
  1043. ? m_plht->_ExtractKey(pRec)
  1044. : NULL);
  1045. }
  1046. bool IsValid() const
  1047. {
  1048. return ((m_plht != NULL)
  1049. && (m_pnc != NULL)
  1050. && (0 <= m_iNode && m_iNode < CLKLinearHashTable::NODES_PER_CLUMP)
  1051. && (m_pnc->m_pvNode[m_iNode] != NULL));
  1052. }
  1053. // Delete the record that the iterator points to. Does an implicit
  1054. // IncrementIterator after deletion.
  1055. LK_RETCODE DeleteRecord();
  1056. // Change the record that the iterator points to. The new record
  1057. // must have the same key as the old one.
  1058. LK_RETCODE ChangeRecord(const void* pNewRec);
  1059. };
  1060. // Const iterators for readonly access. You must use these with
  1061. // const CLKLinearHashTables.
  1062. class CConstIterator : public CIterator
  1063. {
  1064. private:
  1065. // Private, unimplemented copy ctor and op= to prevent
  1066. // compiler synthesizing them.
  1067. CConstIterator(const CConstIterator&);
  1068. CConstIterator& operator=(const CConstIterator&);
  1069. public:
  1070. CConstIterator()
  1071. : CIterator(LKL_READLOCK)
  1072. {}
  1073. };
  1074. private:
  1075. // The public APIs lock the table. The private ones, which are used
  1076. // directly by CLKHashTable, don't.
  1077. LK_RETCODE _InitializeIterator(CIterator* piter);
  1078. LK_RETCODE _CloseIterator(CIterator* piter);
  1079. public:
  1080. // Initialize the iterator to point to the first item in the hash table
  1081. // Returns LK_SUCCESS, LK_NO_MORE_ELEMENTS, or LK_BAD_ITERATOR.
  1082. LK_RETCODE InitializeIterator(CIterator* piter)
  1083. {
  1084. IRTLASSERT(piter != NULL && piter->m_plht == NULL);
  1085. if (piter == NULL || piter->m_plht != NULL)
  1086. return LK_BAD_ITERATOR;
  1087. if (piter->m_lkl == LKL_WRITELOCK)
  1088. _WriteLock();
  1089. else
  1090. _ReadLock();
  1091. return _InitializeIterator(piter);
  1092. }
  1093. // The const iterator version
  1094. LK_RETCODE InitializeIterator(CConstIterator* piter) const
  1095. {
  1096. IRTLASSERT(piter != NULL && piter->m_plht == NULL);
  1097. IRTLASSERT(piter->m_lkl != LKL_WRITELOCK);
  1098. if (piter == NULL || piter->m_plht != NULL
  1099. || piter->m_lkl == LKL_WRITELOCK)
  1100. return LK_BAD_ITERATOR;
  1101. _ReadLock();
  1102. return const_cast<CLKLinearHashTable*>(this)
  1103. ->_InitializeIterator(static_cast<CIterator*>(piter));
  1104. }
  1105. // Move the iterator on to the next item in the table.
  1106. // Returns LK_SUCCESS, LK_NO_MORE_ELEMENTS, or LK_BAD_ITERATOR.
  1107. LK_RETCODE IncrementIterator(CIterator* piter);
  1108. LK_RETCODE IncrementIterator(CConstIterator* piter) const
  1109. {
  1110. IRTLASSERT(piter != NULL && piter->m_plht == this);
  1111. IRTLASSERT(piter->m_lkl != LKL_WRITELOCK);
  1112. if (piter == NULL || piter->m_plht != this
  1113. || piter->m_lkl == LKL_WRITELOCK)
  1114. return LK_BAD_ITERATOR;
  1115. return const_cast<CLKLinearHashTable*>(this)
  1116. ->IncrementIterator(static_cast<CIterator*>(piter));
  1117. }
  1118. // Close the iterator.
  1119. LK_RETCODE CloseIterator(CIterator* piter)
  1120. {
  1121. IRTLASSERT(piter != NULL && piter->m_plht == this);
  1122. if (piter == NULL || piter->m_plht != this)
  1123. return LK_BAD_ITERATOR;
  1124. _CloseIterator(piter);
  1125. if (piter->m_lkl == LKL_WRITELOCK)
  1126. _WriteUnlock();
  1127. else
  1128. _ReadUnlock();
  1129. return LK_SUCCESS;
  1130. };
  1131. // Close the CConstIterator
  1132. LK_RETCODE CloseIterator(CConstIterator* piter) const
  1133. {
  1134. IRTLASSERT(piter != NULL && piter->m_plht == this);
  1135. IRTLASSERT(piter->m_lkl != LKL_WRITELOCK);
  1136. if (piter == NULL || piter->m_plht != this
  1137. || piter->m_lkl == LKL_WRITELOCK)
  1138. return LK_BAD_ITERATOR;
  1139. const_cast<CLKLinearHashTable*>(this)
  1140. ->_CloseIterator(static_cast<CIterator*>(piter));
  1141. _ReadUnlock();
  1142. return LK_SUCCESS;
  1143. };
  1144. };
  1145. //--------------------------------------------------------------------
  1146. // CLKHashTable
  1147. //
  1148. // To improve concurrency, a hash table is divided into a number of
  1149. // (independent) subtables. Each subtable is a linear hash table. The
  1150. // number of subtables is defined when the table is created and remains
  1151. // fixed thereafter. Records are assigned to subtables based on their
  1152. // hashed key.
  1153. //
  1154. // For small or low-contention hashtables, you can bypass this
  1155. // thin wrapper and use CLKLinearHashTable directly. The methods are
  1156. // documented in the declarations for CLKHashTable (above).
  1157. //--------------------------------------------------------------------
  1158. class IRTL_DLLEXP CLKHashTable
  1159. {
  1160. private:
  1161. typedef CLKLinearHashTable SubTable;
  1162. public:
  1163. typedef SubTable::TableLock TableLock;
  1164. typedef SubTable::BucketLock BucketLock;
  1165. class CIterator;
  1166. friend class CLKHashTable::CIterator;
  1167. private:
  1168. enum {
  1169. NAME_SIZE = SubTable::NAME_SIZE,
  1170. };
  1171. // Hash table parameters
  1172. DWORD m_dwSignature; // debugging: id & corruption check
  1173. DWORD m_cSubTables; // number of subtables
  1174. SubTable** m_palhtDir; // array of subtables
  1175. // type-specific function pointers
  1176. PFnExtractKey m_pfnExtractKey;
  1177. PFnCalcKeyHash m_pfnCalcKeyHash;
  1178. LK_RETCODE m_lkrcState; // Internal state of table
  1179. CHAR m_szName[NAME_SIZE]; // an identifier for debugging
  1180. LKHASH_GLOBAL_LOCK_DECLARATIONS();
  1181. // Private copy ctor and op= to prevent compiler synthesizing them.
  1182. // Must provide a (bad) implementation because we export instantiations.
  1183. // TODO: implement these properly; they could be useful.
  1184. CLKHashTable(const CLKHashTable&) {*(BYTE*)NULL;}
  1185. CLKHashTable& operator=(const CLKHashTable&) {return *(CLKHashTable*)NULL;}
  1186. // Extract the key from the record
  1187. const void* _ExtractKey(const void* pvRecord) const
  1188. {
  1189. IRTLASSERT(pvRecord != NULL);
  1190. IRTLASSERT(m_pfnExtractKey != NULL);
  1191. return (*m_pfnExtractKey)(pvRecord);
  1192. }
  1193. // Hash the key
  1194. DWORD _CalcKeyHash(const void* pvKey) const
  1195. {
  1196. // Note pvKey==0 is acceptable, as the real key type could be an int
  1197. IRTLASSERT(m_pfnCalcKeyHash != NULL);
  1198. DWORD dwHash = (*m_pfnCalcKeyHash)(pvKey);
  1199. // We forcibly scramble the result to help ensure a better distribution
  1200. return HashScramble(dwHash);
  1201. }
  1202. // Use the key's hash signature to multiplex into a subtable
  1203. SubTable* _SubTable(DWORD dwSignature) const
  1204. {
  1205. IRTLASSERT(m_lkrcState == LK_SUCCESS
  1206. && m_palhtDir != NULL && m_cSubTables > 0);
  1207. if (m_lkrcState == LK_SUCCESS)
  1208. {
  1209. const DWORD PRIME = 1048583UL; // used to scramble the hash sig
  1210. DWORD index = (dwSignature % PRIME) % m_cSubTables;
  1211. return m_palhtDir[index];
  1212. }
  1213. else
  1214. return NULL;
  1215. }
  1216. void _WriteLock();
  1217. void _ReadLock() const;
  1218. void _WriteUnlock() const;
  1219. void _ReadUnlock() const;
  1220. public:
  1221. CLKHashTable(
  1222. LPCSTR pszName, // An identifier for debugging
  1223. PFnExtractKey pfnExtractKey, // Extract key from record
  1224. PFnCalcKeyHash pfnCalcKeyHash, // Calculate hash signature of key
  1225. PFnEqualKeys pfnEqualKeys, // Compare two keys
  1226. PFnAddRefRecord pfnAddRefRecord=NULL, // AddRef in FindKey, etc
  1227. double maxload=LK_DFLT_MAXLOAD, // bound on avg chain length
  1228. DWORD initsize=LK_DFLT_INITSIZE, // Initial size of hash table.
  1229. DWORD num_subtbls=LK_DFLT_NUM_SUBTBLS // #subordinate hash tables.
  1230. );
  1231. ~CLKHashTable();
  1232. static const char* ClassName() {return "CLKHashTable";}
  1233. int NumSubTables() const {return m_cSubTables;}
  1234. static LK_TABLESIZE NumSubTables(DWORD& rinitsize, DWORD& rnum_subtbls);
  1235. // Thin wrappers for the corresponding methods in CLKLinearHashTable
  1236. LK_RETCODE InsertRecord(const void* pvRecord, bool fOverwrite=false)
  1237. {
  1238. LKHASH_GLOBAL_WRITE_LOCK(); // usu. no-op
  1239. DWORD hash_val = _CalcKeyHash(_ExtractKey(pvRecord));
  1240. SubTable* const pst = _SubTable(hash_val);
  1241. LK_RETCODE lk = (pst != NULL
  1242. ? pst->_InsertRecord(pvRecord, hash_val,
  1243. fOverwrite)
  1244. : LK_UNUSABLE);
  1245. LKHASH_GLOBAL_WRITE_UNLOCK(); // usu. no-op
  1246. return lk;
  1247. }
  1248. LK_RETCODE DeleteKey(const void* pvKey)
  1249. {
  1250. LKHASH_GLOBAL_WRITE_LOCK(); // usu. no-op
  1251. DWORD hash_val = _CalcKeyHash(pvKey);
  1252. SubTable* const pst = _SubTable(hash_val);
  1253. LK_RETCODE lk = (pst != NULL
  1254. ? pst->_DeleteKey(pvKey, hash_val)
  1255. : LK_UNUSABLE);
  1256. LKHASH_GLOBAL_WRITE_UNLOCK(); // usu. no-op
  1257. return lk;
  1258. }
  1259. LK_RETCODE DeleteRecord(const void* pvRecord)
  1260. {
  1261. LKHASH_GLOBAL_WRITE_LOCK(); // usu. no-op
  1262. DWORD hash_val = _CalcKeyHash(_ExtractKey(pvRecord));
  1263. SubTable* const pst = _SubTable(hash_val);
  1264. LK_RETCODE lk = (pst != NULL
  1265. ? pst->_DeleteRecord(pvRecord, hash_val)
  1266. : LK_UNUSABLE);
  1267. LKHASH_GLOBAL_WRITE_UNLOCK(); // usu. no-op
  1268. return lk;
  1269. }
  1270. LK_RETCODE FindKey(const void* pvKey,
  1271. const void** ppvRecord) const
  1272. {
  1273. LKHASH_GLOBAL_READ_LOCK(); // usu. no-op
  1274. DWORD hash_val = _CalcKeyHash(pvKey);
  1275. SubTable* const pst = _SubTable(hash_val);
  1276. LK_RETCODE lkrc = (pst != NULL
  1277. ? pst->_FindKey(pvKey, hash_val, ppvRecord)
  1278. : LK_UNUSABLE);
  1279. LKHASH_GLOBAL_READ_UNLOCK(); // usu. no-op
  1280. return lkrc;
  1281. }
  1282. LK_RETCODE FindRecord(const void* pvRecord) const
  1283. {
  1284. LKHASH_GLOBAL_READ_LOCK(); // usu. no-op
  1285. DWORD hash_val = _CalcKeyHash(_ExtractKey(pvRecord));
  1286. SubTable* const pst = _SubTable(hash_val);
  1287. LK_RETCODE lkrc = (pst != NULL
  1288. ? pst->_FindRecord(pvRecord, hash_val)
  1289. : LK_UNUSABLE);
  1290. LKHASH_GLOBAL_READ_UNLOCK(); // usu. no-op
  1291. return lkrc;
  1292. }
  1293. DWORD Apply(PFnRecordAction pfnAction,
  1294. void* pvState=NULL,
  1295. LK_LOCKTYPE lkl=LKL_READLOCK);
  1296. DWORD ApplyIf(PFnRecordPred pfnPredicate,
  1297. PFnRecordAction pfnAction,
  1298. void* pvState=NULL,
  1299. LK_LOCKTYPE lkl=LKL_READLOCK);
  1300. DWORD DeleteIf(PFnRecordPred pfnPredicate,
  1301. void* pvState=NULL);
  1302. void Clear();
  1303. int CheckTable() const;
  1304. void Print() const;
  1305. DWORD Size() const;
  1306. DWORD MaxSize() const;
  1307. CLKHashTableStats GetStatistics() const;
  1308. bool IsValid() const;
  1309. void SetTableLockSpinCount(WORD wSpins);
  1310. WORD GetTableLockSpinCount();
  1311. void SetBucketLockSpinCount(WORD wSpins);
  1312. WORD GetBucketLockSpinCount();
  1313. enum {
  1314. SIGNATURE = (('L') | ('K' << 8) | ('H' << 16) | ('T' << 24)),
  1315. SIGNATURE_FREE = (('L') | ('K' << 8) | ('H' << 16) | ('x' << 24)),
  1316. };
  1317. bool
  1318. ValidSignature() const
  1319. { return m_dwSignature == SIGNATURE;}
  1320. // LKHASH_ALLOCATOR_DEFINITIONS(CLKHashTable);
  1321. public:
  1322. typedef SubTable::CIterator CLHTIterator;
  1323. class CIterator : public CLHTIterator
  1324. {
  1325. protected:
  1326. friend class CLKHashTable;
  1327. CLKHashTable* m_pht; // which hash table?
  1328. int m_ist; // which subtable
  1329. private:
  1330. // Private copy ctor and op= to prevent compiler synthesizing them.
  1331. // Must provide (bad) implementation because we export instantiations.
  1332. CIterator(const CIterator&) {*(BYTE*)NULL;}
  1333. CIterator& operator=(const CIterator&) {return *(CIterator*)NULL;}
  1334. public:
  1335. CIterator(
  1336. LK_LOCKTYPE lkl=LKL_WRITELOCK)
  1337. : CLHTIterator(lkl),
  1338. m_pht(NULL),
  1339. m_ist(-1)
  1340. {}
  1341. const void* Record() const
  1342. {
  1343. IRTLASSERT(IsValid());
  1344. // This is a hack to work around a compiler bug. Calling
  1345. // CLHTIterator::Record calls this function recursively until
  1346. // the stack overflows.
  1347. const CLHTIterator* pBase = static_cast<const CLHTIterator*>(this);
  1348. return pBase->Record();
  1349. }
  1350. const void* Key() const
  1351. {
  1352. IRTLASSERT(IsValid());
  1353. const CLHTIterator* pBase = static_cast<const CLHTIterator*>(this);
  1354. return pBase->Key();
  1355. }
  1356. bool IsValid() const
  1357. {
  1358. const CLHTIterator* pBase = static_cast<const CLHTIterator*>(this);
  1359. return (m_pht != NULL && m_ist >= 0 && pBase->IsValid());
  1360. }
  1361. };
  1362. // Const iterators for readonly access
  1363. class CConstIterator : public CIterator
  1364. {
  1365. private:
  1366. // Private, unimplemented copy ctor and op= to prevent
  1367. // compiler synthesizing them.
  1368. CConstIterator(const CConstIterator&);
  1369. CConstIterator& operator=(const CConstIterator&);
  1370. public:
  1371. CConstIterator()
  1372. : CIterator(LKL_READLOCK)
  1373. {}
  1374. };
  1375. public:
  1376. LK_RETCODE InitializeIterator(CIterator* piter);
  1377. LK_RETCODE IncrementIterator(CIterator* piter);
  1378. LK_RETCODE CloseIterator(CIterator* piter);
  1379. LK_RETCODE InitializeIterator(CConstIterator* piter) const
  1380. {
  1381. IRTLASSERT(piter != NULL && piter->m_pht == NULL);
  1382. IRTLASSERT(piter->m_lkl != LKL_WRITELOCK);
  1383. if (piter == NULL || piter->m_pht != NULL
  1384. || piter->m_lkl == LKL_WRITELOCK)
  1385. return LK_BAD_ITERATOR;
  1386. return const_cast<CLKHashTable*>(this)
  1387. ->InitializeIterator(static_cast<CIterator*>(piter));
  1388. }
  1389. LK_RETCODE IncrementIterator(CConstIterator* piter) const
  1390. {
  1391. IRTLASSERT(piter != NULL && piter->m_pht == this);
  1392. IRTLASSERT(piter->m_lkl != LKL_WRITELOCK);
  1393. if (piter == NULL || piter->m_pht != this
  1394. || piter->m_lkl == LKL_WRITELOCK)
  1395. return LK_BAD_ITERATOR;
  1396. return const_cast<CLKHashTable*>(this)
  1397. ->IncrementIterator(static_cast<CIterator*>(piter));
  1398. }
  1399. LK_RETCODE CloseIterator(CConstIterator* piter) const
  1400. {
  1401. IRTLASSERT(piter != NULL && piter->m_pht == this);
  1402. IRTLASSERT(piter->m_lkl != LKL_WRITELOCK);
  1403. if (piter == NULL || piter->m_pht != this
  1404. || piter->m_lkl == LKL_WRITELOCK)
  1405. return LK_BAD_ITERATOR;
  1406. return const_cast<CLKHashTable*>(this)
  1407. ->CloseIterator(static_cast<CIterator*>(piter));
  1408. }
  1409. };
  1410. //--------------------------------------------------------------------
  1411. // A typesafe wrapper for CLKHashTable (or CLKLinearHashTable).
  1412. //
  1413. // * _Derived must derive from CTypedHashTable and provide certain member
  1414. // functions. It's needed for various downcasting operations. See
  1415. // CStringTestHashTable and CNumberTestHashTable below.
  1416. // * _Record is the type of the record. C{Linear}HashTable will store
  1417. // pointers to _Record.
  1418. // * _Key is the type of the key. _Key is used directly; i.e., it is
  1419. // not assumed to be a pointer type. C{Linear}HashTable assumes that
  1420. // the key is stored in the associated record. See the comments
  1421. // at the declaration of PFnExtractKey for more details.
  1422. //
  1423. // (optional parameters):
  1424. // * _BaseHashTable is the base hash table: CLKHashTable or CLKLinearHashTable
  1425. // * _BaseIterator is the iterator type, _BaseHashTable::CIterator
  1426. //
  1427. // CTypedHashTable could derive directly from CLKLinearHashTable, if you
  1428. // don't need the extra overhead of CLKHashTable (which is quite low).
  1429. //
  1430. // You may need to add the following line to your code to disable
  1431. // warning messages about truncating extremly long identifiers.
  1432. // #pragma warning (disable : 4786)
  1433. //--------------------------------------------------------------------
  1434. template < class _Derived, class _Record, class _Key,
  1435. class _BaseHashTable=CLKHashTable,
  1436. class _BaseIterator=_BaseHashTable::CIterator
  1437. >
  1438. class CTypedHashTable : public _BaseHashTable
  1439. {
  1440. public:
  1441. // convenient aliases
  1442. typedef _Derived Derived;
  1443. typedef _Record Record;
  1444. typedef _Key Key;
  1445. typedef _BaseHashTable BaseHashTable;
  1446. typedef CTypedHashTable<_Derived, _Record, _Key,
  1447. _BaseHashTable, _BaseIterator>
  1448. HashTable;
  1449. typedef _BaseIterator BaseIterator;
  1450. // ApplyIf() and DeleteIf(): Does the record match the predicate?
  1451. // Note: takes a Record*, not a const Record*. You can modify the
  1452. // record in Pred() or Action(), if you like, but if you do, you
  1453. // should use LKL_WRITELOCK to lock the table.
  1454. typedef LK_PREDICATE (*PFnRecordPred) (Record* pRec, void* pvState);
  1455. // Apply() et al: Perform action on record.
  1456. typedef LK_ACTION (*PFnRecordAction)(Record* pRec, void* pvState);
  1457. private:
  1458. // Wrappers for the typesafe methods exposed by the derived class
  1459. static const void*
  1460. _ExtractKey(const void* pvRecord)
  1461. {
  1462. const _Record* pRec = static_cast<const _Record*>(pvRecord);
  1463. _Key key = static_cast<_Key>(_Derived::ExtractKey(pRec));
  1464. return reinterpret_cast<const void*>(key);
  1465. }
  1466. static DWORD
  1467. _CalcKeyHash(const void* pvKey)
  1468. {
  1469. _Key key = reinterpret_cast<_Key>(const_cast<void*>(pvKey));
  1470. return _Derived::CalcKeyHash(key);
  1471. }
  1472. static bool
  1473. _EqualKeys(const void* pvKey1, const void* pvKey2)
  1474. {
  1475. _Key key1 = reinterpret_cast<_Key>(const_cast<void*>(pvKey1));
  1476. _Key key2 = reinterpret_cast<_Key>(const_cast<void*>(pvKey2));
  1477. return _Derived::EqualKeys(key1, key2);
  1478. }
  1479. // Hmm? what's a good way of bypassing this and passing NULL
  1480. // for pfnAddRefRecord to the C{Linear}HashTable ctor if the user
  1481. // doesn't want this functionality? Perhaps a template bool param?
  1482. static void
  1483. _AddRefRecord(const void* pvRecord, int nIncr)
  1484. {
  1485. _Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
  1486. _Derived::AddRefRecord(pRec, nIncr);
  1487. }
  1488. // Typesafe wrappers for Apply, ApplyIf, and DeleteIf.
  1489. class CState
  1490. {
  1491. public:
  1492. PFnRecordPred m_pfnPred;
  1493. PFnRecordAction m_pfnAction;
  1494. void* m_pvState;
  1495. CState(
  1496. PFnRecordPred pfnPred,
  1497. PFnRecordAction pfnAction,
  1498. void* pvState)
  1499. : m_pfnPred(pfnPred), m_pfnAction(pfnAction), m_pvState(pvState)
  1500. {}
  1501. };
  1502. static LK_PREDICATE
  1503. _Pred(const void* pvRecord, void* pvState)
  1504. {
  1505. _Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
  1506. CState* pState = static_cast<CState*>(pvState);
  1507. return (*pState->m_pfnPred)(pRec, pState->m_pvState);
  1508. }
  1509. static LK_ACTION
  1510. _Action(const void* pvRecord, void* pvState)
  1511. {
  1512. _Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
  1513. CState* pState = static_cast<CState*>(pvState);
  1514. return (*pState->m_pfnAction)(pRec, pState->m_pvState);
  1515. }
  1516. public:
  1517. CTypedHashTable(
  1518. LPCSTR pszName, // An identifier for debugging
  1519. double maxload=LK_DFLT_MAXLOAD, // Upperbound on avg chain length
  1520. DWORD initsize=LK_DFLT_INITSIZE, // Initial size of hash table.
  1521. DWORD num_subtbls=LK_DFLT_NUM_SUBTBLS// #subordinate hash tables.
  1522. )
  1523. : _BaseHashTable(pszName, _ExtractKey, _CalcKeyHash, _EqualKeys,
  1524. _AddRefRecord, maxload, initsize, num_subtbls)
  1525. {}
  1526. LK_RETCODE InsertRecord(const _Record* pRec, bool fOverwrite=false)
  1527. { return _BaseHashTable::InsertRecord(pRec, fOverwrite); }
  1528. LK_RETCODE DeleteKey(const _Key key)
  1529. { return _BaseHashTable::DeleteKey(reinterpret_cast<const void*>(key));}
  1530. LK_RETCODE DeleteRecord(const _Record* pRec)
  1531. { return _BaseHashTable::DeleteRecord(pRec);}
  1532. // Note: returns a _Record**, not a const Record**. Note that you
  1533. // can use a const type for the template parameter to ensure constness.
  1534. LK_RETCODE FindKey(const _Key key, _Record** ppRec) const
  1535. {
  1536. if (ppRec == NULL)
  1537. return LK_BAD_RECORD;
  1538. *ppRec = NULL;
  1539. const void* pvRec = NULL;
  1540. LK_RETCODE lkrc =
  1541. _BaseHashTable::FindKey(reinterpret_cast<const void*>(key), &pvRec);
  1542. *ppRec = static_cast<_Record*>(const_cast<void*>(pvRec));
  1543. return lkrc;
  1544. }
  1545. LK_RETCODE FindRecord(const _Record* pRec) const
  1546. { return _BaseHashTable::FindRecord(pRec);}
  1547. // Other C{Linear}HashTable methods can be exposed without change
  1548. // TODO: Print?
  1549. // Typesafe wrappers for Apply et al
  1550. DWORD Apply(PFnRecordAction pfnAction,
  1551. void* pvState=NULL,
  1552. LK_LOCKTYPE lkl=LKL_READLOCK)
  1553. {
  1554. IRTLASSERT(pfnAction != NULL);
  1555. if (pfnAction == NULL)
  1556. return 0;
  1557. CState state(NULL, pfnAction, pvState);
  1558. return _BaseHashTable::Apply(_Action, &state, lkl);
  1559. }
  1560. DWORD ApplyIf(PFnRecordPred pfnPredicate,
  1561. PFnRecordAction pfnAction,
  1562. void* pvState=NULL,
  1563. LK_LOCKTYPE lkl=LKL_READLOCK)
  1564. {
  1565. IRTLASSERT(pfnPredicate != NULL && pfnAction != NULL);
  1566. if (pfnPredicate == NULL || pfnAction == NULL)
  1567. return 0;
  1568. CState state(pfnPredicate, pfnAction, pvState);
  1569. return _BaseHashTable::ApplyIf(_Pred, _Action, &state, lkl);
  1570. }
  1571. DWORD DeleteIf(PFnRecordPred pfnPredicate, void* pvState=NULL)
  1572. {
  1573. IRTLASSERT(pfnPredicate != NULL);
  1574. if (pfnPredicate == NULL)
  1575. return 0;
  1576. CState state(pfnPredicate, NULL, pvState);
  1577. return _BaseHashTable::DeleteIf(_Pred, &state);
  1578. }
  1579. // Typesafe wrappers for iterators
  1580. class CIterator : public _BaseIterator
  1581. {
  1582. private:
  1583. // Private, unimplemented copy ctor and op= to prevent
  1584. // compiler synthesizing them.
  1585. CIterator(const CIterator&);
  1586. CIterator& operator=(const CIterator&);
  1587. public:
  1588. CIterator(
  1589. LK_LOCKTYPE lkl=LKL_WRITELOCK)
  1590. : _BaseIterator(lkl)
  1591. {}
  1592. _Record* Record() const
  1593. {
  1594. const _BaseIterator* pBase = static_cast<const _BaseIterator*>(this);
  1595. return reinterpret_cast<_Record*>(const_cast<void*>(
  1596. pBase->Record()));
  1597. }
  1598. _Key Key() const
  1599. {
  1600. const _BaseIterator* pBase = static_cast<const _BaseIterator*>(this);
  1601. return reinterpret_cast<_Key>(const_cast<void*>(pBase->Key()));
  1602. }
  1603. };
  1604. // readonly iterator
  1605. class CConstIterator : public CIterator
  1606. {
  1607. private:
  1608. // Private, unimplemented copy ctor and op= to prevent
  1609. // compiler synthesizing them.
  1610. CConstIterator(const CConstIterator&);
  1611. CConstIterator& operator=(const CConstIterator&);
  1612. public:
  1613. CConstIterator()
  1614. : CIterator(LKL_READLOCK)
  1615. {}
  1616. const _Record* Record() const
  1617. {
  1618. return CIterator::Record();
  1619. }
  1620. const _Key Key() const
  1621. {
  1622. return CIterator::Key();
  1623. }
  1624. };
  1625. public:
  1626. LK_RETCODE InitializeIterator(CIterator* piter)
  1627. {
  1628. return _BaseHashTable::InitializeIterator(piter);
  1629. }
  1630. LK_RETCODE IncrementIterator(CIterator* piter)
  1631. {
  1632. return _BaseHashTable::IncrementIterator(piter);
  1633. }
  1634. LK_RETCODE CloseIterator(CIterator* piter)
  1635. {
  1636. return _BaseHashTable::CloseIterator(piter);
  1637. }
  1638. LK_RETCODE InitializeIterator(CConstIterator* piter) const
  1639. {
  1640. return const_cast<HashTable*>(this)
  1641. ->InitializeIterator(static_cast<CIterator*>(piter));
  1642. }
  1643. LK_RETCODE IncrementIterator(CConstIterator* piter) const
  1644. {
  1645. return const_cast<HashTable*>(this)
  1646. ->IncrementIterator(static_cast<CIterator*>(piter));
  1647. }
  1648. LK_RETCODE CloseIterator(CConstIterator* piter) const
  1649. {
  1650. return const_cast<HashTable*>(this)
  1651. ->CloseIterator(static_cast<CIterator*>(piter));
  1652. }
  1653. };
  1654. #ifdef __LKHASH_NAMESPACE__
  1655. }
  1656. #endif // __LKHASH_NAMESPACE__
  1657. #ifdef SAMPLE_LKHASH_TESTCLASS
  1658. #include <hashfn.h>
  1659. //--------------------------------------------------------------------
  1660. // An example of how to create a wrapper for CLKHashTable
  1661. //--------------------------------------------------------------------
  1662. // some random class
  1663. class CTest
  1664. {
  1665. public:
  1666. enum {BUFFSIZE=20};
  1667. int m_n; // This will also be a key
  1668. char m_sz[BUFFSIZE]; // This will be the primary key
  1669. bool m_fWhatever;
  1670. mutable LONG m_cRefs; // Reference count for lifetime management.
  1671. // Must be mutable to use 'const CTest*' in
  1672. // hashtables
  1673. CTest(int n, const char* psz, bool f)
  1674. : m_n(n), m_fWhatever(f), m_cRefs(0)
  1675. {
  1676. strncpy(m_sz, psz, BUFFSIZE-1);
  1677. m_sz[BUFFSIZE-1] = '\0';
  1678. }
  1679. ~CTest()
  1680. {
  1681. IRTLASSERT(m_cRefs == 0);
  1682. }
  1683. };
  1684. // A typed hash table of CTests, keyed on the string field. Case-insensitive.
  1685. class CStringTestHashTable
  1686. : public CTypedHashTable<CStringTestHashTable, const CTest, const char*>
  1687. {
  1688. public:
  1689. CStringTestHashTable()
  1690. : CTypedHashTable<CStringTestHashTable, const CTest,
  1691. const char*>("string")
  1692. {}
  1693. static const char*
  1694. ExtractKey(const CTest* pTest)
  1695. {
  1696. return pTest->m_sz;
  1697. }
  1698. static DWORD
  1699. CalcKeyHash(const char* pszKey)
  1700. {
  1701. return HashStringNoCase(pszKey);
  1702. }
  1703. static bool
  1704. EqualKeys(const char* pszKey1, const char* pszKey2)
  1705. {
  1706. return _stricmp(pszKey1, pszKey2) == 0;
  1707. }
  1708. static void
  1709. AddRefRecord(const CTest* pTest, int nIncr)
  1710. {
  1711. if (nIncr == +1)
  1712. {
  1713. // or, perhaps, pIFoo->AddRef() (watch out for marshalling)
  1714. // or ++pTest->m_cRefs (single-threaded only)
  1715. InterlockedIncrement(&pTest->m_cRefs);
  1716. }
  1717. else if (nIncr == -1)
  1718. {
  1719. // or, perhaps, pIFoo->Release() or --pTest->m_cRefs;
  1720. LONG l = InterlockedDecrement(&pTest->m_cRefs);
  1721. // For some hashtables, it may also make sense to add the following
  1722. // if (l == 0) delete pTest;
  1723. // but that would typically only apply when InsertRecord was
  1724. // used thus
  1725. // lkrc = ht.InsertRecord(new CTest(foo, bar));
  1726. }
  1727. else
  1728. IRTLASSERT(0);
  1729. TRACE("AddRef(%p, %s) %d, cRefs == %d\n",
  1730. pTest, pTest->m_sz, nIncr, pTest->m_cRefs);
  1731. }
  1732. };
  1733. // Another typed hash table of CTests. This one is keyed on the numeric field.
  1734. class CNumberTestHashTable
  1735. : public CTypedHashTable<CNumberTestHashTable, const CTest, int>
  1736. {
  1737. public:
  1738. CNumberTestHashTable()
  1739. : CTypedHashTable<CNumberTestHashTable, const CTest, int>("number") {}
  1740. static int ExtractKey(const CTest* pTest) {return pTest->m_n;}
  1741. static DWORD CalcKeyHash(int nKey) {return Hash(nKey);}
  1742. static bool EqualKeys(int nKey1, int nKey2) {return nKey1 == nKey2;}
  1743. static void AddRefRecord(const CTest* pTest, int nIncr) {/* do nothing*/}
  1744. };
  1745. // A class to exercise ApplyIf()
  1746. class CApplyIfTest
  1747. {
  1748. public:
  1749. static LK_PREDICATE
  1750. Predicate(const CTest* pTest, void* pvState)
  1751. {
  1752. CApplyIfTest* pThis = static_cast<CApplyIfTest*>(pvState);
  1753. ++pThis->m_cPreds;
  1754. TRACE("CApplyIfTest::Predicate(%p (%s, %d), %p)\n",
  1755. pTest, pTest->m_sz, pTest->m_n, pThis);
  1756. return (pTest->m_n % 10 == 7) ? LKP_PERFORM : LKP_NO_ACTION;
  1757. }
  1758. static LK_ACTION
  1759. Action(const CTest* pTest, void* pvState)
  1760. {
  1761. CApplyIfTest* pThis = static_cast<CApplyIfTest*>(pvState);
  1762. ++pThis->m_cActions;
  1763. LK_ACTION lka = (pTest->m_n > 30) ? LKA_SUCCEEDED : LKA_FAILED;
  1764. TRACE("CApplyIfTest::Action(%p (%s, %d), %p) %s\n",
  1765. pTest, pTest->m_sz, pTest->m_n, pThis,
  1766. lka == LKA_SUCCEEDED ? "succeeded" : "failed");
  1767. if (lka == LKA_SUCCEEDED)
  1768. ++pThis->m_cSuccesses;
  1769. else if (lka == LKA_FAILED)
  1770. ++pThis->m_cFailures;
  1771. return lka;
  1772. }
  1773. int m_cPreds;
  1774. int m_cActions;
  1775. int m_cSuccesses;
  1776. int m_cFailures;
  1777. CApplyIfTest()
  1778. : m_cPreds(0), m_cActions(0), m_cSuccesses(0), m_cFailures(0)
  1779. {}
  1780. };
  1781. // The Predicate and Action functions can be static member functions,
  1782. // but don't have to be
  1783. LK_PREDICATE
  1784. DeleteIfGt10(
  1785. const CTest* pTest,
  1786. void* pvState)
  1787. {
  1788. TRACE("DeleteIfGt10(%p, %s, %p) = %d\n",
  1789. pTest, pTest->m_sz, pvState, pTest->m_n);
  1790. return (pTest->m_n > 10) ? LKP_PERFORM : LKP_NO_ACTION;
  1791. }
  1792. #include <stdio.h>
  1793. #include <string.h>
  1794. void Test(
  1795. bool fVerbose)
  1796. {
  1797. // Some objects for the hash tables
  1798. CTest tl(5, "Larson", true);
  1799. CTest tk(17, "Krishnan", false);
  1800. CTest tr(37, "Reilly", true);
  1801. // A string-keyed hash table
  1802. CStringTestHashTable stht;
  1803. IRTLVERIFY(LK_SUCCESS == stht.InsertRecord(&tl));
  1804. IRTLVERIFY(LK_SUCCESS == stht.InsertRecord(&tk));
  1805. IRTLVERIFY(LK_SUCCESS == stht.InsertRecord(&tr));
  1806. TRACE("Check the overwrite feature of InsertRecord\n");
  1807. IRTLVERIFY(LK_KEY_EXISTS == stht.InsertRecord(&tr, false));
  1808. IRTLASSERT(tr.m_cRefs == 1);
  1809. IRTLVERIFY(LK_SUCCESS == stht.InsertRecord(&tr, true));
  1810. IRTLASSERT(tr.m_cRefs == 1); // 1+1-1 == 1
  1811. TRACE("Check that the keys are really present in the table and that "
  1812. "the refcounting works\n");
  1813. const CTest* pTest = NULL;
  1814. IRTLVERIFY(LK_SUCCESS == stht.FindKey(tl.m_sz, &pTest) && pTest == &tl);
  1815. IRTLASSERT(tl.m_cRefs == 2);
  1816. IRTLVERIFY(LK_SUCCESS == stht.FindKey(tk.m_sz, &pTest) && pTest == &tk);
  1817. IRTLASSERT(tk.m_cRefs == 2);
  1818. IRTLVERIFY(LK_SUCCESS == stht.FindKey(tr.m_sz, &pTest) && pTest == &tr);
  1819. IRTLASSERT(tr.m_cRefs == 2);
  1820. IRTLVERIFY(LK_SUCCESS == stht.FindRecord(&tr));
  1821. IRTLASSERT(tr.m_cRefs == 2); // FindRecord does not addref
  1822. TRACE("Look for a key under an alternate spelling (case-insensitive)\n");
  1823. IRTLVERIFY(LK_SUCCESS == stht.FindKey("rEiLlY", &pTest) && pTest == &tr);
  1824. IRTLASSERT(tr.m_cRefs == 3);
  1825. TRACE("Release the references added by FindKey\n");
  1826. stht.AddRefRecord(&tl, -1);
  1827. tk.m_cRefs--;
  1828. tr.m_cRefs = 1;
  1829. TRACE("Now build the numeric hash table\n");
  1830. CNumberTestHashTable ntht;
  1831. IRTLVERIFY(LK_SUCCESS == ntht.InsertRecord(&tl));
  1832. IRTLVERIFY(LK_SUCCESS == ntht.InsertRecord(&tk));
  1833. IRTLVERIFY(LK_SUCCESS == ntht.InsertRecord(&tr));
  1834. TRACE("Test ApplyIf()\n");
  1835. CApplyIfTest ait;
  1836. IRTLVERIFY(1 == ntht.ApplyIf(ait.Predicate, ait.Action, &ait));
  1837. IRTLASSERT(3 == ait.m_cPreds && 2 == ait.m_cActions
  1838. && 1 == ait.m_cSuccesses && 1 == ait.m_cFailures);
  1839. TRACE("Test DeleteIf()\n");
  1840. IRTLASSERT(3 == ntht.Size());
  1841. ntht.DeleteIf(DeleteIfGt10, NULL);
  1842. IRTLASSERT(1 == ntht.Size());
  1843. TRACE("Check that the keys that were supposed to be deleted "
  1844. "really are gone\n");
  1845. IRTLASSERT(tl.m_n <= 10);
  1846. IRTLVERIFY(LK_SUCCESS == ntht.FindKey(tl.m_n, &pTest) && pTest == &tl);
  1847. IRTLASSERT(tk.m_n > 10);
  1848. IRTLVERIFY(LK_NO_SUCH_KEY == ntht.FindKey(tk.m_n, &pTest)
  1849. && pTest == NULL);
  1850. IRTLASSERT(tr.m_n > 10);
  1851. IRTLVERIFY(LK_NO_SUCH_KEY == ntht.FindKey(tr.m_n, &pTest)
  1852. && pTest == NULL);
  1853. IRTLVERIFY(LK_SUCCESS == ntht.DeleteRecord(&tl));
  1854. IRTLASSERT(0 == ntht.Size());
  1855. TRACE("Check Iterators\n");
  1856. DWORD cRec = 0;
  1857. CStringTestHashTable::CIterator iter;
  1858. LK_RETCODE lkrc = stht.InitializeIterator(&iter);
  1859. while (lkrc == LK_SUCCESS)
  1860. {
  1861. ++cRec;
  1862. CStringTestHashTable::Key pszKey = iter.Key();
  1863. CStringTestHashTable::Record* pRec = iter.Record();
  1864. IRTLASSERT(pRec == &tl || pRec == &tk || pRec == &tr);
  1865. if (fVerbose)
  1866. printf("Record(%p) contains \"%s\"\n", pRec, pszKey);
  1867. lkrc = stht.IncrementIterator(&iter);
  1868. }
  1869. IRTLASSERT(lkrc == LK_NO_MORE_ELEMENTS);
  1870. lkrc = stht.CloseIterator(&iter);
  1871. IRTLASSERT(lkrc == LK_SUCCESS);
  1872. IRTLASSERT(cRec == stht.Size());
  1873. TRACE("Check const iterators\n");
  1874. const CStringTestHashTable& sthtConst = stht;
  1875. CStringTestHashTable::CConstIterator iterConst;
  1876. cRec = 0;
  1877. lkrc = sthtConst.InitializeIterator(&iterConst);
  1878. while (lkrc == LK_SUCCESS)
  1879. {
  1880. ++cRec;
  1881. const CStringTestHashTable::Key pszKey = iterConst.Key();
  1882. const CStringTestHashTable::Record* pRec = iterConst.Record();
  1883. IRTLASSERT(pRec == &tl || pRec == &tk || pRec == &tr);
  1884. if (fVerbose)
  1885. printf("Const Record(%p) contains \"%s\"\n", pRec, pszKey);
  1886. lkrc = sthtConst.IncrementIterator(&iterConst);
  1887. }
  1888. IRTLASSERT(lkrc == LK_NO_MORE_ELEMENTS);
  1889. lkrc = sthtConst.CloseIterator(&iterConst);
  1890. IRTLASSERT(lkrc == LK_SUCCESS);
  1891. IRTLASSERT(cRec == sthtConst.Size());
  1892. #if 1
  1893. TRACE("Check Clear\n");
  1894. stht.Clear();
  1895. IRTLASSERT(0 == stht.Size());
  1896. #else
  1897. TRACE("Check DeleteKey\n");
  1898. IRTLVERIFY(LK_SUCCESS == stht.DeleteKey(tl.m_sz));
  1899. IRTLVERIFY(LK_SUCCESS == stht.DeleteKey(tk.m_sz));
  1900. IRTLVERIFY(LK_SUCCESS == stht.DeleteKey(tr.m_sz));
  1901. #endif
  1902. TRACE("Test done\n");
  1903. // ~CTest will check for m_cRefs==0
  1904. }
  1905. #endif // SAMPLE_LKHASH_TESTCLASS
  1906. #endif // __LKHASH_H__