/*++ Microsoft Windows NT RPC Name Service Copyright (c) 1995 Microsoft Corporation Module Name: linklist.cxx Abstract: This module contains definitions of non inline member functions for the basic implementation class CLinkList. Author: Satish Thatte (SatishT) 08/16/95 Created all the code below except where otherwise indicated. --*/ #include extern char * LLname = "CLinkList"; extern char * Lname = "Link"; CLinkList::Link::Link(IDataItem* a, Link* n) { data = a; next = n; fDeleteData = FALSE; } CLinkList::Link::~Link() { // called when ref count goes to zero if (next) next->release(); if (fDeleteData) delete data; // should this be release? } void CLinkList::enque(IDataItem* x) { // no hold() calls needed because the constructor does an implicit hold if (pLnkLast) pLnkLast = pLnkLast->next = new Link(x, NULL); else pLnkFirst = pLnkLast = new Link(x,NULL); ulCount++; } void CLinkList::push(IDataItem* x) { // see comment above pLnkFirst = new Link(x, pLnkFirst); if (!pLnkLast) pLnkLast = pLnkFirst; ulCount++; } void CLinkList::releaseAll(Link *pCurr) /* The reason why this method doesn't just release pCurr is because we must avoid stack overflow caused by a runaway recursive release effect. So we do this iteratively. We must, however, make sure to stop if we hit a link that would not be deleted if released because we are releasing the next one only to simulate the release that would happen due to deletion of the current link. */ { Link *pPrev = NULL; /* The invariant for the loop below is that pCurr->ulRefCount is 1 higher than it should be, if pCurr != NULL. */ while (pCurr && pCurr->willBeDeletedIfReleased()) { pPrev = pCurr; pCurr = pCurr->next; if (pCurr) pCurr->hold(); pPrev->release(); } if (pCurr) pCurr->release(); // printStats(); } IDataItem* CLinkList::pop() /*++ Routine Description: Delete first item in the CLinkList and return it --*/ { if (!pLnkFirst) return NULL; IDataItem* result = pLnkFirst->data; Link* oldFirst = pLnkFirst; pLnkFirst = pLnkFirst->next; if (!pLnkFirst) pLnkLast = NULL; // nothing left else pLnkFirst->hold(); oldFirst->release(); ulCount--; return result; } int CLinkList::remove(IDataItem* pDI) /*++ Routine Description: Remove the specified item and return it -- using pointer equality. The return value primarily acts as a flag notifying success/failure --*/ { if (!pLnkFirst) return FALSE; // empty list if (pLnkFirst->data == pDI) { // remove first item pop(); return TRUE; } Link * pLnkPrev = pLnkFirst, * pLnkCurr = pLnkFirst->next; while (pLnkCurr && (pLnkCurr->data != pDI)) { pLnkPrev = pLnkCurr; pLnkCurr = pLnkCurr->next; } if (!pLnkCurr) return FALSE; // not found /* pLnkCurr contains the item to be removed and it is not the only item in the list since it is not the first item */ pLnkPrev->next = pLnkCurr->next; if (pLnkPrev->next) pLnkPrev->next->hold(); pLnkCurr->release(); ulCount--; return TRUE; } IDataItem* CLinkList::nth(long lOrdinal) /*++ Routine Description: Simply return the Nth data item -- starting the count at 0. --*/ { if (!pLnkFirst) return NULL; // empty list Link * pLnkCurr = pLnkFirst; long lCount = 0; while (pLnkCurr && (lCount++ < lOrdinal)) pLnkCurr = pLnkCurr->next; if (!pLnkCurr) return NULL; // not found else return pLnkCurr->data; } void CLinkList::rotate(long lDegree) /*++ Routine Description: This routine imagines that the list is in fact circular and rotates it by lDegree -- using pop and enque for modularity. We could actually move links around, but this operation is not frequent enough (once for every NS lookup). Here we pay the price of not having a true circular list (we moved away from the true circular list for ref counting). --*/ { if (!pLnkFirst) return; // nothing to rotate; IDataItem *pCurr; for (long i = 0; i < (lDegree % ulCount); i++) { pCurr = pop(); enque(pCurr); } } IDataItem* CLinkList::find(IDataItem* pDI, // item to find TcompFun comp // (pointer to) comparison function ) /*++ Routine Description: Unlike remove, this method is designed to use a client-supplied comparison function instead of pointer equality. The comparison function is expected to behave like strcmp (returning <0 is less, 0 if equal and >0 if greater). --*/ { if (!pLnkFirst) return NULL; // empty list Link * pLnkCurr = pLnkFirst; while (pLnkCurr && comp(pLnkCurr->data,pDI)) pLnkCurr = pLnkCurr->next; if (!pLnkCurr) return NULL; // not found else return pLnkCurr->data; } void CLinkList::catenate(CLinkList& ll) /*++ Routine Description: append the argument list to the current one -- as is, not a copy --*/ { if (!ll.pLnkFirst) return; // nothing to catenate if (!pLnkFirst) pLnkFirst = ll.pLnkFirst; else pLnkLast->next = ll.pLnkFirst; pLnkLast = ll.pLnkLast; ulCount += ll.ulCount; /* we only need to hold the first link, because *it* holds the others */ ll.pLnkFirst->hold(); } void CLinkList::wipeOut() /*++ Routine Description: release all links and all data and reinitialize to empty list --*/ { if (pLnkFirst) { Link *pCurr = pLnkFirst; do { pCurr->fDeleteData = TRUE; // delete data when Link dies pCurr = pCurr->next; } while (pCurr); releaseAll(pLnkFirst); // release as many links as you can, and do it // iteratively for robustness pLnkFirst = pLnkLast = NULL; ulCount = 0; } } CLinkListIterator::CLinkListIterator(CLinkList& source) { ptr = first = source.pLnkFirst; if (ptr) ptr->hold(); } IDataItem* CLinkListIterator::next() { // advance the iterator and return next IDataItem if (!ptr) return NULL; CLinkList::Link* tl = ptr; IDataItem* result = ptr->data; ptr = ptr->next; if (ptr) ptr->hold(); tl->release(); return result; }