/** * @file doublelinkedlist.h * @brief Provides a standard doubly linked list for fun and profit. * * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. * $License$ */ #ifndef LL_DOUBLELINKEDLIST_H #define LL_DOUBLELINKEDLIST_H #include "llerror.h" #include "llrand.h" // node that actually contains the data template class LLDoubleLinkedNode { public: DATA_TYPE *mDatap; LLDoubleLinkedNode *mNextp; LLDoubleLinkedNode *mPrevp; public: // assign the mDatap pointer LLDoubleLinkedNode(DATA_TYPE *data); // destructor does not, by default, destroy associated data // however, the mDatap must be NULL to ensure that we aren't causing memory leaks ~LLDoubleLinkedNode(); // delete associated data and NULL out pointer void deleteData(); // remove associated data and NULL out pointer void removeData(); }; const U32 LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH = 4; template class LLDoubleLinkedList { private: LLDoubleLinkedNode mHead; // head node LLDoubleLinkedNode mTail; // tail node LLDoubleLinkedNode *mQueuep; // The node in the batter's box LLDoubleLinkedNode *mCurrentp; // The node we're talking about // The state stack allows nested exploration of the LLDoubleLinkedList // but should be used with great care LLDoubleLinkedNode *mQueuepStack[LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH]; LLDoubleLinkedNode *mCurrentpStack[LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH]; U32 mStateStackDepth; U32 mCount; // mInsertBefore is a pointer to a user-set function that returns // TRUE if "first" should be located before "second" // NOTE: mInsertBefore() should never return TRUE when ("first" == "second") // or never-ending loops can occur BOOL (*mInsertBefore)(DATA_TYPE *first, DATA_TYPE *second); public: LLDoubleLinkedList(); // destructor destroys list and nodes, but not data in nodes ~LLDoubleLinkedList(); // put data into a node and stick it at the front of the list // set mCurrentp to mQueuep void addData(DATA_TYPE *data); // put data into a node and stick it at the end of the list // set mCurrentp to mQueuep void addDataAtEnd(DATA_TYPE *data); S32 getLength() const; // search the list starting at mHead.mNextp and remove the link with mDatap == data // set mCurrentp to mQueuep // return TRUE if found, FALSE if not found BOOL removeData(const DATA_TYPE *data); // search the list starting at mHead.mNextp and delete the link with mDatap == data // set mCurrentp to mQueuep // return TRUE if found, FALSE if not found BOOL deleteData(DATA_TYPE *data); // remove all nodes from the list and delete the associated data void deleteAllData(); // remove all nodes from the list but do not delete data void removeAllNodes(); BOOL isEmpty(); // check to see if data is in list // set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep // return TRUE if found, FALSE if not found BOOL checkData(const DATA_TYPE *data); // NOTE: This next two funtions are only included here // for those too familiar with the LLLinkedList template class. // They are depreciated. resetList() is unecessary while // getCurrentData() is identical to getNextData() and has // a misleading name. // // The recommended way to loop through a list is as follows: // // datap = list.getFirstData(); // while (datap) // { // /* do stuff */ // datap = list.getNextData(); // } // place mQueuep on mHead node void resetList(); // return the data currently pointed to, // set mCurrentp to that node and bump mQueuep down the list // NOTE: this function is identical to getNextData() DATA_TYPE *getCurrentData(); // reset the list and return the data currently pointed to, // set mCurrentp to that node and bump mQueuep down the list DATA_TYPE *getFirstData(); // reset the list and return the data at position n, set mCurentp // to that node and bump mQueuep down the list // Note: n=0 will behave like getFirstData() DATA_TYPE *getNthData(U32 n); // reset the list and return the last data in it, // set mCurrentp to that node and bump mQueuep up the list DATA_TYPE *getLastData(); // return data in mQueuep, // set mCurrentp mQueuep and bump mQueuep down the list DATA_TYPE *getNextData(); // return the data in mQueuep, // set mCurrentp to mQueuep and bump mQueuep up the list DATA_TYPE *getPreviousData(); // remove the Node at mCurrentp // set mCurrentp to mQueuep void removeCurrentData(); // delete the Node at mCurrentp // set mCurrentp to mQueuep void deleteCurrentData(); // remove the Node at mCurrentp and insert it into newlist // set mCurrentp to mQueuep void moveCurrentData(LLDoubleLinkedList *newlist); // insert the node in front of mCurrentp // set mCurrentp to mQueuep void insertNode(LLDoubleLinkedNode *node); // insert the data in front of mCurrentp // set mCurrentp to mQueuep void insertData(DATA_TYPE *data); // if mCurrentp has a previous node then : // * swaps mCurrentp with its previous // * set mCurrentp to mQueuep // (convenient for forward bubble-sort) // otherwise does nothing void swapCurrentWithPrevious(); // if mCurrentp has a next node then : // * swaps mCurrentp with its next // * set mCurrentp to mQueuep // (convenient for backwards bubble-sort) // otherwise does nothing void swapCurrentWithNext(); // move mCurrentp to the front of the list // set mCurrentp to mQueuep void moveCurrentToFront(); // move mCurrentp to the end of the list // set mCurrentp to mQueuep void moveCurrentToEnd(); // set mInsertBefore void setInsertBefore(BOOL (*insert_before)(DATA_TYPE *first, DATA_TYPE *second)); // add data in front of first node for which mInsertBefore(datap, node->mDatap) returns TRUE // set mCurrentp to mQueuep BOOL addDataSorted(DATA_TYPE *datap); // sort the list using bubble-sort // Yes, this is a different name than the same function in LLLinkedList. // When it comes time for a name consolidation hopefully this one will win. BOOL bubbleSort(); // does a single bubble sort pass on the list BOOL lazyBubbleSort(); // returns TRUE if state successfully pushed (state stack not full) BOOL pushState(); // returns TRUE if state successfully popped (state stack not empty) BOOL popState(); // empties the state stack void clearStateStack(); // randomly move the the links in the list for debug or (Discordian) purposes // sets mCurrentp and mQueuep to top of list void scramble(); private: // add node to beginning of list // set mCurrentp to mQueuep void addNode(LLDoubleLinkedNode *node); // add node to end of list // set mCurrentp to mQueuep void addNodeAtEnd(LLDoubleLinkedNode *node); }; //#endif //////////////////////////////////////////////////////////////////////////////////////////// // doublelinkedlist.cpp // LLDoubleLinkedList template class implementation file. // Provides a standard doubly linked list for fun and profit. // // Copyright 2001, Linden Research, Inc. //#include "llerror.h" //#include "doublelinkedlist.h" ////////////////////////////////////////////////////////////////////////////////////////// // LLDoubleLinkedNode ////////////////////////////////////////////////////////////////////////////////////////// // assign the mDatap pointer template LLDoubleLinkedNode::LLDoubleLinkedNode(DATA_TYPE *data) : mDatap(data), mNextp(NULL), mPrevp(NULL) { } // destructor does not, by default, destroy associated data // however, the mDatap must be NULL to ensure that we aren't causing memory leaks template LLDoubleLinkedNode::~LLDoubleLinkedNode() { if (mDatap) { llerror("Attempting to call LLDoubleLinkedNode destructor with a non-null mDatap!", 1); } } // delete associated data and NULL out pointer template void LLDoubleLinkedNode::deleteData() { delete mDatap; mDatap = NULL; } template void LLDoubleLinkedNode::removeData() { mDatap = NULL; } ////////////////////////////////////////////////////////////////////////////////////// // LLDoubleLinkedList ////////////////////////////////////////////////////////////////////////////////////// // <------- up ------- // // mCurrentp // mQueuep | // | | // | | // .------. .------. .------. .------. // | |---->| |---->| |----->| |-----> NULL // NULL <-----| |<----| |<----| |<-----| | // _'------' '------' '------' '------:_ // .------. /| | | |\ .------. // NULL <-----|mHead |/ | mQueuep \|mTail |-----> NULL // | | mCurrentp | | // '------' '------' // -------- down ---------> template LLDoubleLinkedList::LLDoubleLinkedList() : mHead(NULL), mTail(NULL), mQueuep(NULL) { mCurrentp = mHead.mNextp; mQueuep = mHead.mNextp; mStateStackDepth = 0; mCount = 0; mInsertBefore = NULL; } // destructor destroys list and nodes, but not data in nodes template LLDoubleLinkedList::~LLDoubleLinkedList() { removeAllNodes(); } // put data into a node and stick it at the front of the list // doesn't change mCurrentp nor mQueuep template void LLDoubleLinkedList::addData(DATA_TYPE *data) { // don't allow NULL to be passed to addData if (!data) { llerror("NULL pointer passed to LLDoubleLinkedList::addData()", 0); } // make the new node LLDoubleLinkedNode *temp = new LLDoubleLinkedNode (data); // add the node to the front of the list temp->mPrevp = NULL; temp->mNextp = mHead.mNextp; mHead.mNextp = temp; // if there's something in the list, fix its back pointer if (temp->mNextp) { temp->mNextp->mPrevp = temp; } // otherwise, fix the tail of the list else { mTail.mPrevp = temp; } mCount++; } // put data into a node and stick it at the end of the list template void LLDoubleLinkedList::addDataAtEnd(DATA_TYPE *data) { // don't allow NULL to be passed to addData if (!data) { llerror("NULL pointer passed to LLDoubleLinkedList::addData()", 0); } // make the new node LLDoubleLinkedNode *nodep = new LLDoubleLinkedNode(data); addNodeAtEnd(nodep); mCount++; } // search the list starting at mHead.mNextp and remove the link with mDatap == data // set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data // return TRUE if found, FALSE if not found template BOOL LLDoubleLinkedList::removeData(const DATA_TYPE *data) { BOOL b_found = FALSE; // don't allow NULL to be passed to addData if (!data) { llerror("NULL pointer passed to LLDoubleLinkedList::removeData()", 0); } mCurrentp = mHead.mNextp; while (mCurrentp) { if (mCurrentp->mDatap == data) { b_found = TRUE; // if there is a next one, fix it if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // we are at end of list { mTail.mPrevp = mCurrentp->mPrevp; } // if there is a previous one, fix it if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // we are at beginning of list { mHead.mNextp = mCurrentp->mNextp; } // remove the node mCurrentp->removeData(); delete mCurrentp; mCount--; break; } mCurrentp = mCurrentp->mNextp; } // reset the list back to where it was if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } return b_found; } // search the list starting at mHead.mNextp and delete the link with mDatap == data // set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data // return TRUE if found, FALSE if not found template BOOL LLDoubleLinkedList::deleteData(DATA_TYPE *data) { BOOL b_found = FALSE; // don't allow NULL to be passed to addData if (!data) { llerror("NULL pointer passed to LLDoubleLinkedList::deleteData()", 0); } mCurrentp = mHead.mNextp; while (mCurrentp) { if (mCurrentp->mDatap == data) { b_found = TRUE; // if there is a next one, fix it if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // we are at end of list { mTail.mPrevp = mCurrentp->mPrevp; } // if there is a previous one, fix it if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // we are at beginning of list { mHead.mNextp = mCurrentp->mNextp; } // remove the node mCurrentp->deleteData(); delete mCurrentp; mCount--; break; } mCurrentp = mCurrentp->mNextp; } // reset the list back to where it was if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } return b_found; } // remove all nodes from the list and delete the associated data template void LLDoubleLinkedList::deleteAllData() { mCurrentp = mHead.mNextp; while (mCurrentp) { mQueuep = mCurrentp->mNextp; mCurrentp->deleteData(); delete mCurrentp; mCurrentp = mQueuep; } // reset mHead and mQueuep mHead.mNextp = NULL; mTail.mPrevp = NULL; mCurrentp = mHead.mNextp; mQueuep = mHead.mNextp; mStateStackDepth = 0; mCount = 0; } // remove all nodes from the list but do not delete associated data template void LLDoubleLinkedList::removeAllNodes() { mCurrentp = mHead.mNextp; while (mCurrentp) { mQueuep = mCurrentp->mNextp; mCurrentp->removeData(); delete mCurrentp; mCurrentp = mQueuep; } // reset mHead and mCurrentp mHead.mNextp = NULL; mTail.mPrevp = NULL; mCurrentp = mHead.mNextp; mQueuep = mHead.mNextp; mStateStackDepth = 0; mCount = 0; } template S32 LLDoubleLinkedList::getLength() const { // U32 length = 0; // for (LLDoubleLinkedNode* temp = mHead.mNextp; temp != NULL; temp = temp->mNextp) // { // length++; // } return mCount; } // check to see if data is in list // set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep // return TRUE if found, FALSE if not found template BOOL LLDoubleLinkedList::checkData(const DATA_TYPE *data) { mCurrentp = mHead.mNextp; while (mCurrentp) { if (mCurrentp->mDatap == data) { mQueuep = mCurrentp; return TRUE; } mCurrentp = mCurrentp->mNextp; } mCurrentp = mQueuep; return FALSE; } // NOTE: This next two funtions are only included here // for those too familiar with the LLLinkedList template class. // They are depreciated. resetList() is unecessary while // getCurrentData() is identical to getNextData() and has // a misleading name. // // The recommended way to loop through a list is as follows: // // datap = list.getFirstData(); // while (datap) // { // /* do stuff */ // datap = list.getNextData(); // } // place mCurrentp and mQueuep on first node template void LLDoubleLinkedList::resetList() { mCurrentp = mHead.mNextp; mQueuep = mHead.mNextp; mStateStackDepth = 0; } // return the data currently pointed to, // set mCurrentp to that node and bump mQueuep down the list template DATA_TYPE* LLDoubleLinkedList::getCurrentData() { if (mQueuep) { mCurrentp = mQueuep; mQueuep = mQueuep->mNextp; return mCurrentp->mDatap; } else { return NULL; } } // reset the list and return the data currently pointed to, // set mCurrentp to that node and bump mQueuep down the list template DATA_TYPE* LLDoubleLinkedList::getFirstData() { mQueuep = mHead.mNextp; mCurrentp = mQueuep; if (mQueuep) { mQueuep = mQueuep->mNextp; return mCurrentp->mDatap; } else { return NULL; } } // reset the list and return the data at position n, set mCurentp // to that node and bump mQueuep down the list // Note: n=0 will behave like getFirstData() template DATA_TYPE* LLDoubleLinkedList::getNthData(U32 n) { mCurrentp = mHead.mNextp; if (mCurrentp) { for (U32 i=0; imNextp; if (!mCurrentp) { break; } } } if (mCurrentp) { // bump mQueuep down the list mQueuep = mCurrentp->mNextp; return mCurrentp->mDatap; } else { mQueuep = NULL; return NULL; } } // reset the list and return the last data in it, // set mCurrentp to that node and bump mQueuep up the list template DATA_TYPE* LLDoubleLinkedList::getLastData() { mQueuep = mTail.mPrevp; mCurrentp = mQueuep; if (mQueuep) { mQueuep = mQueuep->mPrevp; return mCurrentp->mDatap; } else { return NULL; } } // return the data in mQueuep, // set mCurrentp to mQueuep and bump mQueuep down the list template DATA_TYPE* LLDoubleLinkedList::getNextData() { if (mQueuep) { mCurrentp = mQueuep; mQueuep = mQueuep->mNextp; return mCurrentp->mDatap; } else { return NULL; } } // return the data in mQueuep, // set mCurrentp to mQueuep and bump mQueuep up the list template DATA_TYPE* LLDoubleLinkedList::getPreviousData() { if (mQueuep) { mCurrentp = mQueuep; mQueuep = mQueuep->mPrevp; return mCurrentp->mDatap; } else { return NULL; } } // remove the Node at mCurrentp // set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep) template void LLDoubleLinkedList::removeCurrentData() { if (mCurrentp) { // if there is a next one, fix it if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // otherwise we are at end of list { mTail.mPrevp = mCurrentp->mPrevp; } // if there is a previous one, fix it if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // otherwise we are at beginning of list { mHead.mNextp = mCurrentp->mNextp; } // remove the node mCurrentp->removeData(); delete mCurrentp; mCount--; // check for redundant pointing if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } } } // delete the Node at mCurrentp // set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep) template void LLDoubleLinkedList::deleteCurrentData() { if (mCurrentp) { // remove the node // if there is a next one, fix it if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // otherwise we are at end of list { mTail.mPrevp = mCurrentp->mPrevp; } // if there is a previous one, fix it if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // otherwise we are at beginning of list { mHead.mNextp = mCurrentp->mNextp; } // remove the LLDoubleLinkedNode mCurrentp->deleteData(); delete mCurrentp; mCount--; // check for redundant pointing if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } } } // remove the Node at mCurrentp and insert it into newlist // set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep) template void LLDoubleLinkedList::moveCurrentData(LLDoubleLinkedList *newlist) { if (mCurrentp) { // remove the node // if there is a next one, fix it if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // otherwise we are at end of list { mTail.mPrevp = mCurrentp->mPrevp; } // if there is a previous one, fix it if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // otherwise we are at beginning of list { mHead.mNextp = mCurrentp->mNextp; } // move the node to the new list newlist->addNode(mCurrentp); // check for redundant pointing if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } } } // Inserts the node previous to mCurrentp // set mCurrentp to mQueuep template void LLDoubleLinkedList::insertNode(LLDoubleLinkedNode *nodep) { // don't allow pointer to NULL to be passed if (!nodep) { llerror("NULL pointer passed to LLDoubleLinkedList::insertNode()", 0); } if (!nodep->mDatap) { llerror("NULL data pointer passed to LLDoubleLinkedList::insertNode()", 0); } if (mCurrentp) { if (mCurrentp->mPrevp) { nodep->mPrevp = mCurrentp->mPrevp; nodep->mNextp = mCurrentp; mCurrentp->mPrevp->mNextp = nodep; mCurrentp->mPrevp = nodep; } else // at beginning of list { nodep->mPrevp = NULL; nodep->mNextp = mCurrentp; mHead.mNextp = nodep; mCurrentp->mPrevp = nodep; } mCurrentp = mQueuep; } else // add to front of list { addNode(nodep); } } // insert the data in front of mCurrentp // set mCurrentp to mQueuep template void LLDoubleLinkedList::insertData(DATA_TYPE *data) { if (!data) { llerror("NULL data pointer passed to LLDoubleLinkedList::insertNode()", 0); } LLDoubleLinkedNode *node = new LLDoubleLinkedNode(data); insertNode(node); mCount++; } // if mCurrentp has a previous node then : // * swaps mCurrentp with its previous // * set mCurrentp to mQueuep // otherwise does nothing template void LLDoubleLinkedList::swapCurrentWithPrevious() { if (mCurrentp) { if (mCurrentp->mPrevp) { // Pull mCurrentp out of list mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // mCurrentp was at end of list { mTail.mPrevp = mCurrentp->mPrevp; } // Fix mCurrentp's pointers mCurrentp->mNextp = mCurrentp->mPrevp; mCurrentp->mPrevp = mCurrentp->mNextp->mPrevp; mCurrentp->mNextp->mPrevp = mCurrentp; if (mCurrentp->mPrevp) { // Fix the backward pointer of mCurrentp's new previous mCurrentp->mPrevp->mNextp = mCurrentp; } else // mCurrentp is now at beginning of list { mHead.mNextp = mCurrentp; } // Set the list back to the way it was mCurrentp = mQueuep; } } } // if mCurrentp has a next node then : // * swaps mCurrentp with its next // * set mCurrentp to mQueuep // otherwise does nothing template void LLDoubleLinkedList::swapCurrentWithNext() { if (mCurrentp) { if (mCurrentp->mNextp) { // Pull mCurrentp out of list mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // mCurrentp was at beginning of list { mHead.mNextp = mCurrentp->mNextp; } // Fix mCurrentp's pointers mCurrentp->mPrevp = mCurrentp->mNextp; mCurrentp->mNextp = mCurrentp->mPrevp->mNextp; mCurrentp->mPrevp->mNextp = mCurrentp; if (mCurrentp->mNextp) { // Fix the back pointer of mCurrentp's new next mCurrentp->mNextp->mPrevp = mCurrentp; } else // mCurrentp is now at end of list { mTail.mPrevp = mCurrentp; } // Set the list back to the way it was mCurrentp = mQueuep; } } } // move mCurrentp to the front of the list // set mCurrentp to mQueuep template void LLDoubleLinkedList::moveCurrentToFront() { if (mCurrentp) { // if there is a previous one, fix it if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // otherwise we are at beginning of list { // check for redundant pointing if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } return; } // if there is a next one, fix it if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // otherwise we are at end of list { mTail.mPrevp = mCurrentp->mPrevp; } // add mCurrentp to beginning of list mCurrentp->mNextp = mHead.mNextp; mHead.mNextp->mPrevp = mCurrentp; // mHead.mNextp MUST be valid, // or the list had only one node // and we would have returned already mCurrentp->mPrevp = NULL; mHead.mNextp = mCurrentp; // check for redundant pointing if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } } } // move mCurrentp to the end of the list // set mCurrentp to mQueuep template void LLDoubleLinkedList::moveCurrentToEnd() { if (mCurrentp) { // if there is a next one, fix it if (mCurrentp->mNextp) { mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp; } else // otherwise we are at end of list and we're done { // check for redundant pointing if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } return; } // if there is a previous one, fix it if (mCurrentp->mPrevp) { mCurrentp->mPrevp->mNextp = mCurrentp->mNextp; } else // otherwise we are at beginning of list { mHead.mNextp = mCurrentp->mNextp; } // add mCurrentp to end of list mCurrentp->mPrevp = mTail.mPrevp; mTail.mPrevp->mNextp = mCurrentp; // mTail.mPrevp MUST be valid, // or the list had only one node // and we would have returned already mCurrentp->mNextp = NULL; mTail.mPrevp = mCurrentp; // check for redundant pointing if (mCurrentp == mQueuep) { mCurrentp = mQueuep = NULL; } else { mCurrentp = mQueuep; } } } template void LLDoubleLinkedList::setInsertBefore(BOOL (*insert_before)(DATA_TYPE *first, DATA_TYPE *second) ) { mInsertBefore = insert_before; } // add data in front of the first node for which mInsertBefore(datap, node->mDatap) returns TRUE // set mCurrentp to mQueuep template BOOL LLDoubleLinkedList::addDataSorted(DATA_TYPE *datap) { // don't allow NULL to be passed to addData() if (!datap) { llerror("NULL pointer passed to LLDoubleLinkedList::addDataSorted()", 0); } // has mInsertBefore not been set? if (!mInsertBefore) { addData(datap); return FALSE; } // is the list empty? if (!mHead.mNextp) { addData(datap); return TRUE; } // Note: this step has been added so that the behavior of LLDoubleLinkedList // is as rigorous as the LLLinkedList class about adding duplicate nodes. // Duplicate nodes can cause a problem when sorting if mInsertBefore(foo, foo) // returns TRUE. However, if mInsertBefore(foo, foo) returns FALSE, then there // shouldn't be any reason to exclude duplicate nodes (as we do here). if (checkData(datap)) { return FALSE; } mCurrentp = mHead.mNextp; while (mCurrentp) { // check to see if datap is already in the list if (datap == mCurrentp->mDatap) { return FALSE; } else if (mInsertBefore(datap, mCurrentp->mDatap)) { insertData(datap); return TRUE; } mCurrentp = mCurrentp->mNextp; } addDataAtEnd(datap); return TRUE; } // bubble-sort until sorted and return TRUE if anything was sorted // leaves mQueuep pointing at last node that was swapped with its mNextp // // NOTE: if you find this function looping for really long times, then you // probably need to check your implementation of mInsertBefore(a,b) and make // sure it does not return TRUE when (a == b)! template BOOL LLDoubleLinkedList::bubbleSort() { BOOL b_swapped = FALSE; U32 count = 0; while (lazyBubbleSort()) { b_swapped = TRUE; if (count++ > 0x7FFFFFFF) { llwarning("LLDoubleLinkedList::bubbleSort() : too many passes...", 1); llwarning(" make sure the mInsertBefore(a, b) does not return TRUE for a == b", 1); break; } } return b_swapped; } // do a single bubble-sort pass and return TRUE if anything was sorted // leaves mQueuep pointing at last node that was swapped with its mNextp template BOOL LLDoubleLinkedList::lazyBubbleSort() { // has mInsertBefore been set? if (!mInsertBefore) { return FALSE; } // is list empty? mCurrentp = mHead.mNextp; if (!mCurrentp) { return FALSE; } BOOL b_swapped = FALSE; // the sort will exit after 0x7FFFFFFF nodes or the end of the list, whichever is first S32 length = 0x7FFFFFFF; S32 count = 0; while (mCurrentp && mCurrentp->mNextp && countmNextp->mDatap, mCurrentp->mDatap)) { b_swapped = TRUE; mQueuep = mCurrentp; swapCurrentWithNext(); // sets mCurrentp to mQueuep } count++; mCurrentp = mCurrentp->mNextp; } return b_swapped; } template BOOL LLDoubleLinkedList::pushState() { if (mStateStackDepth < LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH) { *(mQueuepStack + mStateStackDepth) = mQueuep; *(mCurrentpStack + mStateStackDepth) = mCurrentp; mStateStackDepth++; return TRUE; } return FALSE; } template BOOL LLDoubleLinkedList::popState() { if (mStateStackDepth > 0) { mStateStackDepth--; mQueuep = *(mQueuepStack + mStateStackDepth); mCurrentp = *(mCurrentpStack + mStateStackDepth); return TRUE; } return FALSE; } template void LLDoubleLinkedList::clearStateStack() { mStateStackDepth = 0; } ////////////////////////////////////////////////////////////////////////////////////////// // private members ////////////////////////////////////////////////////////////////////////////////////////// // add node to beginning of list // set mCurrentp to mQueuep template void LLDoubleLinkedList::addNode(LLDoubleLinkedNode *nodep) { // add the node to the front of the list nodep->mPrevp = NULL; nodep->mNextp = mHead.mNextp; mHead.mNextp = nodep; // if there's something in the list, fix its back pointer if (nodep->mNextp) { nodep->mNextp->mPrevp = nodep; } else // otherwise fix the tail node { mTail.mPrevp = nodep; } mCurrentp = mQueuep; } // add node to end of list // set mCurrentp to mQueuep template void LLDoubleLinkedList::addNodeAtEnd(LLDoubleLinkedNode *node) { // add the node to the end of the list node->mNextp = NULL; node->mPrevp = mTail.mPrevp; mTail.mPrevp = node; // if there's something in the list, fix its back pointer if (node->mPrevp) { node->mPrevp->mNextp = node; } else // otherwise fix the head node { mHead.mNextp = node; } mCurrentp = mQueuep; } // randomly move nodes in the list for DEBUG (or Discordian) purposes // sets mCurrentp and mQueuep to top of list template void LLDoubleLinkedList::scramble() { S32 random_number; DATA_TYPE *datap = getFirstData(); while(datap) { random_number = gLindenLabRandomNumber.llrand() % 5; if (0 == random_number) { removeCurrentData(); addData(datap); } else if (1 == random_number) { removeCurrentData(); addDataAtEnd(datap); } else if (2 == random_number) { swapCurrentWithPrevious(); } else if (3 == random_number) { swapCurrentWithNext(); } datap = getNextData(); } mQueuep = mHead.mNextp; mCurrentp = mQueuep; } template BOOL LLDoubleLinkedList::isEmpty() { return (mCount == 0); } #endif