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.

1186 lines
34 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. LKR-hash.h
  5. Abstract:
  6. Declares LKRhash: a fast, scalable, cache- and
  7. multiprocessor-friendly hash table
  8. Authors:
  9. Paul (Per-Ake) Larson, PALarson@microsoft.com, July 1997
  10. Murali R. Krishnan (MuraliK)
  11. George V. Reilly (GeorgeRe) 06-Jan-1998
  12. --*/
  13. #ifndef __LKR_HASH_H__
  14. #define __LKR_HASH_H__
  15. /* Enable STL-style iterators */
  16. #ifndef LKR_NO_STL_ITERATORS
  17. # define LKR_STL_ITERATORS 1
  18. #endif /* !LKR_NO_STL_ITERATORS */
  19. /* Enable call-back, table visitor routines */
  20. #ifndef LKR_NO_APPLY_IF
  21. # define LKR_APPLY_IF
  22. #endif /* !LKR_NO_APPLY_IF */
  23. /* Expose the table's ReadLock and WriteLock routines */
  24. #ifndef LKR_NO_EXPOSED_TABLE_LOCK
  25. # define LKR_EXPOSED_TABLE_LOCK
  26. #endif /* !LKR_NO_EXPOSED_TABLE_LOCK */
  27. #ifndef __IRTLMISC_H__
  28. # include <irtlmisc.h>
  29. #endif /* !__IRTLMISC_H__ */
  30. #ifdef __cplusplus
  31. extern "C" {
  32. #endif /* __cplusplus */
  33. typedef struct LkrHashTable* PLkrHashTable;
  34. /*--------------------------------------------------------------------
  35. * Possible return codes from LKR_functions and TypedLkrHashTable
  36. */
  37. enum LK_RETCODE {
  38. /* severe errors < 0 */
  39. LK_UNUSABLE = -99, /* Table corrupted: all bets are off */
  40. LK_ALLOC_FAIL, /* ran out of memory */
  41. LK_BAD_ITERATOR, /* invalid iterator; e.g., points to another table */
  42. LK_BAD_RECORD, /* invalid record; e.g., NULL for LKR_InsertRecord */
  43. LK_BAD_PARAMETERS, /* invalid parameters; e.g., NULL fnptrs to ctor */
  44. LK_NOT_INITIALIZED, /* LKRHashTableInit was not called */
  45. LK_BAD_TABLE, /* Called with invalid PLkrHashTable */
  46. LK_SUCCESS = 0, /* everything's okay */
  47. LK_KEY_EXISTS, /* key already present for
  48. LKR_InsertRecord(no-overwrite) */
  49. LK_NO_SUCH_KEY, /* key not found */
  50. LK_NO_MORE_ELEMENTS,/* iterator exhausted */
  51. };
  52. #define LKR_SUCCEEDED(lkrc) ((lkrc) >= LK_SUCCESS)
  53. /*--------------------------------------------------------------------
  54. * Size parameter to LKR_CreateTable
  55. */
  56. enum LK_TABLESIZE {
  57. LK_SMALL_TABLESIZE= 1, /* < 200 elements */
  58. LK_MEDIUM_TABLESIZE= 2, /* 200...10,000 elements */
  59. LK_LARGE_TABLESIZE= 3, /* 10,000+ elements */
  60. };
  61. /*--------------------------------------------------------------------
  62. * Creation flag parameter to LKR_CreateTable
  63. */
  64. enum {
  65. LK_CREATE_DEFAULT = 0, /* 0 is an acceptable default */
  66. LK_CREATE_MULTIKEYS = 0x0001, /* Allow multiple identical keys? */
  67. };
  68. /*--------------------------------------------------------------------
  69. * Initialization flag parameters to LKR_Initialize
  70. */
  71. enum {
  72. LK_INIT_DEFAULT = 0, /* 0 is an acceptable default */
  73. LK_INIT_DEBUG_SPEW = 0x1000, /* Enable debug output: debug version only */
  74. };
  75. /*--------------------------------------------------------------------
  76. * Reference Counting and Lifetime Management
  77. *
  78. * Increment the reference count of a record before returning it from
  79. * LKR_FindKey. It's necessary to do it in LKR_FindKey itself while the
  80. * bucket is still locked, rather than one of the wrappers, to avoid race
  81. * conditions. Similarly, the reference count is incremented in
  82. * LKR_InsertRecord and decremented in LKR_DeleteKey. Finally, if an old
  83. * record is overwritten in LKR_InsertRecord, its reference count is
  84. * decremented.
  85. *
  86. * Summary of calls to AddRefRecord
  87. * +1: add a new reference or owner
  88. * - LKR_InsertRecord
  89. * - LKR_FindKey
  90. * - IncrementIterator
  91. * -1: delete a reference => release an owner
  92. * - LKR_InsertRecord (overwrite old record with same key)
  93. * - LKR_DeleteKey, LKR_DeleteRecord
  94. * - ApplyIf(LKP_DELETE), DeleteIf
  95. * - IncrementIterator (previous record), CloseIterator, Erase(iter)
  96. * - LKR_Clear, table destructor
  97. * 0: no change (not called)
  98. * - LKR_FindRecord (by definition, you already have a ref to the record)
  99. *
  100. * It's up to you to decrement the reference count when you're finished
  101. * with it after retrieving it via LKR_FindKey (e.g., you could call
  102. * pht->AddRefRecord(pRec, LKAR_EXPLICIT_RELEASE)) and to determine the
  103. * semantics of what this means. The hashtable itself has no notion of
  104. * reference counts; this is merely to help with the lifetime management
  105. * of the record objects.
  106. */
  107. /* These reason codes help in debugging refcount leaks */
  108. enum LK_ADDREF_REASON {
  109. /* negative reasons => decrement refcount => release ownership */
  110. LKAR_EXPLICIT_RELEASE = -29, /* user calls ht.AddRefRecord to */
  111. /* explicitly release a record */
  112. LKAR_DELETE_KEY = -28, /* DeleteKey() */
  113. LKAR_DELETE_RECORD = -27, /* DeleteRecord() */
  114. LKAR_INSERT_RELEASE = -26, /* InsertRecord overwrites prev record */
  115. LKAR_CLEAR = -25, /* Clear() */
  116. LKAR_DTOR = -24, /* hash table destructor */
  117. LKAR_APPLY_DELETE = -23, /* Apply[If] LKP_(PERFORM|_DELETE) */
  118. LKAR_DELETE_IF_DELETE = -22, /* DeleteIf LKP_(PERFORM|_DELETE) */
  119. LKAR_ITER_RELEASE = -21, /* ++iter releases previous record */
  120. LKAR_ITER_ASSIGN_RELEASE = -20, /* iter.operator= releases prev rec */
  121. LKAR_ITER_DTOR = -19, /* ~iter */
  122. LKAR_ITER_ERASE = -18, /* Erase(iter): iter releases record */
  123. LKAR_ITER_ERASE_TABLE = -17, /* Erase(iter); table releases record */
  124. LKAR_ITER_CLOSE = -16, /* CloseIterator (obsolete) */
  125. /* positive reasons => increment refcount => add an owner */
  126. LKAR_INSERT_RECORD = +11, /* InsertRecord() */
  127. LKAR_FIND_KEY = +12, /* FindKey() */
  128. LKAR_ITER_ACQUIRE = +13, /* ++iter acquires next record */
  129. LKAR_ITER_COPY_CTOR = +14, /* iter copy constructor acquires rec */
  130. LKAR_ITER_ASSIGN_ACQUIRE = +15, /* iter.operator= acquires new rec */
  131. LKAR_ITER_INSERT = +16, /* Insert(iter) */
  132. LKAR_ITER_FIND = +17, /* Find(iter) */
  133. LKAR_EXPLICIT_ACQUIRE = +18, /* user calls ht.AddRefRecord to */
  134. /* explicitly acquire a ref to a rec */
  135. };
  136. /* Convert an LK_ADDREF_REASON to a string representation.
  137. * Useful for debugging.
  138. */
  139. IRTL_DLLEXP
  140. const char*
  141. LKR_AddRefReasonAsString(
  142. LK_ADDREF_REASON lkar);
  143. /*--------------------------------------------------------------------
  144. * Callback functions needed by table:
  145. * ExtractKey, CalcKeyHash, EqualKeys, AddRefRecord
  146. * Internally, records are handled as `const void*' and
  147. * keys are handled as `const DWORD_PTR'. The latter allows for
  148. * keys to be numbers as well as pointers (polymorphism).
  149. */
  150. /* Use types defined in recent versions of the Platform SDK in <basetsd.h>.
  151. */
  152. #ifndef _W64
  153. typedef DWORD DWORD_PTR; /* integral type big enough to hold a pointer */
  154. #endif
  155. /* Given a record, return its key. Assumes that the key is embedded in
  156. * the record, or at least somehow derivable from the record. For
  157. * completely unrelated keys & values, a wrapper class should use
  158. * something like STL's pair<key,value> template to aggregate them
  159. * into a record.
  160. */
  161. typedef
  162. const DWORD_PTR
  163. (WINAPI *LKR_PFnExtractKey) (
  164. const void* pvRecord);
  165. /* Given a key, return its hash signature. The hashing functions in
  166. * hashfn.h (or something that builds upon them) are suggested.
  167. */
  168. typedef
  169. DWORD
  170. (WINAPI *LKR_PFnCalcKeyHash) (
  171. const DWORD_PTR pnKey);
  172. /* Compare two keys for equality; e.g., _stricmp, memcmp, operator==
  173. */
  174. typedef
  175. BOOL
  176. (WINAPI *LKR_PFnEqualKeys) (
  177. const DWORD_PTR pnKey1,
  178. const DWORD_PTR pnKey2);
  179. /* Adjust the reference count of a record. See the earlier discussion
  180. * of reference counting and lifetime management.
  181. */
  182. typedef
  183. void
  184. (WINAPI *LKR_PFnAddRefRecord)(
  185. const void* pvRecord,
  186. LK_ADDREF_REASON lkar);
  187. #ifdef LKR_APPLY_IF
  188. /*--------------------------------------------------------------------
  189. * Apply, ApplyIf, and DeleteIf provide one way to visit (enumerate) all
  190. * records in a table.
  191. */
  192. /*--------------------------------------------------------------------
  193. * Return codes from PFnRecordPred.
  194. */
  195. enum LK_PREDICATE {
  196. LKP_ABORT = 1, /* Stop walking the table immediately */
  197. LKP_NO_ACTION = 2, /* No action, just keep walking */
  198. LKP_PERFORM = 3, /* Perform action and continue walking */
  199. LKP_PERFORM_STOP = 4, /* Perform action, then stop */
  200. LKP_DELETE = 5, /* Delete record and keep walking */
  201. LKP_DELETE_STOP = 6, /* Delete record, then stop */
  202. };
  203. /*--------------------------------------------------------------------
  204. * Return codes from PFnRecordAction.
  205. */
  206. enum LK_ACTION {
  207. LKA_ABORT = 1, /* Stop walking the table immediately */
  208. LKA_FAILED = 2, /* Action failed; continue walking the table */
  209. LKA_SUCCEEDED = 3, /* Action succeeded; continue walking the table */
  210. };
  211. /*--------------------------------------------------------------------
  212. * Parameter to Apply and ApplyIf.
  213. */
  214. enum LK_LOCKTYPE {
  215. LKL_READLOCK = 1, /* Lock the table for reading (for constness) */
  216. LKL_WRITELOCK = 2, /* Lock the table for writing */
  217. };
  218. /* LKR_ApplyIf() and LKR_DeleteIf(): Does the record match the predicate?
  219. */
  220. typedef
  221. LK_PREDICATE
  222. (WINAPI *LKR_PFnRecordPred) (
  223. const void* pvRecord,
  224. void* pvState);
  225. /* LKR_Apply() et al: Perform action on record.
  226. */
  227. typedef
  228. LK_ACTION
  229. (WINAPI *LKR_PFnRecordAction)(
  230. const void* pvRecord,
  231. void* pvState);
  232. #endif /* LKR_APPLY_IF */
  233. /* Initialize the global variables needed by other LKR routines.
  234. */
  235. IRTL_DLLEXP
  236. BOOL
  237. LKR_Initialize(
  238. DWORD dwInitFlags);
  239. /* Clean up the global variables needed by other LKR routines.
  240. */
  241. IRTL_DLLEXP
  242. void
  243. LKR_Terminate();
  244. /* Create a new LkrHashTable
  245. * Returns pointer to new table if successful. NULL, otherwise.
  246. * The table must be destroyed with LKR_DeleteTable.
  247. */
  248. IRTL_DLLEXP
  249. PLkrHashTable
  250. LKR_CreateTable(
  251. LPCSTR pszName, /* Identify the table for debugging */
  252. LKR_PFnExtractKey pfnExtractKey, /* Extract key from record */
  253. LKR_PFnCalcKeyHash pfnCalcKeyHash, /* Calculate hash signature of key */
  254. LKR_PFnEqualKeys pfnEqualKeys, /* Compare two keys */
  255. LKR_PFnAddRefRecord pfnAddRefRecord,/* AddRef in LKR_FindKey, etc */
  256. LK_TABLESIZE nTableSize, /* Small/Med/Large number of elements*/
  257. DWORD fCreateFlags /* Mixture of LK_CREATE_* flags. */
  258. );
  259. /* Destroy an LkrHashTable created by LKR_CreateTable.
  260. */
  261. IRTL_DLLEXP
  262. void
  263. LKR_DeleteTable(
  264. PLkrHashTable plkr);
  265. /* Insert a new record into hash table.
  266. * Returns LKR_SUCCESS if all OK, LKR_KEY_EXISTS if same key already
  267. * exists (unless fOverwrite), LKR_ALLOC_FAIL if out of space,
  268. * or LKR_BAD_RECORD for a bad record.
  269. * If fOverwrite is set and a record with this key is already present,
  270. * it will be overwritten. If there are multiple records with this key,
  271. * only the first will be overwritten.
  272. */
  273. IRTL_DLLEXP
  274. LK_RETCODE
  275. LKR_InsertRecord(
  276. PLkrHashTable plkr,
  277. const void* pvRecord,
  278. BOOL fOverwrite);
  279. /* Delete record with the given key from the table. Does not actually delete
  280. * record from memory, just calls AddRefRecord(LKAR_DELETE_KEY);
  281. * Returns LKR_SUCCESS if all OK, or LKR_NO_SUCH_KEY if not found
  282. * If fDeleteAllSame is set, all records that match pnKey will be deleted
  283. * from the table; otherwise, only the first matching record is deleted.
  284. */
  285. IRTL_DLLEXP
  286. LK_RETCODE
  287. LKR_DeleteKey(
  288. PLkrHashTable plkr,
  289. const DWORD_PTR pnKey,
  290. BOOL fDeleteAllSame);
  291. /* Delete a record from the table, if present.
  292. * Returns LKR_SUCCESS if all OK, or LKR_NO_SUCH_KEY if not found
  293. */
  294. IRTL_DLLEXP
  295. LK_RETCODE
  296. LKR_DeleteRecord(
  297. PLkrHashTable plkr,
  298. const void* pvRecord);
  299. /* Find record with given key.
  300. * Returns: LKR_SUCCESS, if record found (record is returned in *ppvRecord)
  301. * LKR_NO_SUCH_KEY, if no record with given key value was found
  302. * LKR_BAD_RECORD, if ppvRecord is invalid
  303. * LKR_UNUSABLE, if hash table not in usable state
  304. * Note: the record is AddRef'd. You must decrement the reference
  305. * count when you are finished with the record (if you're implementing
  306. * refcounting semantics).
  307. */
  308. IRTL_DLLEXP
  309. LK_RETCODE
  310. LKR_FindKey(
  311. PLkrHashTable plkr,
  312. const DWORD_PTR pnKey,
  313. const void** ppvRecord);
  314. /* Sees if the record is contained in the table
  315. * Returns: LKR_SUCCESS, if record found
  316. * LKR_NO_SUCH_KEY, if record is not in the table
  317. * LKR_BAD_RECORD, if pvRecord is invalid
  318. * LKR_UNUSABLE, if hash table not in usable state
  319. * Note: the record is *not* AddRef'd. By definition, the caller
  320. * already has a reference to it.
  321. */
  322. IRTL_DLLEXP
  323. LK_RETCODE
  324. LKR_FindRecord(
  325. PLkrHashTable plkr,
  326. const void* pvRecord);
  327. #ifdef LKR_APPLY_IF
  328. /* Walk the hash table, applying pfnAction to all records.
  329. * Locks one subtable after another with either a (possibly
  330. * shared) readlock or a writelock, according to lkl.
  331. * Loop is aborted if pfnAction ever returns LKA_ABORT.
  332. * Returns the number of successful applications.
  333. */
  334. IRTL_DLLEXP
  335. DWORD
  336. LKR_Apply(
  337. PLkrHashTable plkr,
  338. LKR_PFnRecordAction pfnAction,
  339. void* pvState,
  340. LK_LOCKTYPE lkl);
  341. /* Walk the hash table, applying pfnAction to any records that match
  342. * pfnPredicate. Locks one subtable after another with either
  343. * a (possibly shared) readlock or a writelock, according to lkl.
  344. * Loop is aborted if pfnAction ever returns LKA_ABORT.
  345. * Returns the number of successful applications.
  346. */
  347. IRTL_DLLEXP
  348. DWORD
  349. LKR_ApplyIf(
  350. PLkrHashTable plkr,
  351. LKR_PFnRecordPred pfnPredicate,
  352. LKR_PFnRecordAction pfnAction,
  353. void* pvState,
  354. LK_LOCKTYPE lkl);
  355. /* Delete any records that match pfnPredicate.
  356. * Locks one subtable after another with a writelock.
  357. * Returns the number of deletions.
  358. *
  359. * Do *not* walk the hash table by hand with an iterator and call
  360. * LKR_DeleteKey. The iterator will end up pointing to garbage.
  361. */
  362. IRTL_DLLEXP
  363. DWORD
  364. LKR_DeleteIf(
  365. PLkrHashTable plkr,
  366. LKR_PFnRecordPred pfnPredicate,
  367. void* pvState);
  368. #endif /* LKR_APPLY_IF */
  369. /* Check table for consistency. Returns 0 if okay, or the number of
  370. * errors otherwise.
  371. */
  372. IRTL_DLLEXP
  373. int
  374. LKR_CheckTable(
  375. PLkrHashTable plkr);
  376. /* Remove all data from the table
  377. */
  378. IRTL_DLLEXP
  379. void
  380. LKR_Clear(
  381. PLkrHashTable plkr);
  382. /* Number of elements in the table
  383. */
  384. IRTL_DLLEXP
  385. DWORD
  386. LKR_Size(
  387. PLkrHashTable plkr);
  388. /* Maximum possible number of elements in the table
  389. */
  390. IRTL_DLLEXP
  391. DWORD
  392. LKR_MaxSize(
  393. PLkrHashTable plkr);
  394. /* Is the hash table usable?
  395. */
  396. IRTL_DLLEXP
  397. BOOL
  398. LKR_IsUsable(
  399. PLkrHashTable plkr);
  400. /* Is the hash table consistent and correct?
  401. */
  402. IRTL_DLLEXP
  403. BOOL
  404. LKR_IsValid(
  405. PLkrHashTable plkr);
  406. #ifdef LKR_EXPOSED_TABLE_LOCK
  407. /* Lock the table (exclusively) for writing
  408. */
  409. IRTL_DLLEXP
  410. void
  411. LKR_WriteLock(
  412. PLkrHashTable plkr);
  413. /* Lock the table (possibly shared) for reading
  414. */
  415. IRTL_DLLEXP
  416. void
  417. LKR_ReadLock(
  418. PLkrHashTable plkr);
  419. /* Unlock the table for writing
  420. */
  421. IRTL_DLLEXP
  422. void
  423. LKR_WriteUnlock(
  424. PLkrHashTable plkr);
  425. /* Unlock the table for reading
  426. */
  427. IRTL_DLLEXP
  428. void
  429. LKR_ReadUnlock(
  430. PLkrHashTable plkr);
  431. /* Is the table already locked for writing?
  432. */
  433. IRTL_DLLEXP
  434. BOOL
  435. LKR_IsWriteLocked(
  436. PLkrHashTable plkr);
  437. /* Is the table already locked for reading?
  438. */
  439. IRTL_DLLEXP
  440. BOOL
  441. LKR_IsReadLocked(
  442. PLkrHashTable plkr);
  443. /* Is the table unlocked for writing?
  444. */
  445. IRTL_DLLEXP
  446. BOOL
  447. LKR_IsWriteUnlocked(
  448. PLkrHashTable plkr);
  449. /* Is the table unlocked for reading?
  450. */
  451. IRTL_DLLEXP
  452. BOOL
  453. LKR_IsReadUnlocked(
  454. PLkrHashTable plkr);
  455. /* Convert the read lock to a write lock. Note: another thread may acquire
  456. * exclusive access to the table before this routine returns.
  457. */
  458. IRTL_DLLEXP
  459. void
  460. LKR_ConvertSharedToExclusive(
  461. PLkrHashTable plkr);
  462. /* Convert the write lock to a read lock
  463. */
  464. IRTL_DLLEXP
  465. void
  466. LKR_ConvertExclusiveToShared(
  467. PLkrHashTable plkr);
  468. #endif /* LKR_EXPOSED_TABLE_LOCK */
  469. #ifdef __cplusplus
  470. } // extern "C"
  471. // Only provide iterators in the C++ interface. It's too hard to
  472. // provide the correct ownership semantics in a typesafe way in C,
  473. // and C users can always use the LKR_ApplyIf family of callback
  474. // enumerators if they really need to walk the hashtable.
  475. #ifdef LKR_STL_ITERATORS
  476. #pragma message("STL iterators")
  477. // needed for std::forward_iterator_tag, etc
  478. # include <utility>
  479. #include <irtldbg.h>
  480. #define LKR_ITER_TRACE IRTLTRACE
  481. class IRTL_DLLEXP LKR_Iterator
  482. {
  483. private:
  484. friend IRTL_DLLEXP LKR_Iterator LKR_Begin(PLkrHashTable plkr);
  485. friend IRTL_DLLEXP LKR_Iterator LKR_End(PLkrHashTable plkr);
  486. // private ctor
  487. LKR_Iterator(bool);
  488. public:
  489. // default ctor
  490. LKR_Iterator();
  491. // copy ctor
  492. LKR_Iterator(const LKR_Iterator& rhs);
  493. // assignment operator
  494. LKR_Iterator& operator=(const LKR_Iterator& rhs);
  495. // dtor
  496. ~LKR_Iterator();
  497. // Increment the iterator to point to the next record, or to LKR_End()
  498. bool Increment();
  499. // Is the iterator valid?
  500. bool IsValid() const;
  501. // Returns the record that the iterator points to.
  502. // Must point to a valid record.
  503. const void* Record() const;
  504. // Returns the key of the record that the iterator points to.
  505. // Must point to a valid record.
  506. const DWORD_PTR Key() const;
  507. // Compare two iterators for equality
  508. bool operator==(const LKR_Iterator& rhs) const;
  509. // Compare two iterators for inequality
  510. bool operator!=(const LKR_Iterator& rhs) const;
  511. // pointer to implementation object
  512. void* pImpl;
  513. }; // class LKR_Iterator
  514. /* Return iterator pointing to first item in table
  515. */
  516. IRTL_DLLEXP
  517. LKR_Iterator
  518. LKR_Begin(
  519. PLkrHashTable plkr);
  520. /* Return a one-past-the-end iterator. Always empty.
  521. */
  522. IRTL_DLLEXP
  523. LKR_Iterator
  524. LKR_End(
  525. PLkrHashTable plkr);
  526. /* Insert a record
  527. * Returns `true' if successful; iterResult points to that record
  528. * Returns `false' otherwise; iterResult == End()
  529. */
  530. IRTL_DLLEXP
  531. bool
  532. LKR_Insert(
  533. PLkrHashTable plkr,
  534. /* in */ const void* pvRecord,
  535. /* out */ LKR_Iterator& riterResult,
  536. /* in */ bool fOverwrite=false);
  537. /* Erase the record pointed to by the iterator; adjust the iterator
  538. * to point to the next record. Returns `true' if successful.
  539. */
  540. IRTL_DLLEXP
  541. bool
  542. LKR_Erase(
  543. PLkrHashTable plkr,
  544. /* in,out */ LKR_Iterator& riter);
  545. /* Erase the records in the range [riterFirst, riterLast).
  546. * Returns `true' if successful.
  547. */
  548. IRTL_DLLEXP
  549. bool
  550. LKR_Erase(
  551. PLkrHashTable plkr,
  552. /*in*/ LKR_Iterator& riterFirst,
  553. /*in*/ LKR_Iterator& riterLast);
  554. /* Find the (first) record that has its key == pnKey.
  555. * If successful, returns `true' and iterator points to (first) record.
  556. * If fails, returns `false' and iterator == End()
  557. */
  558. IRTL_DLLEXP
  559. bool
  560. LKR_Find(
  561. PLkrHashTable plkr,
  562. /* in */ DWORD_PTR pnKey,
  563. /* out */ LKR_Iterator& riterResult);
  564. /* Find the range of records that have their keys == pnKey.
  565. * If successful, returns `true', iterFirst points to first record,
  566. * and iterLast points to one-beyond-the last such record.
  567. * If fails, returns `false' and both iterators == End().
  568. * Primarily useful when fMultiKeys == TRUE
  569. */
  570. IRTL_DLLEXP
  571. bool
  572. LKR_EqualRange(
  573. PLkrHashTable plkr,
  574. /* in */ DWORD_PTR pnKey,
  575. /* out */ LKR_Iterator& riterFirst, // inclusive
  576. /* out */ LKR_Iterator& riterLast); // exclusive
  577. #endif // LKR_STL_ITERATORS
  578. //--------------------------------------------------------------------
  579. // A typesafe wrapper for PLkrHashTable
  580. //
  581. // * _Derived must derive from TypedLkrHashTable and provide certain member
  582. // functions. It's needed for various downcasting operations.
  583. // * _Record is the type of the record. PLkrHashTable will store
  584. // pointers to _Record, as const void*.
  585. // * _Key is the type of the key. _Key is used directly; i.e., it is
  586. // not assumed to be a pointer type. PLkrHashTable assumes that
  587. // the key is stored in the associated record. See the comments
  588. // at the declaration of LKR_PFnExtractKey for more details.
  589. //
  590. // You may need to add the following line to your code to disable
  591. // warning messages about truncating extremly long identifiers.
  592. // #pragma warning (disable : 4786)
  593. //
  594. // The _Derived class should look something like this:
  595. // class CDerived : public TypedLkrHashTable<CDerived, RecordType, KeyType>
  596. // {
  597. // public:
  598. // CDerived()
  599. // : TypedLkrHashTable<CDerived, RecordType, KeyType>("CDerived")
  600. // {/*other ctor actions*/}
  601. // static KeyType ExtractKey(const RecordType* pTest);
  602. // static DWORD CalcKeyHash(const KeyType Key);
  603. // static bool EqualKeys(const KeyType Key1, const KeyType Key2);
  604. // static void AddRefRecord(RecordType* pRecord,LK_ADDREF_REASON lkar);
  605. // // optional: other functions
  606. // };
  607. //
  608. //--------------------------------------------------------------------
  609. template <class _Derived, class _Record, class _Key>
  610. class TypedLkrHashTable
  611. {
  612. public:
  613. // convenient aliases
  614. typedef _Derived Derived;
  615. typedef _Record Record;
  616. typedef _Key Key;
  617. typedef TypedLkrHashTable<_Derived, _Record, _Key> HashTable;
  618. #ifdef LKR_APPLY_IF
  619. // LKR_ApplyIf() and LKR_DeleteIf(): Does the record match the predicate?
  620. // Note: takes a Record*, not a const Record*. You can modify the
  621. // record in Pred() or Action(), if you like, but if you do, you
  622. // should use LKL_WRITELOCK to lock the table.
  623. typedef LK_PREDICATE (WINAPI *PFnRecordPred) (Record* pRec, void* pvState);
  624. // Apply() et al: Perform action on record.
  625. typedef LK_ACTION (WINAPI *PFnRecordAction)(Record* pRec, void* pvState);
  626. #endif // LKR_APPLY_IF
  627. protected:
  628. PLkrHashTable m_plkr;
  629. // Wrappers for the typesafe methods exposed by the derived class
  630. static const DWORD_PTR WINAPI
  631. _ExtractKey(const void* pvRecord)
  632. {
  633. const _Record* pRec = static_cast<const _Record*>(pvRecord);
  634. const _Key key = static_cast<const _Key>(_Derived::ExtractKey(pRec));
  635. // I would prefer to use reinterpret_cast here and in _CalcKeyHash
  636. // and _EqualKeys, but the stupid Win64 compiler thinks it knows
  637. // better than I do.
  638. return (const DWORD_PTR) key;
  639. }
  640. static DWORD WINAPI
  641. _CalcKeyHash(const DWORD_PTR pnKey)
  642. {
  643. const _Key key = (const _Key) (DWORD_PTR) pnKey;
  644. return _Derived::CalcKeyHash(key);
  645. }
  646. static BOOL WINAPI
  647. _EqualKeys(const DWORD_PTR pnKey1, const DWORD_PTR pnKey2)
  648. {
  649. const _Key key1 = (const _Key) (DWORD_PTR) pnKey1;
  650. const _Key key2 = (const _Key) (DWORD_PTR) pnKey2;
  651. return _Derived::EqualKeys(key1, key2);
  652. }
  653. static void WINAPI
  654. _AddRefRecord(const void* pvRecord, LK_ADDREF_REASON lkar)
  655. {
  656. _Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
  657. _Derived::AddRefRecord(pRec, lkar);
  658. }
  659. #ifdef LKR_APPLY_IF
  660. // Typesafe wrappers for Apply, ApplyIf, and DeleteIf.
  661. class CState
  662. {
  663. public:
  664. PFnRecordPred m_pfnPred;
  665. PFnRecordAction m_pfnAction;
  666. void* m_pvState;
  667. CState(
  668. PFnRecordPred pfnPred,
  669. PFnRecordAction pfnAction,
  670. void* pvState)
  671. : m_pfnPred(pfnPred), m_pfnAction(pfnAction), m_pvState(pvState)
  672. {}
  673. };
  674. static LK_PREDICATE WINAPI
  675. _Pred(const void* pvRecord, void* pvState)
  676. {
  677. _Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
  678. CState* pState = static_cast<CState*>(pvState);
  679. return (*pState->m_pfnPred)(pRec, pState->m_pvState);
  680. }
  681. static LK_ACTION WINAPI
  682. _Action(const void* pvRecord, void* pvState)
  683. {
  684. _Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
  685. CState* pState = static_cast<CState*>(pvState);
  686. return (*pState->m_pfnAction)(pRec, pState->m_pvState);
  687. }
  688. #endif // LKR_APPLY_IF
  689. public:
  690. TypedLkrHashTable(
  691. LPCSTR pszName, // An identifier for debugging
  692. LK_TABLESIZE nTableSize, // Small/Med/Large number of elements
  693. bool fMultiKeys=false // Allow multiple identical keys?
  694. )
  695. : m_plkr(NULL)
  696. {
  697. m_plkr = LKR_CreateTable(pszName, _ExtractKey, _CalcKeyHash,
  698. _EqualKeys, _AddRefRecord,
  699. nTableSize, fMultiKeys);
  700. }
  701. ~TypedLkrHashTable()
  702. {
  703. LKR_DeleteTable(m_plkr);
  704. }
  705. LK_RETCODE InsertRecord(const _Record* pRec, bool fOverwrite=false)
  706. { return LKR_InsertRecord(m_plkr, pRec, fOverwrite); }
  707. LK_RETCODE DeleteKey(const _Key key, bool fDeleteAllSame=false)
  708. {
  709. const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
  710. DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
  711. return LKR_DeleteKey(m_plkr, pnKey, fDeleteAllSame);
  712. }
  713. LK_RETCODE DeleteRecord(const _Record* pRec)
  714. { return LKR_DeleteRecord(m_plkr, pRec);}
  715. // Note: returns a _Record**, not a const Record**. Note that you
  716. // can use a const type for the template parameter to ensure constness.
  717. LK_RETCODE FindKey(const _Key key, _Record** ppRec) const
  718. {
  719. if (ppRec == NULL)
  720. return LK_BAD_RECORD;
  721. *ppRec = NULL;
  722. const void* pvRec = NULL;
  723. const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
  724. DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
  725. LK_RETCODE lkrc = LKR_FindKey(m_plkr, pnKey, &pvRec);
  726. *ppRec = static_cast<_Record*>(const_cast<void*>(pvRec));
  727. return lkrc;
  728. }
  729. LK_RETCODE FindRecord(const _Record* pRec) const
  730. { return LKR_FindRecord(m_plkr, pRec);}
  731. #ifdef LKR_APPLY_IF
  732. DWORD Apply(PFnRecordAction pfnAction,
  733. void* pvState=NULL,
  734. LK_LOCKTYPE lkl=LKL_READLOCK)
  735. {
  736. IRTLASSERT(pfnAction != NULL);
  737. if (pfnAction == NULL)
  738. return 0;
  739. CState state(NULL, pfnAction, pvState);
  740. return LKR_Apply(m_plkr, _Action, &state, lkl);
  741. }
  742. DWORD ApplyIf(PFnRecordPred pfnPredicate,
  743. PFnRecordAction pfnAction,
  744. void* pvState=NULL,
  745. LK_LOCKTYPE lkl=LKL_READLOCK)
  746. {
  747. IRTLASSERT(pfnPredicate != NULL && pfnAction != NULL);
  748. if (pfnPredicate == NULL || pfnAction == NULL)
  749. return 0;
  750. CState state(pfnPredicate, pfnAction, pvState);
  751. return LKR_ApplyIf(m_plkr, _Pred, _Action, &state, lkl);
  752. }
  753. DWORD DeleteIf(PFnRecordPred pfnPredicate, void* pvState=NULL)
  754. {
  755. IRTLASSERT(pfnPredicate != NULL);
  756. if (pfnPredicate == NULL)
  757. return 0;
  758. CState state(pfnPredicate, NULL, pvState);
  759. return LKR_DeleteIf(m_plkr, _Pred, &state);
  760. }
  761. #endif // LKR_APPLY_IF
  762. int CheckTable() const
  763. { return LKR_CheckTable(m_plkr); }
  764. void Clear()
  765. { return LKR_Clear(m_plkr); }
  766. DWORD Size() const
  767. { return LKR_Size(m_plkr); }
  768. DWORD MaxSize() const
  769. { return LKR_MaxSize(m_plkr); }
  770. BOOL IsUsable() const
  771. { return LKR_IsUsable(m_plkr); }
  772. BOOL IsValid() const
  773. { return LKR_IsValid(m_plkr); }
  774. #ifdef LKR_EXPOSED_TABLE_LOCK
  775. void WriteLock()
  776. { LKR_WriteLock(m_plkr); }
  777. void ReadLock() const
  778. { LKR_ReadLock(m_plkr); }
  779. void WriteUnlock()
  780. { LKR_WriteUnlock(m_plkr); }
  781. void ReadUnlock() const
  782. { LKR_ReadUnlock(m_plkr); }
  783. BOOL IsWriteLocked() const
  784. { return LKR_IsWriteLocked(m_plkr); }
  785. BOOL IsReadLocked() const
  786. { return LKR_IsReadLocked(m_plkr); }
  787. BOOL IsWriteUnlocked() const
  788. { return LKR_IsWriteUnlocked(m_plkr); }
  789. BOOL IsReadUnlocked() const
  790. { return LKR_IsReadUnlocked(m_plkr); }
  791. void ConvertSharedToExclusive() const
  792. { LKR_ConvertSharedToExclusive(m_plkr); }
  793. void ConvertExclusiveToShared() const
  794. { LKR_ConvertExclusiveToShared(m_plkr); }
  795. #endif // LKR_EXPOSED_TABLE_LOCK
  796. #ifdef LKR_STL_ITERATORS
  797. friend class LKR_Iterator;
  798. // TODO: const_iterator
  799. public:
  800. class iterator
  801. {
  802. friend class TypedLkrHashTable<_Derived, _Record, _Key>;
  803. protected:
  804. LKR_Iterator m_iter;
  805. iterator(
  806. LKR_Iterator& rhs)
  807. : m_iter(rhs)
  808. {
  809. LKR_ITER_TRACE(_TEXT("Typed::prot ctor, this=%p, rhs=%p\n"),
  810. this, &rhs);
  811. }
  812. public:
  813. typedef std::forward_iterator_tag iterator_category;
  814. typedef _Record value_type;
  815. typedef ptrdiff_t difference_type;
  816. typedef size_t size_type;
  817. typedef value_type& reference;
  818. typedef value_type* pointer;
  819. iterator()
  820. : m_iter()
  821. {
  822. LKR_ITER_TRACE(_TEXT("Typed::default ctor, this=%p\n"), this);
  823. }
  824. iterator(
  825. const iterator& rhs)
  826. : m_iter(rhs.m_iter)
  827. {
  828. LKR_ITER_TRACE(_TEXT("Typed::copy ctor, this=%p, rhs=%p\n"),
  829. this, &rhs);
  830. }
  831. iterator& operator=(
  832. const iterator& rhs)
  833. {
  834. LKR_ITER_TRACE(_TEXT("Typed::operator=, this=%p, rhs=%p\n"),
  835. this, &rhs);
  836. m_iter = rhs.m_iter;
  837. return *this;
  838. }
  839. ~iterator()
  840. {
  841. LKR_ITER_TRACE(_TEXT("Typed::dtor, this=%p\n"), this);
  842. }
  843. reference operator*() const
  844. {
  845. void* pvRecord = const_cast<void*>(m_iter.Record());
  846. return reinterpret_cast<reference>(pvRecord);
  847. }
  848. pointer operator->() const { return &(operator*()); }
  849. // pre-increment
  850. iterator& operator++()
  851. {
  852. LKR_ITER_TRACE(_TEXT("Typed::pre-increment, this=%p\n"), this);
  853. m_iter.Increment();
  854. return *this;
  855. }
  856. // post-increment
  857. iterator operator++(int)
  858. {
  859. LKR_ITER_TRACE(_TEXT("Typed::post-increment, this=%p\n"), this);
  860. iterator iterPrev = *this;
  861. m_iter.Increment();
  862. return iterPrev;
  863. }
  864. bool operator==(
  865. const iterator& rhs) const
  866. {
  867. LKR_ITER_TRACE(_TEXT("Typed::operator==, this=%p, rhs=%p\n"),
  868. this, &rhs);
  869. return m_iter == rhs.m_iter;
  870. }
  871. bool operator!=(
  872. const iterator& rhs) const
  873. {
  874. LKR_ITER_TRACE(_TEXT("Typed::operator!=, this=%p, rhs=%p\n"),
  875. this, &rhs);
  876. return m_iter != rhs.m_iter;
  877. }
  878. _Record* Record() const
  879. {
  880. LKR_ITER_TRACE(_TEXT("Typed::Record, this=%p\n"), this);
  881. return reinterpret_cast<_Record*>(
  882. const_cast<void*>(m_iter.Record()));
  883. }
  884. _Key Key() const
  885. {
  886. LKR_ITER_TRACE(_TEXT("Typed::Key, this=%p\n"), this);
  887. return reinterpret_cast<_Key>(
  888. reinterpret_cast<void*>(m_iter.Key()));
  889. }
  890. }; // class iterator
  891. // Return iterator pointing to first item in table
  892. iterator begin()
  893. {
  894. LKR_ITER_TRACE(_TEXT("Typed::begin()\n"));
  895. return LKR_Begin(m_plkr);
  896. }
  897. // Return a one-past-the-end iterator. Always empty.
  898. iterator end() const
  899. {
  900. LKR_ITER_TRACE(_TEXT("Typed::end()\n"));
  901. return LKR_End(m_plkr);
  902. }
  903. template <class _InputIterator>
  904. TypedLkrHashTable(
  905. LPCSTR pszName, // An identifier for debugging
  906. _InputIterator f, // first element in range
  907. _InputIterator l, // one-beyond-last element
  908. LK_TABLESIZE nTableSize, // Small/Med/Large number of elements
  909. bool fMultiKeys=false // Allow multiple identical keys?
  910. )
  911. {
  912. m_plkr = LKR_CreateTable(pszName, _ExtractKey, _CalcKeyHash,
  913. _EqualKeys, _AddRefRecord,
  914. nTableSize, fMultiKeys);
  915. insert(f, l);
  916. }
  917. template <class _InputIterator>
  918. void insert(_InputIterator f, _InputIterator l)
  919. {
  920. for ( ; f != l; ++f)
  921. InsertRecord(&(*f));
  922. }
  923. bool
  924. Insert(
  925. const _Record* pRecord,
  926. iterator& riterResult,
  927. bool fOverwrite=false)
  928. {
  929. LKR_ITER_TRACE(_TEXT("Typed::Insert\n"));
  930. return LKR_Insert(m_plkr, pRecord, riterResult.m_iter, fOverwrite);
  931. }
  932. bool
  933. Erase(
  934. iterator& riter)
  935. {
  936. LKR_ITER_TRACE(_TEXT("Typed::Erase\n"));
  937. return LKR_Erase(m_plkr, riter.m_iter);
  938. }
  939. bool
  940. Erase(
  941. iterator& riterFirst,
  942. iterator& riterLast)
  943. {
  944. LKR_ITER_TRACE(_TEXT("Typed::Erase2\n"));
  945. return LKR_Erase(m_plkr, riterFirst.m_iter, riterLast.m_iter);
  946. }
  947. bool
  948. Find(
  949. const _Key key,
  950. iterator& riterResult)
  951. {
  952. LKR_ITER_TRACE(_TEXT("Typed::Find\n"));
  953. const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
  954. DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
  955. return LKR_Find(m_plkr, pnKey, riterResult.m_iter);
  956. }
  957. bool
  958. EqualRange(
  959. const _Key key,
  960. iterator& riterFirst,
  961. iterator& riterLast)
  962. {
  963. LKR_ITER_TRACE(_TEXT("Typed::EqualRange\n"));
  964. const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
  965. DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
  966. return LKR_EqualRange(m_plkr, pnKey, riterFirst.m_iter,
  967. riterLast.m_iter);
  968. }
  969. #undef LKR_ITER_TRACE
  970. #endif // LKR_STL_ITERATORS
  971. }; // class TypedLkrHashTable
  972. #endif /* __cplusplus */
  973. #endif /* __LKR_HASH_H__ */