Leaked source code of windows server 2003
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.
 
 
 
 
 
 

425 lines
13 KiB

//-----------------------------------------------------------------------------
//
//
// File: qwiklist.cpp
//
// Description: Implementation of CQuickList
//
// Author: Mike Swafford (MikeSwa)
//
// History:
// 6/15/98 - MikeSwa Created
//
// Copyright (C) 1998 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include "aqprecmp.h"
#include "qwiklist.h"
CPool CQuickList::s_QuickListPool;
//---[ CQuickList::CQuickList ]------------------------------------------------
//
//
// Description:
// Default contructor for CQuikList... initializes as head of list
// Parameters:
// -
// Returns:
// -
// History:
// 6/15/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CQuickList::CQuickList()
{
m_dwSignature = QUICK_LIST_SIG;
//ASSERT constants
_ASSERT(!(~QUICK_LIST_INDEX_MASK & QUICK_LIST_PAGE_SIZE));
_ASSERT((~QUICK_LIST_INDEX_MASK + 1)== QUICK_LIST_PAGE_SIZE);
m_dwCurrentIndexStart = 0;
InitializeListHead(&m_liListPages);
m_cItems = 0;
ZeroMemory(m_rgpvData, QUICK_LIST_PAGE_SIZE*sizeof(PVOID));
}
//---[ CQuickList::CQuickList ]------------------------------------------------
//
//
// Description:
// Constructor for QQuickList, inserts it into the tail of current list
// Parameters:
//
// Returns:
//
// History:
// 6/15/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CQuickList::CQuickList(CQuickList *pqlstHead)
{
_ASSERT(pqlstHead);
_ASSERT(pqlstHead->m_liListPages.Blink);
CQuickList *pqlstTail = CONTAINING_RECORD(pqlstHead->m_liListPages.Blink, CQuickList, m_liListPages);
_ASSERT(QUICK_LIST_SIG == pqlstTail->m_dwSignature);
m_dwSignature = QUICK_LIST_SIG;
m_dwCurrentIndexStart = pqlstTail->m_dwCurrentIndexStart + QUICK_LIST_PAGE_SIZE;
m_cItems = QUICK_LIST_LEAF_PAGE;
ZeroMemory(m_rgpvData, QUICK_LIST_PAGE_SIZE*sizeof(PVOID));
InsertTailList(&(pqlstHead->m_liListPages), &m_liListPages);
}
//---[ CQuickList::~CQuickList ]-----------------------------------------------
//
//
// Description:
// CQuickList destructor
// Parameters:
// -
// Returns:
// -
// History:
// 6/15/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CQuickList::~CQuickList()
{
m_dwSignature = QUICK_LIST_SIG_DELETE;
CQuickList *pqlstCurrent = NULL;
CQuickList *pqlstNext = NULL;
if (QUICK_LIST_LEAF_PAGE != m_cItems)
{
//head node... loop through every thing and delete leaf pages
pqlstCurrent = CONTAINING_RECORD(m_liListPages.Flink,
CQuickList, m_liListPages);
while (this != pqlstCurrent)
{
pqlstNext = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Flink,
CQuickList, m_liListPages);
delete pqlstCurrent;
pqlstCurrent = pqlstNext;
}
}
}
//---[ CQuickList::pvGetItem ]-------------------------------------------------
//
//
// Description:
// Looks up item at given index
// Parameters:
// IN dwIndex Index of item to lookup
// IN OUT ppvContext Context for speeding up lookup
// Returns:
// Value of item at index
// NULL if index is out of ranges
// History:
// 6/15/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
PVOID CQuickList::pvGetItem(IN DWORD dwIndex, IN OUT PVOID *ppvContext)
{
_ASSERT(ppvContext);
PVOID pvReturn = NULL;
BOOL fSearchForwards = TRUE;
DWORD dwForwardDist = 0;
DWORD dwBackwardDist = 0;
DWORD dwMaxStartingIndex = m_cItems & QUICK_LIST_INDEX_MASK;
CQuickList *pqlstDirection = NULL;
CQuickList *pqlstCurrent = (CQuickList *) *ppvContext;
CQuickList *pqlstSentinal = NULL;
DWORD cDbgItems = m_cItems;
if (dwIndex >= m_cItems)
return NULL;
if (!pqlstCurrent)
pqlstCurrent = this;
pqlstSentinal = pqlstCurrent;
//short circuit direction logic
if (pqlstCurrent->fIsIndexOnThisPage(dwIndex))
{
pvReturn = pqlstCurrent->m_rgpvData[dwIndex & ~QUICK_LIST_INDEX_MASK];
*ppvContext = pqlstCurrent;
_ASSERT((dwIndex < m_cItems) || (NULL == pvReturn));
goto Exit;
}
//determine which direction to go in (we want to traverse the smallest # of pages
//possible
pqlstDirection = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Flink, CQuickList, m_liListPages);
if (dwIndex > pqlstDirection->m_dwCurrentIndexStart)
dwForwardDist = dwIndex - pqlstDirection->m_dwCurrentIndexStart;
else
dwForwardDist = pqlstDirection->m_dwCurrentIndexStart - dwIndex;
pqlstDirection = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Blink, CQuickList, m_liListPages);
if (dwIndex > pqlstDirection->m_dwCurrentIndexStart)
dwBackwardDist = dwIndex - pqlstDirection->m_dwCurrentIndexStart;
else
dwBackwardDist = pqlstDirection->m_dwCurrentIndexStart - dwIndex;
//fix up distances to account for going through the 0th page
//max distance is dwMaxStartingIndex/2
if (dwBackwardDist > dwMaxStartingIndex/2)
dwBackwardDist -= dwMaxStartingIndex;
if (dwForwardDist > dwMaxStartingIndex/2)
dwForwardDist -= dwMaxStartingIndex;
if (dwForwardDist > dwBackwardDist)
fSearchForwards = FALSE;
//$$NOTE: current lookup time is O(lg base{QUICK_LIST_PAGE_BASE} (n))/2.
//Consecutive lookups will be O(1) (because of the hints)
do
{
if (fSearchForwards)
{
//going forward is quicker
pqlstCurrent = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Flink, CQuickList, m_liListPages);
}
else
{
//going backwards is quicker
pqlstCurrent = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Blink, CQuickList, m_liListPages);
}
_ASSERT(QUICK_LIST_SIG == pqlstCurrent->m_dwSignature);
if (pqlstCurrent->fIsIndexOnThisPage(dwIndex))
{
pvReturn = pqlstCurrent->m_rgpvData[dwIndex & ~QUICK_LIST_INDEX_MASK];
_ASSERT((dwIndex < m_cItems) || (NULL == pvReturn));
break;
}
} while (pqlstSentinal != pqlstCurrent); //stop when we return to list head
*ppvContext = pqlstCurrent;
_ASSERT((cDbgItems == m_cItems) && "Non-threadsafe access to CQuickList");
Exit:
return pvReturn;
}
//---[ CQuickList::pvDeleteItem ]----------------------------------------------
//
//
// Description:
// Will remove item at a given index from the quick list.
//
// If you need to determine the data at that has been moved into this
// index, call pvGetItem with the returned context, and it will return
// without needing to search.
// Parameters:
// IN dwIndex Index of item to lookup
// IN OUT ppvContext Context for speeding up lookup
// Returns:
// Value of item that was removed.
// NULL if index is out of ranges
// History:
// 9/10/98 - MikeSwa Created
// 11/5/98 - MikeSwa Fixed problem about not always marking deleted
// entry NULL.
//
//-----------------------------------------------------------------------------
PVOID CQuickList::pvDeleteItem(IN DWORD dwIndex, IN OUT PVOID *ppvContext)
{
PVOID pvItem = NULL;
PVOID pvLast = NULL; //last item in list
CQuickList *pqlstCurrent = NULL; //page that index is on
CQuickList *pqlstLast = NULL; //last page
DWORD dwLastIndex = m_cItems - 1;
//Check to make sure that the index is valid
if (dwIndex >= m_cItems)
goto Exit;
//Use single function to look up index and find quick list page
pvItem = pvGetItem(dwIndex, (PVOID *) &pqlstCurrent);
_ASSERT(pvItem); //we checked the index... and we cannot insert NULL ptrs
_ASSERT(QUICK_LIST_SIG == pqlstCurrent->m_dwSignature);
_ASSERT(pqlstCurrent->fIsIndexOnThisPage(dwIndex));
//Now that we know the index and the page... we can move that last
//entry in the list to this index.
pqlstLast = CONTAINING_RECORD(m_liListPages.Blink, CQuickList, m_liListPages);
_ASSERT(QUICK_LIST_SIG == pqlstLast->m_dwSignature);
_ASSERT(pqlstLast->fIsIndexOnThisPage(dwLastIndex));
if (dwLastIndex != dwIndex)
{
//In general if we are not deleting the last entry... we need to move
//the last entry to the current index
pvLast = pqlstLast->m_rgpvData[dwLastIndex & ~QUICK_LIST_INDEX_MASK];
_ASSERT(pvLast); //shouldn't be NULL!
//Now that we have last item... write it!
pqlstCurrent->m_rgpvData[dwIndex & ~QUICK_LIST_INDEX_MASK] = pvLast;
//NULL the old last entry
pqlstLast->m_rgpvData[dwLastIndex & ~QUICK_LIST_INDEX_MASK] = NULL;
}
else
{
//if we deleted that last entry... the current and last pages
//should be the same
_ASSERT(pqlstLast == pqlstCurrent);
//Set emptied data pointer to NULL
pqlstLast->m_rgpvData[dwLastIndex & ~QUICK_LIST_INDEX_MASK] = NULL;
}
//Decrement total count
m_cItems--;
_ASSERT(QUICK_LIST_LEAF_PAGE != m_cItems);
//Determine if it is neccessary to delete the last page
if (!(dwLastIndex & ~QUICK_LIST_INDEX_MASK) && m_cItems)
{
//dwLastIndex was the only entry on the last page & it wasn't the head page
//Unless the above test is wrong... the last page is *not* the head page
_ASSERT(QUICK_LIST_LEAF_PAGE == pqlstLast->m_cItems);
_ASSERT(this != pqlstLast);
//Remove from list
RemoveEntryList(&(pqlstLast->m_liListPages));
if (pqlstCurrent == pqlstLast) //we cannot return a deleted context
pqlstCurrent = this;
delete pqlstLast;
}
//Safety check to make sure another thread hasn't come along
_ASSERT(m_cItems == dwLastIndex);
Exit:
if (ppvContext)
*ppvContext = pqlstCurrent;
return pvItem;
}
//---[ CQuickList::HrAppendItem ]-----------------------------------------------
//
//
// Description:
// Appends new data item to end of array
// Parameters:
// IN pvData - Data to insert
// OUT pdwIndex - Index data was inserted at
// Returns:
// E_OUTOFMEMORY if unable to allocate another page
// E_INVALIDARG if pvData is NULL
// History:
// 6/15/98 - MikeSwa Created
// 9/9/98 - MikeSwa - Added pdwIndex OUT param
//
//-----------------------------------------------------------------------------
HRESULT CQuickList::HrAppendItem(IN PVOID pvData, OUT DWORD *pdwIndex)
{
HRESULT hr = S_OK;
CQuickList *pqlstCurrent = NULL;
_ASSERT(pvData && "Cannot insert NULL pointers");
if (!pvData)
{
hr = E_INVALIDARG;
goto Exit;
}
if (m_cItems && !(m_cItems & ~QUICK_LIST_INDEX_MASK)) //on page boundary
{
//there is not room on the last page
pqlstCurrent = new CQuickList(this);
if (!pqlstCurrent)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
}
else if (!(m_cItems & QUICK_LIST_INDEX_MASK))
{
pqlstCurrent = this;
}
else
{
pqlstCurrent = CONTAINING_RECORD(m_liListPages.Blink, CQuickList, m_liListPages);
}
_ASSERT(pqlstCurrent->fIsIndexOnThisPage(m_cItems));
pqlstCurrent->m_rgpvData[m_cItems & ~QUICK_LIST_INDEX_MASK] = pvData;
//Set OUT param to index (before we increment the count)
if (pdwIndex)
*pdwIndex = m_cItems;
m_cItems++;
_ASSERT(QUICK_LIST_LEAF_PAGE != m_cItems);
Exit:
return hr;
}
//---[ CQuickList::Clone ]-----------------------------------------------------
//
//
// Description:
// Copies the contents of this CQuickList into a new CQuickList - caller
// is responsible for deleting the CQuickList we create here
// Parameters:
// IN / OUT pqlClone - pointer to destination CQuickList pointer
// Returns:
// S_OK - cloned successfully
// E_OUTOFMEMORY - failed to allocate CQuickList
// History:
// 11/9/2000 - dbraun - created
//
//-----------------------------------------------------------------------------
HRESULT CQuickList::Clone (CQuickList **ppqlClone)
{
HRESULT hr = S_OK;
DWORD dwIndex = 0;
PVOID pvItem = NULL;
PVOID pvContext = NULL;
DWORD dwNewIndex = 0;
CQuickList *pql = NULL;
_ASSERT(ppqlClone);
pql = new CQuickList();
if (!pql)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Now copy the contents of this CQuickList
for (dwIndex = 0; dwIndex < m_cItems; dwIndex++)
{
pvItem = pvGetItem(dwIndex, &pvContext);
_ASSERT(pvItem);
hr = pql->HrAppendItem(pvItem, &dwNewIndex);
if (FAILED(hr))
goto Exit; // Report the failure to our caller
}
*ppqlClone = pql;
pql = NULL;
Exit:
if (pql)
delete pql;
return hr;
}