//----------------------------------------------------------------------------- // // // 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; }