Imported existing code
This commit is contained in:
523
indra/llcommon/llskiplist.h
Normal file
523
indra/llcommon/llskiplist.h
Normal file
@@ -0,0 +1,523 @@
|
||||
/**
|
||||
* @file llskiplist.h
|
||||
* @brief skip list implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
#ifndef LL_LLSKIPLIST_H
|
||||
#define LL_LLSKIPLIST_H
|
||||
|
||||
#include "llrand.h"
|
||||
#include "llrand.h"
|
||||
|
||||
// NOTA BENE: Insert first needs to be < NOT <=
|
||||
// Binary depth must be >= 2
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH = 10>
|
||||
class LLSkipList
|
||||
{
|
||||
public:
|
||||
typedef BOOL (*compare)(const DATA_TYPE& first, const DATA_TYPE& second);
|
||||
typedef compare insert_func;
|
||||
typedef compare equals_func;
|
||||
|
||||
void init();
|
||||
|
||||
// basic constructor
|
||||
LLSkipList();
|
||||
|
||||
// basic constructor including sorter
|
||||
LLSkipList(insert_func insert_first, equals_func equals);
|
||||
~LLSkipList();
|
||||
|
||||
inline void setInsertFirst(insert_func insert_first);
|
||||
inline void setEquals(equals_func equals);
|
||||
|
||||
inline BOOL addData(const DATA_TYPE& data);
|
||||
inline BOOL checkData(const DATA_TYPE& data);
|
||||
|
||||
// returns number of items in the list
|
||||
inline S32 getLength() const; // NOT a constant time operation, traverses entire list!
|
||||
|
||||
inline BOOL moveData(const DATA_TYPE& data, LLSkipList *newlist);
|
||||
|
||||
inline BOOL removeData(const DATA_TYPE& data);
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
inline void removeAllNodes();
|
||||
|
||||
// place mCurrentp on first node
|
||||
inline void resetList();
|
||||
|
||||
// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
inline DATA_TYPE getCurrentData();
|
||||
|
||||
// same as getCurrentData() but a more intuitive name for the operation
|
||||
inline DATA_TYPE getNextData();
|
||||
|
||||
// remove the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
inline void removeCurrentData();
|
||||
|
||||
// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
inline DATA_TYPE getFirstData();
|
||||
|
||||
class LLSkipNode
|
||||
{
|
||||
public:
|
||||
LLSkipNode()
|
||||
: mData(0)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LLSkipNode(DATA_TYPE data)
|
||||
: mData(data)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
~LLSkipNode()
|
||||
{
|
||||
}
|
||||
|
||||
DATA_TYPE mData;
|
||||
LLSkipNode *mForward[BINARY_DEPTH];
|
||||
|
||||
private:
|
||||
// Disallow copying of LLSkipNodes by not implementing these methods.
|
||||
LLSkipNode(const LLSkipNode &);
|
||||
LLSkipNode &operator=(const LLSkipNode &);
|
||||
};
|
||||
|
||||
static BOOL defaultEquals(const DATA_TYPE& first, const DATA_TYPE& second)
|
||||
{
|
||||
return first == second;
|
||||
}
|
||||
|
||||
private:
|
||||
LLSkipNode mHead;
|
||||
LLSkipNode *mUpdate[BINARY_DEPTH];
|
||||
LLSkipNode *mCurrentp;
|
||||
LLSkipNode *mCurrentOperatingp;
|
||||
S32 mLevel;
|
||||
insert_func mInsertFirst;
|
||||
equals_func mEquals;
|
||||
|
||||
private:
|
||||
// Disallow copying of LLSkipNodes by not implementing these methods.
|
||||
LLSkipList(const LLSkipList &);
|
||||
LLSkipList &operator=(const LLSkipList &);
|
||||
};
|
||||
|
||||
|
||||
///////////////////////
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
|
||||
// Binary depth must be >= 2
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::init()
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
mLevel = 1;
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
|
||||
// basic constructor
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList()
|
||||
: mInsertFirst(NULL),
|
||||
mEquals(defaultEquals)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
// basic constructor including sorter
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList(insert_func insert,
|
||||
equals_func equals)
|
||||
: mInsertFirst(insert),
|
||||
mEquals(equals)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::~LLSkipList()
|
||||
{
|
||||
removeAllNodes();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(insert_func insert_first)
|
||||
{
|
||||
mInsertFirst = insert_first;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(equals_func equals)
|
||||
{
|
||||
mEquals = equals;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::addData(const DATA_TYPE& data)
|
||||
{
|
||||
S32 level;
|
||||
LLSkipNode *current = &mHead;
|
||||
LLSkipNode *temp;
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
// now add the new node
|
||||
S32 newlevel;
|
||||
for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
|
||||
{
|
||||
if (ll_frand() < 0.5f)
|
||||
break;
|
||||
}
|
||||
|
||||
LLSkipNode *snode = new LLSkipNode(data);
|
||||
|
||||
if (newlevel > mLevel)
|
||||
{
|
||||
mHead.mForward[mLevel] = NULL;
|
||||
mUpdate[mLevel] = &mHead;
|
||||
mLevel = newlevel;
|
||||
}
|
||||
|
||||
for (level = 0; level < newlevel; level++)
|
||||
{
|
||||
snode->mForward[level] = mUpdate[level]->mForward[level];
|
||||
mUpdate[level]->mForward[level] = snode;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE& data)
|
||||
{
|
||||
S32 level;
|
||||
LLSkipNode *current = &mHead;
|
||||
LLSkipNode *temp;
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
|
||||
if (current)
|
||||
{
|
||||
return mEquals(current->mData, data);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// returns number of items in the list
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline S32 LLSkipList<DATA_TYPE, BINARY_DEPTH>::getLength() const
|
||||
{
|
||||
U32 length = 0;
|
||||
for (LLSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE& data, LLSkipList *newlist)
|
||||
{
|
||||
BOOL removed = removeData(data);
|
||||
BOOL added = newlist->addData(data);
|
||||
return removed && added;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE& data)
|
||||
{
|
||||
S32 level;
|
||||
LLSkipNode *current = &mHead;
|
||||
LLSkipNode *temp;
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
|
||||
if (!current)
|
||||
{
|
||||
// empty list or beyond the end!
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// is this the one we want?
|
||||
if (!mEquals(current->mData, data))
|
||||
{
|
||||
// nope!
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do we need to fix current or currentop?
|
||||
if (current == mCurrentp)
|
||||
{
|
||||
mCurrentp = current->mForward[0];
|
||||
}
|
||||
|
||||
if (current == mCurrentOperatingp)
|
||||
{
|
||||
mCurrentOperatingp = current->mForward[0];
|
||||
}
|
||||
// yes it is! change pointers as required
|
||||
for (level = 0; level < mLevel; level++)
|
||||
{
|
||||
if (mUpdate[level]->mForward[level] != current)
|
||||
{
|
||||
// cool, we've fixed all the pointers!
|
||||
break;
|
||||
}
|
||||
mUpdate[level]->mForward[level] = current->mForward[level];
|
||||
}
|
||||
|
||||
// clean up cuurent
|
||||
delete current;
|
||||
|
||||
// clean up mHead
|
||||
while ( (mLevel > 1)
|
||||
&&(!mHead.mForward[mLevel - 1]))
|
||||
{
|
||||
mLevel--;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
|
||||
{
|
||||
LLSkipNode *temp;
|
||||
// reset mCurrentp
|
||||
mCurrentp = *(mHead.mForward);
|
||||
|
||||
while (mCurrentp)
|
||||
{
|
||||
temp = mCurrentp->mForward[0];
|
||||
delete mCurrentp;
|
||||
mCurrentp = temp;
|
||||
}
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// place mCurrentp on first node
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
|
||||
{
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = mCurrentp->mForward[0];
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return (DATA_TYPE)0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
// same as getCurrentData() but a more intuitive name for the operation
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = mCurrentp->mForward[0];
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return (DATA_TYPE)0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
// remove the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
removeData(mCurrentOperatingp->mData);
|
||||
}
|
||||
}
|
||||
|
||||
// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
|
||||
{
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = mCurrentp->mForward[0];
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return (DATA_TYPE)0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user