2025-04-27 07:49:33 -04:00

609 lines
10 KiB
C++

/*++
Microsoft Windows NT RPC Name Service
Copyright (c) 1996-1997 Microsoft Corporation
Module Name:
linklist.hxx
Abstract:
This module contains definitions of class CLinkList, and templates derived
from it for type safety.Multithreading safety is assumed to be enforced
by external locking.
Author:
Satish Thatte (SatishT) 03/12/96
--*/
#ifndef __LINKLIST_HXX_
#define __LINKLIST_HXX_
#include <or.hxx>
// For the moment, we only permit linked lists of hash table items.
// This may be restructured in future.
struct ISearchKey;
class CTableElement;
typedef CTableElement IDataItem;
/*++
Class Definition:
CLinkList
Abstract:
This is a simple linked list class. It has a private Link class.
--*/
class CLinkList
{
friend class CLinkListIterator;
public:
struct Link
{
// declare the members needed for a page static allocator
DECL_PAGE_ALLOCATOR
Link * _pNext;
IDataItem * _pData;
#if DBG
void IsValid();
DECLARE_VALIDITY_CLASS(CLinkList)
#endif
Link(IDataItem * a, Link * n);
// declare the page-allocator-based new and delete operators
DECL_NEW_AND_DELETE
};
protected:
Link * _pLnkFirst;
// two protected functions for specialized use when reshuffling
// lists -- among other things, reference counts are not changed
// since the reference is deemed to be held by the link
Link * PopLink()
{
Link * pResult = _pLnkFirst;
if (pResult != NULL)
{
_pLnkFirst = _pLnkFirst->_pNext;
pResult->_pNext = NULL;
}
return pResult;
}
void
PushLink(Link * pLink)
{
ASSERT(pLink != NULL);
pLink->_pNext = _pLnkFirst;
_pLnkFirst = pLink;
}
public:
#if DBG
void IsValid()
{
if (_pLnkFirst)
{
ASSERT(!IsBadWritePtr(_pLnkFirst,sizeof(Link)));
_pLnkFirst->IsValid();
}
}
DECLARE_VALIDITY_CLASS(CLinkList)
#endif
CLinkList()
{
_pLnkFirst = NULL;
}
~CLinkList()
{
Clear();
}
void Init()
{
_pLnkFirst = NULL;
}
// Is there anything in this list?
BOOL IsEmpty();
USHORT Size();
// Insert at the beginning, disallowing duplicates
ORSTATUS Insert(IDataItem * pData);
// Just insert at the beginning, ignoring duplicates
ORSTATUS Push(IDataItem * pData);
// Remove first item and return it
IDataItem * Pop();
// Remove the specified item and return it
IDataItem * Remove(ISearchKey&);
// Remove a list of items
void Remove(CLinkList&);
// Find the specified item and return it
IDataItem * Find(ISearchKey&);
// delete all links, but not the data items
void Clear();
void Transfer(CLinkList& target);
ORSTATUS Merge(CLinkList& source);
};
/*++
Class Definition:
CLinkListIterator
Abstract:
An iterator class for traversing a CLinkList.
--*/
class CLinkListIterator {
CLinkList::Link * _pIter; // the current link
public:
CLinkListIterator() : _pIter(NULL) {}
void Init(CLinkList& source);
IDataItem * Next(); // advance the iterator and return _pNext IDataItem
BOOL Finished(); // anything further coming?
};
// Forward declarations
template <class Data> class TCSafeLinkListIterator;
template <class Data> class TCCacheList;
/*++
Template Class Definition:
TCSafeLinkList
Abstract:
The template TCSafeLinkList make it easy to produce "type safe" incarnations of
the CLinkList classe, avoiding the use of casts in client code.
Note that Data must be a subtype of IDataItem.
--*/
template <class Data>
class TCSafeLinkList : protected CLinkList
{
friend class TCSafeLinkListIterator<Data>;
friend class CResolverHashTable;
friend class TCCacheList<Data>;
public:
void * operator new(size_t s)
{
return OrMemAlloc(s);
}
void operator delete(void * p) // do not inherit this!
{
OrMemFree(p);
}
#if DBG
void IsValid()
{
CLinkList::IsValid();
}
#endif // DBG
void Init()
{
CLinkList::Init();
}
BOOL IsEmpty()
{
return CLinkList::IsEmpty();
}
USHORT Size()
{
return CLinkList::Size();
}
ORSTATUS Insert(Data * pData)
{
return CLinkList::Insert(pData);
}
ORSTATUS Push(Data * pData)
{
return CLinkList::Push(pData);
}
Data * Pop()
{
return (Data *) CLinkList::Pop();
}
Data * Remove(ISearchKey& sk)
{
return (Data *) CLinkList::Remove(sk);
}
void Remove(TCSafeLinkList& NotWanted)
{
CLinkList::Remove(NotWanted);
}
Data * Find(ISearchKey& sk)
{
return (Data *) CLinkList::Find(sk);
}
void Clear()
{
CLinkList::Clear();
}
void Transfer(TCSafeLinkList& target)
{
CLinkList::Transfer(target);
}
ORSTATUS Merge(TCSafeLinkList& source)
{
return CLinkList::Merge(source);
}
};
/*++
Template Class Definition:
TCSafeLinkListIterator
Abstract:
The iterator for TCSafeLinkLists.
--*/
template <class Data>
class TCSafeLinkListIterator : private CLinkListIterator {
public:
void Init(TCSafeLinkList<Data>& l)
{
CLinkListIterator::Init(l);
}
Data * Next()
{
return (Data *) CLinkListIterator::Next();
}
BOOL Finished()
{
return CLinkListIterator::Finished();
}
};
#define DEFINE_LIST(DATA) \
typedef TCSafeLinkList<DATA> DATA##List; \
typedef TCSafeLinkListIterator<DATA> DATA##ListIterator;
//
// Inline methods for CLinkList
//
inline
void
CLinkList::Transfer(CLinkList& target)
{
VALIDATE_METHOD
target._pLnkFirst = _pLnkFirst;
_pLnkFirst = NULL;
}
inline
CLinkList::Link::Link(IDataItem * a, Link * n)
{
_pData = a;
_pNext = n;
}
inline
void
CLinkList::Clear()
{
VALIDATE_METHOD
IDataItem * pCurr;
while (pCurr = Pop());
}
inline
BOOL
CLinkList::IsEmpty()
{
VALIDATE_METHOD
return _pLnkFirst == NULL;
}
//
// Inline methods for CLinkListIterator
//
inline
void
CLinkListIterator::Init(CLinkList& source)
{
_pIter = source._pLnkFirst;
}
inline
IDataItem*
CLinkListIterator::Next() { // advance the iterator and return _pNext IDataItem
if (!_pIter) return NULL;
IDataItem* result = _pIter->_pData;
_pIter = _pIter->_pNext;
return result;
}
inline
BOOL
CLinkListIterator::Finished()
{
return _pIter == NULL;
}
//
// The CCacheList class below implements an LRU Cache based on linked lists
// The CacheItem type must be derived fro CTableElement as usual
//
template <class CacheItem>
class TCCacheList
{
public:
TCCacheList(USHORT limit) : _limit(limit)
{
ASSERT(_limit > 0);
}
BOOL IsEmpty()
{
return _myList.IsEmpty();
}
BOOL IsFull()
{
return Size() == _limit;
}
USHORT Size()
{
return _myList.Size();
}
CacheItem * Pop()
{
return _myList.Pop();
}
// Returns removed item if any
ORSTATUS Insert(CacheItem * pData, CacheItem * &pRemovedItem);
// This Remove takes an optional second parameter which, if non-null,
// is used to validate the remove -- the parameter is the item we
// want to remove, so if the list contains something else at the given
// key we just put it back and report failure by returning NULL
CacheItem * Remove(ISearchKey& sk, CacheItem *pMatchItem = NULL);
CacheItem * Find(ISearchKey& sk);
void Clear()
{
_myList.Clear();
}
private:
// Called only if the list is full, which means it is not empty
// Returns the removed CacheItem
CacheItem *RemoveLast();
TCSafeLinkList<CacheItem> _myList;
USHORT _limit;
};
//
// TCCacheList method templates
//
template <class CacheItem>
CacheItem *
TCCacheList<CacheItem>::Remove(
ISearchKey& sk,
CacheItem *pMatchItem
)
{
CacheItem* pRemovedItem = _myList.Remove(sk);
if (
(pMatchItem == NULL) || // don't care
(pRemovedItem == NULL) || // already removed
(pRemovedItem == pMatchItem) // matched
)
{
return pRemovedItem;
}
else // Insert and hope for the best
{
_myList.Insert(pRemovedItem);
return NULL;
}
}
template <class CacheItem>
CacheItem *
TCCacheList<CacheItem>::Find(ISearchKey& sk)
{
// Can't use Remove here because the refcount may drop to zero
CacheItem *pItem = _myList.Find(sk);
ORSTATUS status;
if (pItem != NULL)
{
pItem->Reference(); // to keep it alive
Remove(sk);
status = _myList.Insert(pItem);
pItem->Release(); // could be last reference if insert failed
if (status != OR_OK)
{
pItem = NULL; // we may have just lost it
}
}
return pItem;
}
template <class CacheItem>
ORSTATUS
TCCacheList<CacheItem>::Insert(
CacheItem * pData,
CacheItem * &pRemovedItem
)
{
pRemovedItem = NULL;
// Find also adjusts LRU status
CacheItem *pDummy = Find(*pData);
if (pDummy != NULL)
{
ASSERT(pDummy == pData);
return OR_OK;
}
if (IsFull()) // if not a duplicate and the list is full
{
pRemovedItem = RemoveLast(); // remove the least recently used item
}
return _myList.Insert(pData);
}
template <class CacheItem>
CacheItem *
TCCacheList<CacheItem>::RemoveLast()
{
TCSafeLinkList<CacheItem>::Link *pLink = _myList._pLnkFirst;
while (pLink->_pNext != NULL)
{
pLink = pLink->_pNext;
}
// save this because the remove below will destroy the Link
CacheItem *pReturnValue = (CacheItem*) pLink->_pData;
// This relies on the fact that there are no duplicates
Remove(*(pLink->_pData));
return pReturnValue;
}
#endif // __LINKLIST_HXX_