1187 lines
34 KiB
C++
1187 lines
34 KiB
C++
/*++
|
||
|
||
Copyright (c) 1998-2000 Microsoft Corporation
|
||
|
||
Module Name :
|
||
LKR-hash.h
|
||
|
||
Abstract:
|
||
Declares LKRhash: a fast, scalable, cache- and
|
||
multiprocessor-friendly hash table
|
||
|
||
Authors:
|
||
Paul (Per-Ake) Larson, PALarson@microsoft.com, July 1997
|
||
Murali R. Krishnan (MuraliK)
|
||
George V. Reilly (GeorgeRe) 06-Jan-1998
|
||
|
||
--*/
|
||
|
||
|
||
#ifndef __LKR_HASH_H__
|
||
#define __LKR_HASH_H__
|
||
|
||
|
||
/* Enable STL-style iterators */
|
||
#ifndef LKR_NO_STL_ITERATORS
|
||
# define LKR_STL_ITERATORS 1
|
||
#endif /* !LKR_NO_STL_ITERATORS */
|
||
|
||
/* Enable call-back, table visitor routines */
|
||
#ifndef LKR_NO_APPLY_IF
|
||
# define LKR_APPLY_IF
|
||
#endif /* !LKR_NO_APPLY_IF */
|
||
|
||
/* Expose the table's ReadLock and WriteLock routines */
|
||
#ifndef LKR_NO_EXPOSED_TABLE_LOCK
|
||
# define LKR_EXPOSED_TABLE_LOCK
|
||
#endif /* !LKR_NO_EXPOSED_TABLE_LOCK */
|
||
|
||
|
||
#ifndef __IRTLMISC_H__
|
||
# include <irtlmisc.h>
|
||
#endif /* !__IRTLMISC_H__ */
|
||
|
||
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif /* __cplusplus */
|
||
|
||
|
||
typedef struct LkrHashTable* PLkrHashTable;
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Possible return codes from LKR_functions and TypedLkrHashTable
|
||
*/
|
||
enum LK_RETCODE {
|
||
/* severe errors < 0 */
|
||
LK_UNUSABLE = -99, /* Table corrupted: all bets are off */
|
||
LK_ALLOC_FAIL, /* ran out of memory */
|
||
LK_BAD_ITERATOR, /* invalid iterator; e.g., points to another table */
|
||
LK_BAD_RECORD, /* invalid record; e.g., NULL for LKR_InsertRecord */
|
||
LK_BAD_PARAMETERS, /* invalid parameters; e.g., NULL fnptrs to ctor */
|
||
LK_NOT_INITIALIZED, /* LKRHashTableInit was not called */
|
||
LK_BAD_TABLE, /* Called with invalid PLkrHashTable */
|
||
|
||
LK_SUCCESS = 0, /* everything's okay */
|
||
LK_KEY_EXISTS, /* key already present for
|
||
LKR_InsertRecord(no-overwrite) */
|
||
LK_NO_SUCH_KEY, /* key not found */
|
||
LK_NO_MORE_ELEMENTS,/* iterator exhausted */
|
||
};
|
||
|
||
#define LKR_SUCCEEDED(lkrc) ((lkrc) >= LK_SUCCESS)
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Size parameter to LKR_CreateTable
|
||
*/
|
||
|
||
enum LK_TABLESIZE {
|
||
LK_SMALL_TABLESIZE= 1, /* < 200 elements */
|
||
LK_MEDIUM_TABLESIZE= 2, /* 200...10,000 elements */
|
||
LK_LARGE_TABLESIZE= 3, /* 10,000+ elements */
|
||
};
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Creation flag parameter to LKR_CreateTable
|
||
*/
|
||
|
||
enum {
|
||
LK_CREATE_DEFAULT = 0, /* 0 is an acceptable default */
|
||
LK_CREATE_MULTIKEYS = 0x0001, /* Allow multiple identical keys? */
|
||
};
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Initialization flag parameters to LKR_Initialize
|
||
*/
|
||
|
||
enum {
|
||
LK_INIT_DEFAULT = 0, /* 0 is an acceptable default */
|
||
LK_INIT_DEBUG_SPEW = 0x1000, /* Enable debug output: debug version only */
|
||
};
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Reference Counting and Lifetime Management
|
||
*
|
||
* Increment the reference count of a record before returning it from
|
||
* LKR_FindKey. It's necessary to do it in LKR_FindKey itself while the
|
||
* bucket is still locked, rather than one of the wrappers, to avoid race
|
||
* conditions. Similarly, the reference count is incremented in
|
||
* LKR_InsertRecord and decremented in LKR_DeleteKey. Finally, if an old
|
||
* record is overwritten in LKR_InsertRecord, its reference count is
|
||
* decremented.
|
||
*
|
||
* Summary of calls to AddRefRecord
|
||
* +1: add a new reference or owner
|
||
* - LKR_InsertRecord
|
||
* - LKR_FindKey
|
||
* - IncrementIterator
|
||
* -1: delete a reference => release an owner
|
||
* - LKR_InsertRecord (overwrite old record with same key)
|
||
* - LKR_DeleteKey, LKR_DeleteRecord
|
||
* - ApplyIf(LKP_DELETE), DeleteIf
|
||
* - IncrementIterator (previous record), CloseIterator, Erase(iter)
|
||
* - LKR_Clear, table destructor
|
||
* 0: no change (not called)
|
||
* - LKR_FindRecord (by definition, you already have a ref to the record)
|
||
*
|
||
* It's up to you to decrement the reference count when you're finished
|
||
* with it after retrieving it via LKR_FindKey (e.g., you could call
|
||
* pht->AddRefRecord(pRec, LKAR_EXPLICIT_RELEASE)) and to determine the
|
||
* semantics of what this means. The hashtable itself has no notion of
|
||
* reference counts; this is merely to help with the lifetime management
|
||
* of the record objects.
|
||
*/
|
||
|
||
/* These reason codes help in debugging refcount leaks */
|
||
enum LK_ADDREF_REASON {
|
||
|
||
/* negative reasons => decrement refcount => release ownership */
|
||
LKAR_EXPLICIT_RELEASE = -29, /* user calls ht.AddRefRecord to */
|
||
/* explicitly release a record */
|
||
LKAR_DELETE_KEY = -28, /* DeleteKey() */
|
||
LKAR_DELETE_RECORD = -27, /* DeleteRecord() */
|
||
LKAR_INSERT_RELEASE = -26, /* InsertRecord overwrites prev record */
|
||
LKAR_CLEAR = -25, /* Clear() */
|
||
LKAR_DTOR = -24, /* hash table destructor */
|
||
LKAR_APPLY_DELETE = -23, /* Apply[If] LKP_(PERFORM|_DELETE) */
|
||
LKAR_DELETE_IF_DELETE = -22, /* DeleteIf LKP_(PERFORM|_DELETE) */
|
||
LKAR_ITER_RELEASE = -21, /* ++iter releases previous record */
|
||
LKAR_ITER_ASSIGN_RELEASE = -20, /* iter.operator= releases prev rec */
|
||
LKAR_ITER_DTOR = -19, /* ~iter */
|
||
LKAR_ITER_ERASE = -18, /* Erase(iter): iter releases record */
|
||
LKAR_ITER_ERASE_TABLE = -17, /* Erase(iter); table releases record */
|
||
LKAR_ITER_CLOSE = -16, /* CloseIterator (obsolete) */
|
||
|
||
/* positive reasons => increment refcount => add an owner */
|
||
LKAR_INSERT_RECORD = +11, /* InsertRecord() */
|
||
LKAR_FIND_KEY = +12, /* FindKey() */
|
||
LKAR_ITER_ACQUIRE = +13, /* ++iter acquires next record */
|
||
LKAR_ITER_COPY_CTOR = +14, /* iter copy constructor acquires rec */
|
||
LKAR_ITER_ASSIGN_ACQUIRE = +15, /* iter.operator= acquires new rec */
|
||
LKAR_ITER_INSERT = +16, /* Insert(iter) */
|
||
LKAR_ITER_FIND = +17, /* Find(iter) */
|
||
LKAR_EXPLICIT_ACQUIRE = +18, /* user calls ht.AddRefRecord to */
|
||
/* explicitly acquire a ref to a rec */
|
||
};
|
||
|
||
|
||
/* Convert an LK_ADDREF_REASON to a string representation.
|
||
* Useful for debugging.
|
||
*/
|
||
IRTL_DLLEXP
|
||
const char*
|
||
LKR_AddRefReasonAsString(
|
||
LK_ADDREF_REASON lkar);
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Callback functions needed by table:
|
||
* ExtractKey, CalcKeyHash, EqualKeys, AddRefRecord
|
||
* Internally, records are handled as `const void*' and
|
||
* keys are handled as `const DWORD_PTR'. The latter allows for
|
||
* keys to be numbers as well as pointers (polymorphism).
|
||
*/
|
||
|
||
|
||
/* Use types defined in recent versions of the Platform SDK in <basetsd.h>.
|
||
*/
|
||
#ifndef _W64
|
||
typedef DWORD DWORD_PTR; /* integral type big enough to hold a pointer */
|
||
#endif
|
||
|
||
|
||
/* Given a record, return its key. Assumes that the key is embedded in
|
||
* the record, or at least somehow derivable from the record. For
|
||
* completely unrelated keys & values, a wrapper class should use
|
||
* something like STL's pair<key,value> template to aggregate them
|
||
* into a record.
|
||
*/
|
||
typedef
|
||
const DWORD_PTR
|
||
(WINAPI *LKR_PFnExtractKey) (
|
||
const void* pvRecord);
|
||
|
||
/* Given a key, return its hash signature. The hashing functions in
|
||
* hashfn.h (or something that builds upon them) are suggested.
|
||
*/
|
||
typedef
|
||
DWORD
|
||
(WINAPI *LKR_PFnCalcKeyHash) (
|
||
const DWORD_PTR pnKey);
|
||
|
||
/* Compare two keys for equality; e.g., _stricmp, memcmp, operator==
|
||
*/
|
||
typedef
|
||
BOOL
|
||
(WINAPI *LKR_PFnEqualKeys) (
|
||
const DWORD_PTR pnKey1,
|
||
const DWORD_PTR pnKey2);
|
||
|
||
|
||
/* Adjust the reference count of a record. See the earlier discussion
|
||
* of reference counting and lifetime management.
|
||
*/
|
||
typedef
|
||
void
|
||
(WINAPI *LKR_PFnAddRefRecord)(
|
||
const void* pvRecord,
|
||
LK_ADDREF_REASON lkar);
|
||
|
||
|
||
|
||
#ifdef LKR_APPLY_IF
|
||
/*--------------------------------------------------------------------
|
||
* Apply, ApplyIf, and DeleteIf provide one way to visit (enumerate) all
|
||
* records in a table.
|
||
*/
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Return codes from PFnRecordPred.
|
||
*/
|
||
|
||
enum LK_PREDICATE {
|
||
LKP_ABORT = 1, /* Stop walking the table immediately */
|
||
LKP_NO_ACTION = 2, /* No action, just keep walking */
|
||
LKP_PERFORM = 3, /* Perform action and continue walking */
|
||
LKP_PERFORM_STOP = 4, /* Perform action, then stop */
|
||
LKP_DELETE = 5, /* Delete record and keep walking */
|
||
LKP_DELETE_STOP = 6, /* Delete record, then stop */
|
||
};
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Return codes from PFnRecordAction.
|
||
*/
|
||
|
||
enum LK_ACTION {
|
||
LKA_ABORT = 1, /* Stop walking the table immediately */
|
||
LKA_FAILED = 2, /* Action failed; continue walking the table */
|
||
LKA_SUCCEEDED = 3, /* Action succeeded; continue walking the table */
|
||
};
|
||
|
||
|
||
/*--------------------------------------------------------------------
|
||
* Parameter to Apply and ApplyIf.
|
||
*/
|
||
|
||
enum LK_LOCKTYPE {
|
||
LKL_READLOCK = 1, /* Lock the table for reading (for constness) */
|
||
LKL_WRITELOCK = 2, /* Lock the table for writing */
|
||
};
|
||
|
||
|
||
|
||
/* LKR_ApplyIf() and LKR_DeleteIf(): Does the record match the predicate?
|
||
*/
|
||
typedef
|
||
LK_PREDICATE
|
||
(WINAPI *LKR_PFnRecordPred) (
|
||
const void* pvRecord,
|
||
void* pvState);
|
||
|
||
/* LKR_Apply() et al: Perform action on record.
|
||
*/
|
||
typedef
|
||
LK_ACTION
|
||
(WINAPI *LKR_PFnRecordAction)(
|
||
const void* pvRecord,
|
||
void* pvState);
|
||
|
||
#endif /* LKR_APPLY_IF */
|
||
|
||
|
||
|
||
/* Initialize the global variables needed by other LKR routines.
|
||
*/
|
||
IRTL_DLLEXP
|
||
BOOL
|
||
LKR_Initialize(
|
||
DWORD dwInitFlags);
|
||
|
||
/* Clean up the global variables needed by other LKR routines.
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_Terminate();
|
||
|
||
/* Create a new LkrHashTable
|
||
* Returns pointer to new table if successful. NULL, otherwise.
|
||
* The table must be destroyed with LKR_DeleteTable.
|
||
*/
|
||
IRTL_DLLEXP
|
||
PLkrHashTable
|
||
LKR_CreateTable(
|
||
LPCSTR pszName, /* Identify the table for debugging */
|
||
LKR_PFnExtractKey pfnExtractKey, /* Extract key from record */
|
||
LKR_PFnCalcKeyHash pfnCalcKeyHash, /* Calculate hash signature of key */
|
||
LKR_PFnEqualKeys pfnEqualKeys, /* Compare two keys */
|
||
LKR_PFnAddRefRecord pfnAddRefRecord,/* AddRef in LKR_FindKey, etc */
|
||
LK_TABLESIZE nTableSize, /* Small/Med/Large number of elements*/
|
||
DWORD fCreateFlags /* Mixture of LK_CREATE_* flags. */
|
||
);
|
||
|
||
/* Destroy an LkrHashTable created by LKR_CreateTable.
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_DeleteTable(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Insert a new record into hash table.
|
||
* Returns LKR_SUCCESS if all OK, LKR_KEY_EXISTS if same key already
|
||
* exists (unless fOverwrite), LKR_ALLOC_FAIL if out of space,
|
||
* or LKR_BAD_RECORD for a bad record.
|
||
* If fOverwrite is set and a record with this key is already present,
|
||
* it will be overwritten. If there are multiple records with this key,
|
||
* only the first will be overwritten.
|
||
*/
|
||
IRTL_DLLEXP
|
||
LK_RETCODE
|
||
LKR_InsertRecord(
|
||
PLkrHashTable plkr,
|
||
const void* pvRecord,
|
||
BOOL fOverwrite);
|
||
|
||
/* Delete record with the given key from the table. Does not actually delete
|
||
* record from memory, just calls AddRefRecord(LKAR_DELETE_KEY);
|
||
* Returns LKR_SUCCESS if all OK, or LKR_NO_SUCH_KEY if not found
|
||
* If fDeleteAllSame is set, all records that match pnKey will be deleted
|
||
* from the table; otherwise, only the first matching record is deleted.
|
||
*/
|
||
IRTL_DLLEXP
|
||
LK_RETCODE
|
||
LKR_DeleteKey(
|
||
PLkrHashTable plkr,
|
||
const DWORD_PTR pnKey,
|
||
BOOL fDeleteAllSame);
|
||
|
||
/* Delete a record from the table, if present.
|
||
* Returns LKR_SUCCESS if all OK, or LKR_NO_SUCH_KEY if not found
|
||
*/
|
||
IRTL_DLLEXP
|
||
LK_RETCODE
|
||
LKR_DeleteRecord(
|
||
PLkrHashTable plkr,
|
||
const void* pvRecord);
|
||
|
||
/* Find record with given key.
|
||
* Returns: LKR_SUCCESS, if record found (record is returned in *ppvRecord)
|
||
* LKR_NO_SUCH_KEY, if no record with given key value was found
|
||
* LKR_BAD_RECORD, if ppvRecord is invalid
|
||
* LKR_UNUSABLE, if hash table not in usable state
|
||
* Note: the record is AddRef'd. You must decrement the reference
|
||
* count when you are finished with the record (if you're implementing
|
||
* refcounting semantics).
|
||
*/
|
||
IRTL_DLLEXP
|
||
LK_RETCODE
|
||
LKR_FindKey(
|
||
PLkrHashTable plkr,
|
||
const DWORD_PTR pnKey,
|
||
const void** ppvRecord);
|
||
|
||
/* Sees if the record is contained in the table
|
||
* Returns: LKR_SUCCESS, if record found
|
||
* LKR_NO_SUCH_KEY, if record is not in the table
|
||
* LKR_BAD_RECORD, if pvRecord is invalid
|
||
* LKR_UNUSABLE, if hash table not in usable state
|
||
* Note: the record is *not* AddRef'd. By definition, the caller
|
||
* already has a reference to it.
|
||
*/
|
||
IRTL_DLLEXP
|
||
LK_RETCODE
|
||
LKR_FindRecord(
|
||
PLkrHashTable plkr,
|
||
const void* pvRecord);
|
||
|
||
|
||
#ifdef LKR_APPLY_IF
|
||
|
||
/* Walk the hash table, applying pfnAction to all records.
|
||
* Locks one subtable after another with either a (possibly
|
||
* shared) readlock or a writelock, according to lkl.
|
||
* Loop is aborted if pfnAction ever returns LKA_ABORT.
|
||
* Returns the number of successful applications.
|
||
*/
|
||
IRTL_DLLEXP
|
||
DWORD
|
||
LKR_Apply(
|
||
PLkrHashTable plkr,
|
||
LKR_PFnRecordAction pfnAction,
|
||
void* pvState,
|
||
LK_LOCKTYPE lkl);
|
||
|
||
/* Walk the hash table, applying pfnAction to any records that match
|
||
* pfnPredicate. Locks one subtable after another with either
|
||
* a (possibly shared) readlock or a writelock, according to lkl.
|
||
* Loop is aborted if pfnAction ever returns LKA_ABORT.
|
||
* Returns the number of successful applications.
|
||
*/
|
||
IRTL_DLLEXP
|
||
DWORD
|
||
LKR_ApplyIf(
|
||
PLkrHashTable plkr,
|
||
LKR_PFnRecordPred pfnPredicate,
|
||
LKR_PFnRecordAction pfnAction,
|
||
void* pvState,
|
||
LK_LOCKTYPE lkl);
|
||
|
||
/* Delete any records that match pfnPredicate.
|
||
* Locks one subtable after another with a writelock.
|
||
* Returns the number of deletions.
|
||
*
|
||
* Do *not* walk the hash table by hand with an iterator and call
|
||
* LKR_DeleteKey. The iterator will end up pointing to garbage.
|
||
*/
|
||
IRTL_DLLEXP
|
||
DWORD
|
||
LKR_DeleteIf(
|
||
PLkrHashTable plkr,
|
||
LKR_PFnRecordPred pfnPredicate,
|
||
void* pvState);
|
||
|
||
#endif /* LKR_APPLY_IF */
|
||
|
||
|
||
/* Check table for consistency. Returns 0 if okay, or the number of
|
||
* errors otherwise.
|
||
*/
|
||
IRTL_DLLEXP
|
||
int
|
||
LKR_CheckTable(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Remove all data from the table
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_Clear(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Number of elements in the table
|
||
*/
|
||
IRTL_DLLEXP
|
||
DWORD
|
||
LKR_Size(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Maximum possible number of elements in the table
|
||
*/
|
||
IRTL_DLLEXP
|
||
DWORD
|
||
LKR_MaxSize(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Is the hash table usable?
|
||
*/
|
||
IRTL_DLLEXP
|
||
BOOL
|
||
LKR_IsUsable(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Is the hash table consistent and correct?
|
||
*/
|
||
IRTL_DLLEXP
|
||
BOOL
|
||
LKR_IsValid(
|
||
PLkrHashTable plkr);
|
||
|
||
#ifdef LKR_EXPOSED_TABLE_LOCK
|
||
|
||
/* Lock the table (exclusively) for writing
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_WriteLock(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Lock the table (possibly shared) for reading
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_ReadLock(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Unlock the table for writing
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_WriteUnlock(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Unlock the table for reading
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_ReadUnlock(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Is the table already locked for writing?
|
||
*/
|
||
IRTL_DLLEXP
|
||
BOOL
|
||
LKR_IsWriteLocked(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Is the table already locked for reading?
|
||
*/
|
||
IRTL_DLLEXP
|
||
BOOL
|
||
LKR_IsReadLocked(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Is the table unlocked for writing?
|
||
*/
|
||
IRTL_DLLEXP
|
||
BOOL
|
||
LKR_IsWriteUnlocked(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Is the table unlocked for reading?
|
||
*/
|
||
IRTL_DLLEXP
|
||
BOOL
|
||
LKR_IsReadUnlocked(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Convert the read lock to a write lock. Note: another thread may acquire
|
||
* exclusive access to the table before this routine returns.
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_ConvertSharedToExclusive(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Convert the write lock to a read lock
|
||
*/
|
||
IRTL_DLLEXP
|
||
void
|
||
LKR_ConvertExclusiveToShared(
|
||
PLkrHashTable plkr);
|
||
|
||
#endif /* LKR_EXPOSED_TABLE_LOCK */
|
||
|
||
|
||
#ifdef __cplusplus
|
||
} // extern "C"
|
||
|
||
|
||
|
||
// Only provide iterators in the C++ interface. It's too hard to
|
||
// provide the correct ownership semantics in a typesafe way in C,
|
||
// and C users can always use the LKR_ApplyIf family of callback
|
||
// enumerators if they really need to walk the hashtable.
|
||
|
||
|
||
#ifdef LKR_STL_ITERATORS
|
||
|
||
#pragma message("STL iterators")
|
||
|
||
// needed for std::forward_iterator_tag, etc
|
||
# include <utility>
|
||
|
||
#include <irtldbg.h>
|
||
|
||
#define LKR_ITER_TRACE IRTLTRACE
|
||
|
||
|
||
class IRTL_DLLEXP LKR_Iterator
|
||
{
|
||
private:
|
||
friend IRTL_DLLEXP LKR_Iterator LKR_Begin(PLkrHashTable plkr);
|
||
friend IRTL_DLLEXP LKR_Iterator LKR_End(PLkrHashTable plkr);
|
||
|
||
// private ctor
|
||
LKR_Iterator(bool);
|
||
|
||
public:
|
||
// default ctor
|
||
LKR_Iterator();
|
||
// copy ctor
|
||
LKR_Iterator(const LKR_Iterator& rhs);
|
||
// assignment operator
|
||
LKR_Iterator& operator=(const LKR_Iterator& rhs);
|
||
// dtor
|
||
~LKR_Iterator();
|
||
|
||
// Increment the iterator to point to the next record, or to LKR_End()
|
||
bool Increment();
|
||
// Is the iterator valid?
|
||
bool IsValid() const;
|
||
|
||
// Returns the record that the iterator points to.
|
||
// Must point to a valid record.
|
||
const void* Record() const;
|
||
// Returns the key of the record that the iterator points to.
|
||
// Must point to a valid record.
|
||
const DWORD_PTR Key() const;
|
||
|
||
// Compare two iterators for equality
|
||
bool operator==(const LKR_Iterator& rhs) const;
|
||
// Compare two iterators for inequality
|
||
bool operator!=(const LKR_Iterator& rhs) const;
|
||
|
||
// pointer to implementation object
|
||
void* pImpl;
|
||
}; // class LKR_Iterator
|
||
|
||
|
||
/* Return iterator pointing to first item in table
|
||
*/
|
||
IRTL_DLLEXP
|
||
LKR_Iterator
|
||
LKR_Begin(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Return a one-past-the-end iterator. Always empty.
|
||
*/
|
||
IRTL_DLLEXP
|
||
LKR_Iterator
|
||
LKR_End(
|
||
PLkrHashTable plkr);
|
||
|
||
/* Insert a record
|
||
* Returns `true' if successful; iterResult points to that record
|
||
* Returns `false' otherwise; iterResult == End()
|
||
*/
|
||
IRTL_DLLEXP
|
||
bool
|
||
LKR_Insert(
|
||
PLkrHashTable plkr,
|
||
/* in */ const void* pvRecord,
|
||
/* out */ LKR_Iterator& riterResult,
|
||
/* in */ bool fOverwrite=false);
|
||
|
||
/* Erase the record pointed to by the iterator; adjust the iterator
|
||
* to point to the next record. Returns `true' if successful.
|
||
*/
|
||
IRTL_DLLEXP
|
||
bool
|
||
LKR_Erase(
|
||
PLkrHashTable plkr,
|
||
/* in,out */ LKR_Iterator& riter);
|
||
|
||
/* Erase the records in the range [riterFirst, riterLast).
|
||
* Returns `true' if successful.
|
||
*/
|
||
IRTL_DLLEXP
|
||
bool
|
||
LKR_Erase(
|
||
PLkrHashTable plkr,
|
||
/*in*/ LKR_Iterator& riterFirst,
|
||
/*in*/ LKR_Iterator& riterLast);
|
||
|
||
/* Find the (first) record that has its key == pnKey.
|
||
* If successful, returns `true' and iterator points to (first) record.
|
||
* If fails, returns `false' and iterator == End()
|
||
*/
|
||
IRTL_DLLEXP
|
||
bool
|
||
LKR_Find(
|
||
PLkrHashTable plkr,
|
||
/* in */ DWORD_PTR pnKey,
|
||
/* out */ LKR_Iterator& riterResult);
|
||
|
||
/* Find the range of records that have their keys == pnKey.
|
||
* If successful, returns `true', iterFirst points to first record,
|
||
* and iterLast points to one-beyond-the last such record.
|
||
* If fails, returns `false' and both iterators == End().
|
||
* Primarily useful when fMultiKeys == TRUE
|
||
*/
|
||
IRTL_DLLEXP
|
||
bool
|
||
LKR_EqualRange(
|
||
PLkrHashTable plkr,
|
||
/* in */ DWORD_PTR pnKey,
|
||
/* out */ LKR_Iterator& riterFirst, // inclusive
|
||
/* out */ LKR_Iterator& riterLast); // exclusive
|
||
|
||
#endif // LKR_STL_ITERATORS
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------
|
||
// A typesafe wrapper for PLkrHashTable
|
||
//
|
||
// * _Derived must derive from TypedLkrHashTable and provide certain member
|
||
// functions. It's needed for various downcasting operations.
|
||
// * _Record is the type of the record. PLkrHashTable will store
|
||
// pointers to _Record, as const void*.
|
||
// * _Key is the type of the key. _Key is used directly; i.e., it is
|
||
// not assumed to be a pointer type. PLkrHashTable assumes that
|
||
// the key is stored in the associated record. See the comments
|
||
// at the declaration of LKR_PFnExtractKey for more details.
|
||
//
|
||
// You may need to add the following line to your code to disable
|
||
// warning messages about truncating extremly long identifiers.
|
||
// #pragma warning (disable : 4786)
|
||
//
|
||
// The _Derived class should look something like this:
|
||
// class CDerived : public TypedLkrHashTable<CDerived, RecordType, KeyType>
|
||
// {
|
||
// public:
|
||
// CDerived()
|
||
// : TypedLkrHashTable<CDerived, RecordType, KeyType>("CDerived")
|
||
// {/*other ctor actions*/}
|
||
// static KeyType ExtractKey(const RecordType* pTest);
|
||
// static DWORD CalcKeyHash(const KeyType Key);
|
||
// static bool EqualKeys(const KeyType Key1, const KeyType Key2);
|
||
// static void AddRefRecord(RecordType* pRecord,LK_ADDREF_REASON lkar);
|
||
// // optional: other functions
|
||
// };
|
||
//
|
||
//--------------------------------------------------------------------
|
||
|
||
template <class _Derived, class _Record, class _Key>
|
||
class TypedLkrHashTable
|
||
{
|
||
public:
|
||
// convenient aliases
|
||
typedef _Derived Derived;
|
||
typedef _Record Record;
|
||
typedef _Key Key;
|
||
|
||
typedef TypedLkrHashTable<_Derived, _Record, _Key> HashTable;
|
||
|
||
#ifdef LKR_APPLY_IF
|
||
// LKR_ApplyIf() and LKR_DeleteIf(): Does the record match the predicate?
|
||
// Note: takes a Record*, not a const Record*. You can modify the
|
||
// record in Pred() or Action(), if you like, but if you do, you
|
||
// should use LKL_WRITELOCK to lock the table.
|
||
typedef LK_PREDICATE (WINAPI *PFnRecordPred) (Record* pRec, void* pvState);
|
||
|
||
// Apply() et al: Perform action on record.
|
||
typedef LK_ACTION (WINAPI *PFnRecordAction)(Record* pRec, void* pvState);
|
||
#endif // LKR_APPLY_IF
|
||
|
||
protected:
|
||
PLkrHashTable m_plkr;
|
||
|
||
// Wrappers for the typesafe methods exposed by the derived class
|
||
|
||
static const DWORD_PTR WINAPI
|
||
_ExtractKey(const void* pvRecord)
|
||
{
|
||
const _Record* pRec = static_cast<const _Record*>(pvRecord);
|
||
const _Key key = static_cast<const _Key>(_Derived::ExtractKey(pRec));
|
||
// I would prefer to use reinterpret_cast here and in _CalcKeyHash
|
||
// and _EqualKeys, but the stupid Win64 compiler thinks it knows
|
||
// better than I do.
|
||
return (const DWORD_PTR) key;
|
||
}
|
||
|
||
static DWORD WINAPI
|
||
_CalcKeyHash(const DWORD_PTR pnKey)
|
||
{
|
||
const _Key key = (const _Key) (DWORD_PTR) pnKey;
|
||
return _Derived::CalcKeyHash(key);
|
||
}
|
||
|
||
static BOOL WINAPI
|
||
_EqualKeys(const DWORD_PTR pnKey1, const DWORD_PTR pnKey2)
|
||
{
|
||
const _Key key1 = (const _Key) (DWORD_PTR) pnKey1;
|
||
const _Key key2 = (const _Key) (DWORD_PTR) pnKey2;
|
||
return _Derived::EqualKeys(key1, key2);
|
||
}
|
||
|
||
static void WINAPI
|
||
_AddRefRecord(const void* pvRecord, LK_ADDREF_REASON lkar)
|
||
{
|
||
_Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
|
||
_Derived::AddRefRecord(pRec, lkar);
|
||
}
|
||
|
||
|
||
#ifdef LKR_APPLY_IF
|
||
// Typesafe wrappers for Apply, ApplyIf, and DeleteIf.
|
||
|
||
class CState
|
||
{
|
||
public:
|
||
PFnRecordPred m_pfnPred;
|
||
PFnRecordAction m_pfnAction;
|
||
void* m_pvState;
|
||
|
||
CState(
|
||
PFnRecordPred pfnPred,
|
||
PFnRecordAction pfnAction,
|
||
void* pvState)
|
||
: m_pfnPred(pfnPred), m_pfnAction(pfnAction), m_pvState(pvState)
|
||
{}
|
||
};
|
||
|
||
static LK_PREDICATE WINAPI
|
||
_Pred(const void* pvRecord, void* pvState)
|
||
{
|
||
_Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
|
||
CState* pState = static_cast<CState*>(pvState);
|
||
|
||
return (*pState->m_pfnPred)(pRec, pState->m_pvState);
|
||
}
|
||
|
||
static LK_ACTION WINAPI
|
||
_Action(const void* pvRecord, void* pvState)
|
||
{
|
||
_Record* pRec = static_cast<_Record*>(const_cast<void*>(pvRecord));
|
||
CState* pState = static_cast<CState*>(pvState);
|
||
|
||
return (*pState->m_pfnAction)(pRec, pState->m_pvState);
|
||
}
|
||
#endif // LKR_APPLY_IF
|
||
|
||
public:
|
||
TypedLkrHashTable(
|
||
LPCSTR pszName, // An identifier for debugging
|
||
LK_TABLESIZE nTableSize, // Small/Med/Large number of elements
|
||
bool fMultiKeys=false // Allow multiple identical keys?
|
||
)
|
||
: m_plkr(NULL)
|
||
{
|
||
m_plkr = LKR_CreateTable(pszName, _ExtractKey, _CalcKeyHash,
|
||
_EqualKeys, _AddRefRecord,
|
||
nTableSize, fMultiKeys);
|
||
}
|
||
|
||
~TypedLkrHashTable()
|
||
{
|
||
LKR_DeleteTable(m_plkr);
|
||
}
|
||
|
||
LK_RETCODE InsertRecord(const _Record* pRec, bool fOverwrite=false)
|
||
{ return LKR_InsertRecord(m_plkr, pRec, fOverwrite); }
|
||
|
||
LK_RETCODE DeleteKey(const _Key key, bool fDeleteAllSame=false)
|
||
{
|
||
const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
|
||
DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
|
||
return LKR_DeleteKey(m_plkr, pnKey, fDeleteAllSame);
|
||
}
|
||
|
||
LK_RETCODE DeleteRecord(const _Record* pRec)
|
||
{ return LKR_DeleteRecord(m_plkr, pRec);}
|
||
|
||
// Note: returns a _Record**, not a const Record**. Note that you
|
||
// can use a const type for the template parameter to ensure constness.
|
||
LK_RETCODE FindKey(const _Key key, _Record** ppRec) const
|
||
{
|
||
if (ppRec == NULL)
|
||
return LK_BAD_RECORD;
|
||
*ppRec = NULL;
|
||
const void* pvRec = NULL;
|
||
const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
|
||
DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
|
||
LK_RETCODE lkrc = LKR_FindKey(m_plkr, pnKey, &pvRec);
|
||
*ppRec = static_cast<_Record*>(const_cast<void*>(pvRec));
|
||
return lkrc;
|
||
}
|
||
|
||
LK_RETCODE FindRecord(const _Record* pRec) const
|
||
{ return LKR_FindRecord(m_plkr, pRec);}
|
||
|
||
#ifdef LKR_APPLY_IF
|
||
DWORD Apply(PFnRecordAction pfnAction,
|
||
void* pvState=NULL,
|
||
LK_LOCKTYPE lkl=LKL_READLOCK)
|
||
{
|
||
IRTLASSERT(pfnAction != NULL);
|
||
if (pfnAction == NULL)
|
||
return 0;
|
||
|
||
CState state(NULL, pfnAction, pvState);
|
||
return LKR_Apply(m_plkr, _Action, &state, lkl);
|
||
}
|
||
|
||
DWORD ApplyIf(PFnRecordPred pfnPredicate,
|
||
PFnRecordAction pfnAction,
|
||
void* pvState=NULL,
|
||
LK_LOCKTYPE lkl=LKL_READLOCK)
|
||
{
|
||
IRTLASSERT(pfnPredicate != NULL && pfnAction != NULL);
|
||
if (pfnPredicate == NULL || pfnAction == NULL)
|
||
return 0;
|
||
|
||
CState state(pfnPredicate, pfnAction, pvState);
|
||
return LKR_ApplyIf(m_plkr, _Pred, _Action, &state, lkl);
|
||
}
|
||
|
||
DWORD DeleteIf(PFnRecordPred pfnPredicate, void* pvState=NULL)
|
||
{
|
||
IRTLASSERT(pfnPredicate != NULL);
|
||
if (pfnPredicate == NULL)
|
||
return 0;
|
||
|
||
CState state(pfnPredicate, NULL, pvState);
|
||
return LKR_DeleteIf(m_plkr, _Pred, &state);
|
||
}
|
||
#endif // LKR_APPLY_IF
|
||
|
||
int CheckTable() const
|
||
{ return LKR_CheckTable(m_plkr); }
|
||
|
||
void Clear()
|
||
{ return LKR_Clear(m_plkr); }
|
||
|
||
DWORD Size() const
|
||
{ return LKR_Size(m_plkr); }
|
||
|
||
DWORD MaxSize() const
|
||
{ return LKR_MaxSize(m_plkr); }
|
||
|
||
BOOL IsUsable() const
|
||
{ return LKR_IsUsable(m_plkr); }
|
||
|
||
BOOL IsValid() const
|
||
{ return LKR_IsValid(m_plkr); }
|
||
|
||
#ifdef LKR_EXPOSED_TABLE_LOCK
|
||
void WriteLock()
|
||
{ LKR_WriteLock(m_plkr); }
|
||
|
||
void ReadLock() const
|
||
{ LKR_ReadLock(m_plkr); }
|
||
|
||
void WriteUnlock()
|
||
{ LKR_WriteUnlock(m_plkr); }
|
||
|
||
void ReadUnlock() const
|
||
{ LKR_ReadUnlock(m_plkr); }
|
||
|
||
BOOL IsWriteLocked() const
|
||
{ return LKR_IsWriteLocked(m_plkr); }
|
||
|
||
BOOL IsReadLocked() const
|
||
{ return LKR_IsReadLocked(m_plkr); }
|
||
|
||
BOOL IsWriteUnlocked() const
|
||
{ return LKR_IsWriteUnlocked(m_plkr); }
|
||
|
||
BOOL IsReadUnlocked() const
|
||
{ return LKR_IsReadUnlocked(m_plkr); }
|
||
|
||
void ConvertSharedToExclusive() const
|
||
{ LKR_ConvertSharedToExclusive(m_plkr); }
|
||
|
||
void ConvertExclusiveToShared() const
|
||
{ LKR_ConvertExclusiveToShared(m_plkr); }
|
||
#endif // LKR_EXPOSED_TABLE_LOCK
|
||
|
||
|
||
#ifdef LKR_STL_ITERATORS
|
||
friend class LKR_Iterator;
|
||
|
||
// TODO: const_iterator
|
||
|
||
public:
|
||
class iterator
|
||
{
|
||
friend class TypedLkrHashTable<_Derived, _Record, _Key>;
|
||
|
||
protected:
|
||
LKR_Iterator m_iter;
|
||
|
||
iterator(
|
||
LKR_Iterator& rhs)
|
||
: m_iter(rhs)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::prot ctor, this=%p, rhs=%p\n"),
|
||
this, &rhs);
|
||
}
|
||
|
||
public:
|
||
typedef std::forward_iterator_tag iterator_category;
|
||
typedef _Record value_type;
|
||
typedef ptrdiff_t difference_type;
|
||
typedef size_t size_type;
|
||
typedef value_type& reference;
|
||
typedef value_type* pointer;
|
||
|
||
iterator()
|
||
: m_iter()
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::default ctor, this=%p\n"), this);
|
||
}
|
||
|
||
iterator(
|
||
const iterator& rhs)
|
||
: m_iter(rhs.m_iter)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::copy ctor, this=%p, rhs=%p\n"),
|
||
this, &rhs);
|
||
}
|
||
|
||
iterator& operator=(
|
||
const iterator& rhs)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::operator=, this=%p, rhs=%p\n"),
|
||
this, &rhs);
|
||
m_iter = rhs.m_iter;
|
||
return *this;
|
||
}
|
||
|
||
~iterator()
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::dtor, this=%p\n"), this);
|
||
}
|
||
|
||
reference operator*() const
|
||
{
|
||
void* pvRecord = const_cast<void*>(m_iter.Record());
|
||
return reinterpret_cast<reference>(pvRecord);
|
||
}
|
||
|
||
pointer operator->() const { return &(operator*()); }
|
||
|
||
// pre-increment
|
||
iterator& operator++()
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::pre-increment, this=%p\n"), this);
|
||
m_iter.Increment();
|
||
return *this;
|
||
}
|
||
|
||
// post-increment
|
||
iterator operator++(int)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::post-increment, this=%p\n"), this);
|
||
iterator iterPrev = *this;
|
||
m_iter.Increment();
|
||
return iterPrev;
|
||
}
|
||
|
||
bool operator==(
|
||
const iterator& rhs) const
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::operator==, this=%p, rhs=%p\n"),
|
||
this, &rhs);
|
||
return m_iter == rhs.m_iter;
|
||
}
|
||
|
||
bool operator!=(
|
||
const iterator& rhs) const
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::operator!=, this=%p, rhs=%p\n"),
|
||
this, &rhs);
|
||
return m_iter != rhs.m_iter;
|
||
}
|
||
|
||
_Record* Record() const
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::Record, this=%p\n"), this);
|
||
return reinterpret_cast<_Record*>(
|
||
const_cast<void*>(m_iter.Record()));
|
||
}
|
||
|
||
_Key Key() const
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::Key, this=%p\n"), this);
|
||
return reinterpret_cast<_Key>(
|
||
reinterpret_cast<void*>(m_iter.Key()));
|
||
}
|
||
}; // class iterator
|
||
|
||
// Return iterator pointing to first item in table
|
||
iterator begin()
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::begin()\n"));
|
||
return LKR_Begin(m_plkr);
|
||
}
|
||
|
||
// Return a one-past-the-end iterator. Always empty.
|
||
iterator end() const
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::end()\n"));
|
||
return LKR_End(m_plkr);
|
||
}
|
||
|
||
template <class _InputIterator>
|
||
TypedLkrHashTable(
|
||
LPCSTR pszName, // An identifier for debugging
|
||
_InputIterator f, // first element in range
|
||
_InputIterator l, // one-beyond-last element
|
||
LK_TABLESIZE nTableSize, // Small/Med/Large number of elements
|
||
bool fMultiKeys=false // Allow multiple identical keys?
|
||
)
|
||
{
|
||
m_plkr = LKR_CreateTable(pszName, _ExtractKey, _CalcKeyHash,
|
||
_EqualKeys, _AddRefRecord,
|
||
nTableSize, fMultiKeys);
|
||
insert(f, l);
|
||
}
|
||
|
||
template <class _InputIterator>
|
||
void insert(_InputIterator f, _InputIterator l)
|
||
{
|
||
for ( ; f != l; ++f)
|
||
InsertRecord(&(*f));
|
||
}
|
||
|
||
bool
|
||
Insert(
|
||
const _Record* pRecord,
|
||
iterator& riterResult,
|
||
bool fOverwrite=false)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::Insert\n"));
|
||
return LKR_Insert(m_plkr, pRecord, riterResult.m_iter, fOverwrite);
|
||
}
|
||
|
||
bool
|
||
Erase(
|
||
iterator& riter)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::Erase\n"));
|
||
return LKR_Erase(m_plkr, riter.m_iter);
|
||
}
|
||
|
||
bool
|
||
Erase(
|
||
iterator& riterFirst,
|
||
iterator& riterLast)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::Erase2\n"));
|
||
return LKR_Erase(m_plkr, riterFirst.m_iter, riterLast.m_iter);
|
||
}
|
||
|
||
bool
|
||
Find(
|
||
const _Key key,
|
||
iterator& riterResult)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::Find\n"));
|
||
const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
|
||
DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
|
||
return LKR_Find(m_plkr, pnKey, riterResult.m_iter);
|
||
}
|
||
|
||
bool
|
||
EqualRange(
|
||
const _Key key,
|
||
iterator& riterFirst,
|
||
iterator& riterLast)
|
||
{
|
||
LKR_ITER_TRACE(_TEXT("Typed::EqualRange\n"));
|
||
const void* pvKey = reinterpret_cast<const void*>((DWORD_PTR)(key));
|
||
DWORD_PTR pnKey = reinterpret_cast<DWORD_PTR>(pvKey);
|
||
return LKR_EqualRange(m_plkr, pnKey, riterFirst.m_iter,
|
||
riterLast.m_iter);
|
||
}
|
||
|
||
#undef LKR_ITER_TRACE
|
||
|
||
#endif // LKR_STL_ITERATORS
|
||
|
||
}; // class TypedLkrHashTable
|
||
|
||
#endif /* __cplusplus */
|
||
|
||
#endif /* __LKR_HASH_H__ */
|