summaryrefslogtreecommitdiff
path: root/indra/llcommon/doublelinkedlist.h
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcommon/doublelinkedlist.h
Print done when done.
Diffstat (limited to 'indra/llcommon/doublelinkedlist.h')
-rw-r--r--indra/llcommon/doublelinkedlist.h1379
1 files changed, 1379 insertions, 0 deletions
diff --git a/indra/llcommon/doublelinkedlist.h b/indra/llcommon/doublelinkedlist.h
new file mode 100644
index 0000000000..2546d621d0
--- /dev/null
+++ b/indra/llcommon/doublelinkedlist.h
@@ -0,0 +1,1379 @@
+/**
+ * @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 DATA_TYPE> 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 DATA_TYPE> class LLDoubleLinkedList
+{
+private:
+ LLDoubleLinkedNode<DATA_TYPE> mHead; // head node
+ LLDoubleLinkedNode<DATA_TYPE> mTail; // tail node
+ LLDoubleLinkedNode<DATA_TYPE> *mQueuep; // The node in the batter's box
+ LLDoubleLinkedNode<DATA_TYPE> *mCurrentp; // The node we're talking about
+
+ // The state stack allows nested exploration of the LLDoubleLinkedList
+ // but should be used with great care
+ LLDoubleLinkedNode<DATA_TYPE> *mQueuepStack[LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH];
+ LLDoubleLinkedNode<DATA_TYPE> *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<DATA_TYPE> *newlist);
+
+ // insert the node in front of mCurrentp
+ // set mCurrentp to mQueuep
+ void insertNode(LLDoubleLinkedNode<DATA_TYPE> *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<DATA_TYPE> *node);
+
+ // add node to end of list
+ // set mCurrentp to mQueuep
+ void addNodeAtEnd(LLDoubleLinkedNode<DATA_TYPE> *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 <class DATA_TYPE>
+LLDoubleLinkedNode<DATA_TYPE>::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 <class DATA_TYPE>
+LLDoubleLinkedNode<DATA_TYPE>::~LLDoubleLinkedNode()
+{
+ if (mDatap)
+ {
+ llerror("Attempting to call LLDoubleLinkedNode destructor with a non-null mDatap!", 1);
+ }
+}
+
+
+// delete associated data and NULL out pointer
+template <class DATA_TYPE>
+void LLDoubleLinkedNode<DATA_TYPE>::deleteData()
+{
+ delete mDatap;
+ mDatap = NULL;
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedNode<DATA_TYPE>::removeData()
+{
+ mDatap = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+// LLDoubleLinkedList
+//////////////////////////////////////////////////////////////////////////////////////
+
+// <------- up -------
+//
+// mCurrentp
+// mQueuep |
+// | |
+// | |
+// .------. .------. .------. .------.
+// | |---->| |---->| |----->| |-----> NULL
+// NULL <-----| |<----| |<----| |<-----| |
+// _'------' '------' '------' '------:_
+// .------. /| | | |\ .------.
+// NULL <-----|mHead |/ | mQueuep \|mTail |-----> NULL
+// | | mCurrentp | |
+// '------' '------'
+// -------- down --------->
+
+template <class DATA_TYPE>
+LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+LLDoubleLinkedList<DATA_TYPE>::~LLDoubleLinkedList()
+{
+ removeAllNodes();
+}
+
+
+// put data into a node and stick it at the front of the list
+// doesn't change mCurrentp nor mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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<DATA_TYPE> *temp = new LLDoubleLinkedNode<DATA_TYPE> (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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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<DATA_TYPE> *nodep = new LLDoubleLinkedNode<DATA_TYPE>(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 <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+S32 LLDoubleLinkedList<DATA_TYPE>::getLength() const
+{
+// U32 length = 0;
+// for (LLDoubleLinkedNode<DATA_TYPE>* 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 <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+ void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+ DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getNthData(U32 n)
+{
+ mCurrentp = mHead.mNextp;
+
+ if (mCurrentp)
+ {
+ for (U32 i=0; i<n; i++)
+ {
+ mCurrentp = mCurrentp->mNextp;
+ 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 <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::moveCurrentData(LLDoubleLinkedList<DATA_TYPE> *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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::insertNode(LLDoubleLinkedNode<DATA_TYPE> *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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::insertData(DATA_TYPE *data)
+{
+ if (!data)
+ {
+ llerror("NULL data pointer passed to LLDoubleLinkedList::insertNode()", 0);
+ }
+ LLDoubleLinkedNode<DATA_TYPE> *node = new LLDoubleLinkedNode<DATA_TYPE>(data);
+ insertNode(node);
+ mCount++;
+}
+
+
+// if mCurrentp has a previous node then :
+// * swaps mCurrentp with its previous
+// * set mCurrentp to mQueuep
+// otherwise does nothing
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::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 && count<length)
+ {
+ if (mInsertBefore(mCurrentp->mNextp->mDatap, mCurrentp->mDatap))
+ {
+ b_swapped = TRUE;
+ mQueuep = mCurrentp;
+ swapCurrentWithNext(); // sets mCurrentp to mQueuep
+ }
+ count++;
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ return b_swapped;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::pushState()
+{
+ if (mStateStackDepth < LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH)
+ {
+ *(mQueuepStack + mStateStackDepth) = mQueuep;
+ *(mCurrentpStack + mStateStackDepth) = mCurrentp;
+ mStateStackDepth++;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::popState()
+{
+ if (mStateStackDepth > 0)
+ {
+ mStateStackDepth--;
+ mQueuep = *(mQueuepStack + mStateStackDepth);
+ mCurrentp = *(mCurrentpStack + mStateStackDepth);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::clearStateStack()
+{
+ mStateStackDepth = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// private members
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// add node to beginning of list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addNode(LLDoubleLinkedNode<DATA_TYPE> *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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addNodeAtEnd(LLDoubleLinkedNode<DATA_TYPE> *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 <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::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 <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::isEmpty()
+{
+ return (mCount == 0);
+}
+
+
+#endif