NT4/private/ole32/com/inc/skiplist.hxx
2020-09-30 17:12:29 +02:00

671 lines
24 KiB
C++

//--------------------------------------------------------------------------
//
// Copyright (C) 1992, Microsoft Corporation.
//
// File: skiplist.hxx
//
// Contents: Skip list classes.
//
// CSkipList -- A class that implements skip lists.
// The skip list manages the efficient lookup
// of "Entry"s which are inserted into the skip list
// by the client. "Entry"s are represented by
// a pointer passed into the CSkipList::Insert()
// method by the client.
// These pointers can be retrieved by the Search()
// method which takes a pointer to a "Base", (which
// acts as a key.)
//
// "Entry"s are always derived from "Base"s. That is,
// the CSkipList::Search() method assumes that a
// simple pointer manipulation can turn a pointer to
// an "Entry" that it has in the list into a pointer
// to "Base" that it needs for comparison purposes.
// Once the pointer is converted, the user-supplied
// comparison function is called to facilitate the
// search.
//
// An example
// ----------
//
// We have a "Base" class which is a key. Pointers
// to CKeys will be passed to CSkipList::Search()
// and to the constructor of CSkipList (the max key)
//
// class CKey
// {
// int keyvalue; // a simple integer key
// public:
// static int Compare(void *pkey1, void *pkey2)
// {
// return ((CKey*)pkey1)->keyvalue <
// ((CKey*)pkey2)->keyvalue;
// }
//
// static int Delete(const *pentry)
// {
// delete pentry;
// }
// };
//
// We have an "Entry" class which is-a key and also
// is-a CExtra (whatever the client needs/wants.)
// Pointers to CDataEntrys are passed to
// CSkipList::Insert. Also, pointers to CDataEntrys
// are converted to pointers to CKeys by the addition
// of an offset which is set once for all skip list
// entries when passed to CSkipList constructor.
//
// class CDataEntry : public CExtra, private CKey
// {
// char *pszData;
// };
//
// we can easily make a skip list out of the
// CDataEntries as follows:
//
// static int maxkey=32677;
//
// CSkipList mysl((LPFNCOMPARE)(CKey::Compare),
// (LPFNDELETE)(CKey::Delete),
// OFFSETBETWEEN(CDataEntry, CKey),
// SKIPLIST_PRIVATE,
// &maxkey,
// 10,
// hr);
//
// Notes on parameters:
// 1. Pass in the address of the comparison function.
// This comparison function is called by
// CSkipList::Search(). The first paramter to
// the compare function is a pointer to the "Base"
// part of an "Entry" in the skip list (computed by
// adding offset computed by OFFSETBETWEEN macro).
// Search() routine makes the determination as to which
// "Entry"s to compare based on internal details of
// the skip list mechanism.
// The second parameter to the Compare function is the
// "Base" pointer parameter passed to Search.
//
// 2. Pass in the address of the deletion function.
// This function is called only as part of the
// destructor of CSkipList and allows the deletion
// of any entries left in the skip list. If
// the skip list is used in such a way as to not
// "own" the pointers within, then this function
// will simple return after doing nothing.
// If the skip list is used in such a way as to
// actually "own" the objects inserted, then
// the user-supplied delete function should be used
// to cleanup correctly.
//
// 3. Since all entries in the list must be used as both
// "key" and "data", the EntryToKeyOffset gives
// the offset in bytes between the "Entry" part and
// the "Base" part. In the example, the offset would
// be the size of CExtra.
//
// See method description for details on other parameters.
//
// Insert an entry:
// ----------------
// mysl.Insert(new CDataEntry);
//
// Lookup an entry:
// ----------------
// CKey key(some param to ctr);
// mysl.Search(&key); // returns a pointer to respective
// // CDataEntry *
//
// CSkipListEntry -- class used privately by CSkipList.
//
// History: 06-May-92 Ricksa Created
// 18-May-94 BruceMa Fixed scm memory leak in SKIP_LIST_ENTRY
// 28-Jun-94 BruceMa Memory sift fixes
// 04-Oct-94 BillMo Changed from macro to class implementation.
// Added comments above and to skip list methods
// after demacroization.
//
//--------------------------------------------------------------------------
#ifndef __SKIPLIST_HXX__
#define __SKIPLIST_HXX__
#include <scmmem.hxx>
// Used by insert/delete algorithms to preallocate array on stack.
// This is valid for skip lists with <= 64K elements.
#define SKIP_LIST_MAX 16
// Set up the generator for skip list levels
void InitSkLevelGenerator(void);
// Get a level from the generator
int GetSkLevel(int cMax);
//+-------------------------------------------------------------------------
//
// Macro: OFFSETBETWEEN
//
// Parameters: [Entry] -- class name of the class which forms entries
// in the skip list. see example below.
// MUST BE DERIVED from class named in parameter
// 'Base'
//
// [Base] -- class name of the class which is the 'key'
// for the skip list entries
//
// Purpose: Encapsulate calculation of offset that the skip list uses
// to convert pointers to "Entrys" to pointers to "Bases"
//
// History: 04-Nov-94 Ricksa Created
//
// Notes: VERY IMPORTANT NOTE: "Entry" must be derived from "Base"
//
//--------------------------------------------------------------------------
#define OFFSETBETWEEN(Entry, Base) \
((char*)( (const Base*)((const Entry*)0x1000 ))) - \
((char*)( (const Base*)0x1000))
//+-------------------------------------------------------------------------
//
// The following defines control how allocation is done by the skip list.
// The fSharedAlloc parameter of CSkipList::CSkipList can either be:
//
// SKIPLIST_SHARED -- in x86 Windows builds will cause the shared allocator
// to be used for CSkipListEntrys.
// (In NT builds will currently be privately allocated.)
//
// SKIPLIST_PRIVATE -- in all build environments use PrivMemAlloc.
//
//--------------------------------------------------------------------------
#define SKIPLIST_SHARED TRUE
#define SKIPLIST_PRIVATE FALSE
//+-------------------------------------------------------------------------
//
// Documentation aid:
//
// Since CSkipLists do not know the actual classes to be inserted/deleted
// there will always be a cast involved in using the comparison function.
// For this reason the typedefs below act as a documentation aid and
// reminder that the pointers are expected to point to particular
// meta-types of objects.
//
// "Base" is used where a pointer to the "key" is expected. (e.g. the
// Search method.)
//
// "Entry" is used where a pointer to the derived class (data+key) is
// expected (e.g. the Insert method.)
//
//--------------------------------------------------------------------------
typedef void Base;
typedef void Entry;
//+-------------------------------------------------------------------------
//
// Some type definitions.
//
//--------------------------------------------------------------------------
class CSkipListEntry;
// note: the name CSkipListEnum is used so that the details of the
// enumerator are hidden and can be expanded in the future without
// have to modify the source of clients.
typedef CSkipListEntry * CSkipListEnum;
//+-------------------------------------------------------------------------
//
// Function type: LPFNCOMPARE
//
// Purpose: Provide parameter signature for comparison function used
// by skip lists. The user implementation should
// compare the two keys and return <0, >0 or ==0.
//
// Arguments: [pkey1] -- pointer to the "Base" part of an "Entry" in
// the skip list as calculated by adding the
// "EntryToKeyOffset" value (passed to CSkipList
// constructor) to the address of an "Entry" in the
// list.
// [pkey2] -- the address passed to CSkipList::Search which is
// the address of a "Base" key being used to locate
// the respective entry in skip list.
//
// History: 04-Nov-94 BillMo Created
//
//--------------------------------------------------------------------------
typedef int (*LPFNCOMPARE)(Base *pkey1, Base *pkey2);
//+-------------------------------------------------------------------------
//
// Function type: LPFNDELETE
//
// Purpose: Provide parameter signature for deletion function used
// by skip list's destructor. The user implementation should
// delete the entry as appropriate (i.e. should delete it
// if the destruction of the skip list without deleting
// would cause leaks.)
//
// i.e. if the skip list owns the pointers, delete them in this
// function, otherwise don't.
//
// Arguments: [pentry] -- pointer to an "Entry" part of an "Entry"
// originally passed to CSkipList::Insert.
//
// History: 04-Nov-94 BillMo Created
//
//--------------------------------------------------------------------------
typedef void (*LPFNDELETE)(Entry *);
//+-------------------------------------------------------------------------
//
// Class: CSkipList
//
// Purpose: Implements head of a skip list.
//
// Interface: CSkipList -- constructor
// ~CSkipList -- destructor (calls user-supplied deletion
// routine)
// Search -- find item in list
// Insert -- add new item to the list (insert the pointer)
// Remove -- remove item from the list (return the pointer)
// (this doesn't delete the pointer or call
// lpfnDelete.)
// Replace -- replace the entry (MUST be same key value.)
// First -- get first entry in skip list.
// Next -- get next entry given by CSkipListEnum param.
// GetList -- get the list
//
// History: 06-May-92 Ricksa Created
// 04-Nov-94 BillMo Update comments.
//
// Notes: CSkipList has a nested class: CSkipListEntry
//
//--------------------------------------------------------------------------
class CSkipList
{
public:
CSkipList(
LPFNCOMPARE lpfnCompare,
LPFNDELETE lpfnDelete,
const int EntryToKeyOffset,
BOOL fSharedAlloc,
Base * pvMaxKey,
const int cMaxLevel,
HRESULT & hr);
~CSkipList(void);
Entry * Search(const Base * skey);
Entry * Insert(Entry *pEntryNew);
Entry * Remove(Base * BaseKey);
Entry * Replace(Base * BaseKey, Entry * pEntryNew);
Entry * First(CSkipListEnum *psle);
Entry * Next(CSkipListEnum *psle);
CSkipListEntry * GetList(void);
private:
void * Allocate(ULONG cb);
void Deallocate(void *pv);
CSkipListEntry * FillUpdateArray(const Base *BaseKey,
CSkipListEntry **ppEntryUpdate);
Entry * _Search(const Base * BaseKey,
CSkipListEntry **ppPrivEntry);
int _cCurrentMaxLevel;
int _cMaxLevel;
CSkipListEntry * _pEntryHead;
BOOL _fSharedAlloc;
const int _EntryToKeyOffset;
LPFNCOMPARE _lpfnCompare;
LPFNDELETE _lpfnDelete;
};
//+-------------------------------------------------------------------
//
// Member: CSkipList::Allocate
//
// Synopsis: Allocate the requested number of bytes from the
// shared heap, or private heap, depending on the
// state of CSkipList::_fSharedAlloc
// If _fSharedAlloc is TRUE, then ScmMemAlloc is called,
// otherwise PrivMemAlloc is used.
//
// Arguments: [cb] -- Number of bytes requsted.
//
// Returns: Pointer to allocated memory, or NULL on failure.
//
//--------------------------------------------------------------------
inline void *
CSkipList::Allocate(ULONG cb)
{
if (_fSharedAlloc)
return ScmMemAlloc(cb);
else
return PrivMemAlloc(cb);
}
//+-------------------------------------------------------------------
//
// Member: CSkipList::Deallocate
//
// Synopsis: Dellocate the requested block from the
// shared heap, or private heap, depending on the
// state of CSkipList::_fSharedAlloc
// If _fSharedAlloc is TRUE, then ScmMemFree is called,
// otherwise PrivMemFree is used.
//
// Arguments: [pv] -- Pointer to block to free.
//
//--------------------------------------------------------------------
inline void
CSkipList::Deallocate(void *pv)
{
if (_fSharedAlloc)
ScmMemFree(pv);
else
PrivMemFree(pv);
}
//+-------------------------------------------------------------------------
//
// Class: CSkipListEntry
//
// Purpose: Implements an entry in a skip list.
//
// Interface: Initialize -- initialize object
// GetSize -- calculate size to allocate for object
// cLevel -- returns number of forward pointers
// GetForward -- returns the ith forward pointer
// SetForward -- sets ith forward pointer
// GetBase -- return base address of array
// GetEntry -- get user-supplied object pointer
// GetKey -- get key of user-supplied object
//
// History: 06-May-92 Ricksa Created
// 04-Nov-94 BillMo Demacroize and update comments.
// Notes:
//
//--------------------------------------------------------------------------
class CSkipListEntry
{
private:
friend class CSkipList;
CSkipListEntry(); // declared but not defined
void Initialize(Entry * pvEntry,
const int cEntries,
BOOL fHead);
static int GetSize(const int cEntries);
int cLevel(void);
CSkipListEntry* GetForward(int iCur);
void SetForward(int iCur, CSkipListEntry* pnew);
CSkipListEntry ** GetBase(void);
Entry * GetEntry(void);
Base * GetKey(const int EntryToKeyOffset);
int _cEntries:24;
int _fHead:8;
Entry * _pvEntry;
CSkipListEntry * _apBaseForward[1];
};
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::Initialize
//
// Synopsis: Set up member variables of skip list entry
//
// Arguments: [pvEntry] -- client-supplied "entry"/"base" pointer
// [cEntries] -- count of entries requested.
// [fHead] -- TRUE if head of skip list
//
// Returns: size in bytes required for an object to contain requested
// number of entries.
//
//--------------------------------------------------------------------
inline void
CSkipListEntry::Initialize(Entry * pvEntry,
const int cEntries,
BOOL fHead)
{
_pvEntry = pvEntry;
_cEntries = cEntries;
_fHead = fHead;
}
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::GetSize
//
// Synopsis: Calculate the size in bytes required for a CSkipListEntry
// to contain [cEntries] elements.
//
// Arguments: [cEntries] -- count of entries requested.
//
// Returns: size in bytes required for an object to contain requested
// number of entries.
//
//--------------------------------------------------------------------
inline int
CSkipListEntry::GetSize(const int cEntries)
{
return sizeof(CSkipListEntry) +
(cEntries-1)*sizeof(CSkipListEntry *);
}
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::cLevel
//
// Synopsis: Returns the level (entry count) of skip list entry.
//
//--------------------------------------------------------------------
inline int CSkipListEntry::cLevel(void)
{
return _cEntries;
}
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::GetForward
//
// Synopsis: Return the [iCur]'th forward pointer.
//
//--------------------------------------------------------------------
inline CSkipListEntry* CSkipListEntry::GetForward(int iCur)
{
return _apBaseForward[iCur];
}
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::SetForward
//
// Synopsis: Set the [iCur]'th forward pointer to [pnew]
//
//--------------------------------------------------------------------
inline void CSkipListEntry::SetForward(int iCur, CSkipListEntry* pnew)
{
_apBaseForward[iCur] = pnew;
}
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::GetBase
//
// Synopsis: Get the base address of the forward pointer array.
//
//--------------------------------------------------------------------
inline CSkipListEntry **CSkipListEntry::GetBase(void)
{
return _apBaseForward;
}
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::GetEntry
//
// Synopsis: Get pointer to the client-supplied object that was
// inserted into the skiplist using CSkipList::Insert.
//
//--------------------------------------------------------------------
inline Entry * CSkipListEntry::GetEntry(void)
{
Win4Assert(!_fHead);
return(_pvEntry);
}
//+-------------------------------------------------------------------
//
// Member: CSkipListEntry::GetKey
//
// Synopsis: Return a pointer to the key of the object passed into
// CSkipList::Insert or CSkipList::CSkipList.
//
// Arguments: [EntryToKeyOffset] -- amount to add to the address of
// the client-supplied entry in
// order to get the address of the
// key.
//
//
// Returns: Pointer to "Base", otherwise known as key, of client
// inserted object (or max key)
//
// Notes: If _fHead is TRUE, this indicates that the skip list
// entry is the one allocated by CSkipList::CSkipList.
// In this case, the pointer to the user-supplied entry
// is actually a pointer to the key (since we don't need
// a full entry object for use as a maximum key value.)
// If _fHead is FALSE, then the object was allocated by
// CSkipList::Insert and thus the entry pointer needs
// to be adjusted in order to get the address of the key.
//
//--------------------------------------------------------------------
inline Base * CSkipListEntry::GetKey(const int EntryToKeyOffset)
{
if (_fHead)
return (Base*)_pvEntry;
else
return (Base*) (((char*)_pvEntry)+EntryToKeyOffset);
}
//+-------------------------------------------------------------------------
//
// Macro: DEFINE_TYPE_SAFE_SKIPLIST
//
// Purpose: Generates a wrapper class to provide type safe skip lists.
//
// Arguments: [NewType] -- The name of the new class (derives privately
// from CSkipList.
// [EntryType] -- type of entry (must be derived from BaseType.
// [BaseType] -- type of key
//
// History: 04-Nov-94 BillMo Created.
//
// Notes:
//
//--------------------------------------------------------------------------
#define DEFINE_TYPE_SAFE_SKIPLIST(NewType,EntryType,BaseType)\
class NewType : private CSkipList\
{\
public:\
inline NewType(\
LPFNCOMPARE lpfnCompare,\
LPFNDELETE lpfnDelete,\
const int EntryToKeyOffset,\
BOOL fSharedAlloc,\
Base * pvMaxKey,\
const int cMaxLevel,\
HRESULT & hr) :\
CSkipList(lpfnCompare,\
lpfnDelete,\
EntryToKeyOffset,\
fSharedAlloc,\
pvMaxKey,\
cMaxLevel,\
hr) {}\
inline EntryType * Search(const BaseType * skey)\
{\
return (EntryType*) CSkipList::Search(skey);\
}\
inline EntryType * Insert(EntryType *pEntryNew)\
{\
return (EntryType*) CSkipList::Insert(pEntryNew);\
}\
inline EntryType * Remove(BaseType * BaseKey)\
{\
return (EntryType*) CSkipList::Remove(BaseKey);\
}\
inline EntryType * Replace(BaseType * BaseKey, EntryType * pEntryNew)\
{\
return (EntryType*) CSkipList::Replace(BaseKey, pEntryNew);\
}\
inline EntryType * First(CSkipListEnum *psle)\
{\
return (EntryType*) CSkipList::First(psle);\
}\
inline EntryType * Next(CSkipListEnum *psle)\
{\
return (EntryType*) CSkipList::Next(psle);\
}\
inline CSkipListEntry * GetList(void)\
{\
return CSkipList::GetList();\
}\
};
#endif // __SKIPLIST_HXX__