removed unused data structures
parent
49933aadb1
commit
ae028e7987
|
|
@ -34,7 +34,6 @@
|
|||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
#include "lluuidhashmap.h"
|
||||
#include "llmotion.h"
|
||||
#include "llpose.h"
|
||||
#include "llframetimer.h"
|
||||
|
|
|
|||
|
|
@ -212,8 +212,6 @@ set(llcommon_HEADER_FILES
|
|||
llpriqueuemap.h
|
||||
llprocess.h
|
||||
llprocessor.h
|
||||
llptrskiplist.h
|
||||
llptrskipmap.h
|
||||
llptrto.h
|
||||
llqueuedthread.h
|
||||
llrand.h
|
||||
|
|
@ -230,8 +228,6 @@ set(llcommon_HEADER_FILES
|
|||
llsecondlifeurls.h
|
||||
llsimplehash.h
|
||||
llsingleton.h
|
||||
llskiplist.h
|
||||
llskipmap.h
|
||||
llsortedvector.h
|
||||
llstack.h
|
||||
llstacktrace.h
|
||||
|
|
@ -255,7 +251,6 @@ set(llcommon_HEADER_FILES
|
|||
llunit.h
|
||||
lluri.h
|
||||
lluuid.h
|
||||
lluuidhashmap.h
|
||||
llversionserver.h
|
||||
llversionviewer.h
|
||||
llwin32headers.h
|
||||
|
|
|
|||
|
|
@ -1,724 +0,0 @@
|
|||
/**
|
||||
* @file llptrskiplist.h
|
||||
* @brief Skip list implementation.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPTRSKIPLIST_H
|
||||
#define LL_LLPTRSKIPLIST_H
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llrand.h"
|
||||
//#include "vmath.h"
|
||||
#include "llrand.h"
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
// LLPtrSkipList implementation - skip list for pointers to objects
|
||||
//
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH = 8>
|
||||
class LLPtrSkipList
|
||||
{
|
||||
public:
|
||||
friend class LLPtrSkipNode;
|
||||
|
||||
// basic constructor
|
||||
LLPtrSkipList();
|
||||
// basic constructor including sorter
|
||||
LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
|
||||
BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second));
|
||||
~LLPtrSkipList();
|
||||
|
||||
inline void setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second));
|
||||
inline void setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second));
|
||||
|
||||
inline BOOL addData(DATA_TYPE *data);
|
||||
|
||||
inline BOOL checkData(const DATA_TYPE *data);
|
||||
|
||||
inline S32 getLength(); // returns number of items in the list - NOT constant time!
|
||||
|
||||
inline BOOL removeData(const DATA_TYPE *data);
|
||||
|
||||
// note that b_sort is ignored
|
||||
inline BOOL moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort);
|
||||
|
||||
inline BOOL moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort);
|
||||
|
||||
// resort -- use when the value we're sorting by changes
|
||||
/* IW 12/6/02 - This doesn't work!
|
||||
Instead, remove the data BEFORE you change it
|
||||
Then re-insert it after you change it
|
||||
BOOL resortData(DATA_TYPE *data)
|
||||
*/
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
inline void removeAllNodes();
|
||||
|
||||
inline BOOL deleteData(const DATA_TYPE *data);
|
||||
|
||||
// remove all nodes from the list and delete data
|
||||
inline void deleteAllData();
|
||||
|
||||
// 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();
|
||||
|
||||
// delete the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
inline void deleteCurrentData();
|
||||
|
||||
// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
inline DATA_TYPE *getFirstData();
|
||||
|
||||
// TRUE if nodes are not in sorted order
|
||||
inline BOOL corrupt();
|
||||
|
||||
protected:
|
||||
class LLPtrSkipNode
|
||||
{
|
||||
public:
|
||||
LLPtrSkipNode()
|
||||
: mData(NULL)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LLPtrSkipNode(DATA_TYPE *data)
|
||||
: mData(data)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
~LLPtrSkipNode()
|
||||
{
|
||||
if (mData)
|
||||
{
|
||||
llerror("Attempting to call LLPtrSkipNode destructor with a non-null mDatap!", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// delete associated data and NULLs out pointer
|
||||
void deleteData()
|
||||
{
|
||||
delete mData;
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
// NULLs out pointer
|
||||
void removeData()
|
||||
{
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
DATA_TYPE *mData;
|
||||
LLPtrSkipNode *mForward[BINARY_DEPTH];
|
||||
};
|
||||
|
||||
static BOOL defaultEquals(const DATA_TYPE *first, const DATA_TYPE *second)
|
||||
{
|
||||
return first == second;
|
||||
}
|
||||
|
||||
|
||||
LLPtrSkipNode mHead;
|
||||
LLPtrSkipNode *mUpdate[BINARY_DEPTH];
|
||||
LLPtrSkipNode *mCurrentp;
|
||||
LLPtrSkipNode *mCurrentOperatingp;
|
||||
S32 mLevel;
|
||||
BOOL (*mInsertFirst)(const DATA_TYPE *first, const DATA_TYPE *second);
|
||||
BOOL (*mEquals)(const DATA_TYPE *first, const DATA_TYPE *second);
|
||||
};
|
||||
|
||||
|
||||
// basic constructor
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList()
|
||||
: mInsertFirst(NULL), mEquals(defaultEquals)
|
||||
{
|
||||
if (BINARY_DEPTH < 2)
|
||||
{
|
||||
llerrs << "Trying to create skip list with too little depth, "
|
||||
"must be 2 or greater" << llendl;
|
||||
}
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
mLevel = 1;
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// basic constructor including sorter
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
|
||||
BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second))
|
||||
:mInsertFirst(insert_first), mEquals(equals)
|
||||
{
|
||||
if (BINARY_DEPTH < 2)
|
||||
{
|
||||
llerrs << "Trying to create skip list with too little depth, "
|
||||
"must be 2 or greater" << llendl;
|
||||
}
|
||||
mLevel = 1;
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::~LLPtrSkipList()
|
||||
{
|
||||
removeAllNodes();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second))
|
||||
{
|
||||
mInsertFirst = insert_first;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second))
|
||||
{
|
||||
mEquals = equals;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::addData(DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *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;
|
||||
}
|
||||
|
||||
LLPtrSkipNode *snode = new LLPtrSkipNode(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 LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *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);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// returns number of items in the list
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline S32 LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getLength()
|
||||
{
|
||||
U32 length = 0;
|
||||
for (LLPtrSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *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
|
||||
{
|
||||
// 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
|
||||
current->removeData();
|
||||
delete current;
|
||||
|
||||
// clean up mHead
|
||||
while ( (mLevel > 1)
|
||||
&&(!mHead.mForward[mLevel - 1]))
|
||||
{
|
||||
mLevel--;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// note that b_sort is ignored
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort)
|
||||
{
|
||||
BOOL removed = removeData(data);
|
||||
BOOL added = newlist->addData(data);
|
||||
return removed && added;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort)
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
mCurrentp = mCurrentOperatingp->mForward[0];
|
||||
BOOL removed = removeData(mCurrentOperatingp);
|
||||
BOOL added = newlist->addData(mCurrentOperatingp);
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
return removed && added;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// resort -- use when the value we're sorting by changes
|
||||
/* IW 12/6/02 - This doesn't work!
|
||||
Instead, remove the data BEFORE you change it
|
||||
Then re-insert it after you change it
|
||||
BOOL resortData(DATA_TYPE *data)
|
||||
{
|
||||
removeData(data);
|
||||
addData(data);
|
||||
}
|
||||
*/
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
|
||||
{
|
||||
LLPtrSkipNode *temp;
|
||||
// reset mCurrentp
|
||||
mCurrentp = *(mHead.mForward);
|
||||
|
||||
while (mCurrentp)
|
||||
{
|
||||
temp = mCurrentp->mForward[0];
|
||||
mCurrentp->removeData();
|
||||
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);
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteData(const DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *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
|
||||
current->deleteData();
|
||||
delete current;
|
||||
|
||||
// clean up mHead
|
||||
while ( (mLevel > 1)
|
||||
&&(!mHead.mForward[mLevel - 1]))
|
||||
{
|
||||
mLevel--;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// remove all nodes from the list and delete data
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteAllData()
|
||||
{
|
||||
LLPtrSkipNode *temp;
|
||||
// reset mCurrentp
|
||||
mCurrentp = *(mHead.mForward);
|
||||
|
||||
while (mCurrentp)
|
||||
{
|
||||
temp = mCurrentp->mForward[0];
|
||||
mCurrentp->deleteData();
|
||||
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 LLPtrSkipList<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 *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = *mCurrentp->mForward;
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return 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 *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = *mCurrentp->mForward;
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return 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 LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
removeData(mCurrentOperatingp->mData);
|
||||
}
|
||||
}
|
||||
|
||||
// delete the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteCurrentData()
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
deleteData(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 *LLPtrSkipList<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 0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::corrupt()
|
||||
{
|
||||
LLPtrSkipNode *previous = mHead.mForward[0];
|
||||
|
||||
// Empty lists are not corrupt.
|
||||
if (!previous) return FALSE;
|
||||
|
||||
LLPtrSkipNode *current = previous->mForward[0];
|
||||
while(current)
|
||||
{
|
||||
if (!mInsertFirst(previous->mData, current->mData))
|
||||
{
|
||||
// prev shouldn't be in front of cur!
|
||||
return TRUE;
|
||||
}
|
||||
current = current->mForward[0];
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,517 +0,0 @@
|
|||
/**
|
||||
* @file llskiplist.h
|
||||
* @brief skip list implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,583 +0,0 @@
|
|||
/**
|
||||
* @file lluuidhashmap.h
|
||||
* @brief A uuid based hash map.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLUUIDHASHMAP_H
|
||||
#define LL_LLUUIDHASHMAP_H
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include "llerror.h"
|
||||
#include "lluuid.h"
|
||||
|
||||
// UUID hash map
|
||||
|
||||
/*
|
||||
LLUUIDHashMap<uuid_pair, 32> foo(test_equals);
|
||||
LLUUIDHashMapIter<uuid_pair, 32> bar(&foo);
|
||||
|
||||
LLDynamicArray<LLUUID> source_ids;
|
||||
const S32 COUNT = 100000;
|
||||
S32 q;
|
||||
for (q = 0; q < COUNT; q++)
|
||||
{
|
||||
llinfos << "Creating" << llendl;
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
//llinfos << q << ":" << id << llendl;
|
||||
uuid_pair pair;
|
||||
pair.mUUID = id;
|
||||
pair.mValue = q;
|
||||
foo.set(id, pair);
|
||||
source_ids.put(id);
|
||||
//ms_sleep(1);
|
||||
}
|
||||
|
||||
uuid_pair cur;
|
||||
llinfos << "Iterating" << llendl;
|
||||
for (cur = bar.first(); !bar.done(); cur = bar.next())
|
||||
{
|
||||
if (source_ids[cur.mValue] != cur.mUUID)
|
||||
{
|
||||
llerrs << "Incorrect value iterated!" << llendl;
|
||||
}
|
||||
//llinfos << cur.mValue << ":" << cur.mUUID << llendl;
|
||||
//ms_sleep(1);
|
||||
}
|
||||
|
||||
llinfos << "Finding" << llendl;
|
||||
for (q = 0; q < COUNT; q++)
|
||||
{
|
||||
cur = foo.get(source_ids[q]);
|
||||
if (source_ids[cur.mValue] != cur.mUUID)
|
||||
{
|
||||
llerrs << "Incorrect value found!" << llendl;
|
||||
}
|
||||
//llinfos << res.mValue << ":" << res.mUUID << llendl;
|
||||
//ms_sleep(1);
|
||||
}
|
||||
|
||||
llinfos << "Removing" << llendl;
|
||||
for (q = 0; q < COUNT/2; q++)
|
||||
{
|
||||
if (!foo.remove(source_ids[q]))
|
||||
{
|
||||
llerrs << "Remove failed!" << llendl;
|
||||
}
|
||||
//ms_sleep(1);
|
||||
}
|
||||
|
||||
llinfos << "Iterating" << llendl;
|
||||
for (cur = bar.first(); !bar.done(); cur = bar.next())
|
||||
{
|
||||
if (source_ids[cur.mValue] != cur.mUUID)
|
||||
{
|
||||
llerrs << "Incorrect value found!" << llendl;
|
||||
}
|
||||
//llinfos << cur.mValue << ":" << cur.mUUID << llendl;
|
||||
//ms_sleep(1);
|
||||
}
|
||||
llinfos << "Done with UUID map test" << llendl;
|
||||
|
||||
return 0;
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// LLUUIDHashNode
|
||||
//
|
||||
|
||||
template <class DATA, int SIZE>
|
||||
class LLUUIDHashNode
|
||||
{
|
||||
public:
|
||||
LLUUIDHashNode();
|
||||
|
||||
public:
|
||||
S32 mCount;
|
||||
U8 mKey[SIZE];
|
||||
DATA mData[SIZE];
|
||||
LLUUIDHashNode<DATA, SIZE> *mNextNodep;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// LLUUIDHashNode implementation
|
||||
//
|
||||
template <class DATA, int SIZE>
|
||||
LLUUIDHashNode<DATA, SIZE>::LLUUIDHashNode()
|
||||
{
|
||||
mCount = 0;
|
||||
mNextNodep = NULL;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
class LLUUIDHashMap
|
||||
{
|
||||
public:
|
||||
// basic constructor including sorter
|
||||
LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
|
||||
const DATA_TYPE &null_data);
|
||||
~LLUUIDHashMap();
|
||||
|
||||
inline DATA_TYPE &get(const LLUUID &uuid);
|
||||
inline BOOL check(const LLUUID &uuid) const;
|
||||
inline DATA_TYPE &set(const LLUUID &uuid, const DATA_TYPE &type);
|
||||
inline BOOL remove(const LLUUID &uuid);
|
||||
void removeAll();
|
||||
|
||||
inline S32 getLength() const; // Warning, NOT O(1!)
|
||||
public:
|
||||
BOOL (*mEquals)(const LLUUID &uuid, const DATA_TYPE &data);
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE> mNodes[256];
|
||||
|
||||
S32 mIterCount;
|
||||
protected:
|
||||
DATA_TYPE mNull;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// LLUUIDHashMap implementation
|
||||
//
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLUUIDHashMap<DATA_TYPE, SIZE>::LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
|
||||
const DATA_TYPE &null_data)
|
||||
: mEquals(equals),
|
||||
mIterCount(0),
|
||||
mNull(null_data)
|
||||
{ }
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLUUIDHashMap<DATA_TYPE, SIZE>::~LLUUIDHashMap()
|
||||
{
|
||||
removeAll();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
void LLUUIDHashMap<DATA_TYPE, SIZE>::removeAll()
|
||||
{
|
||||
S32 bin;
|
||||
for (bin = 0; bin < 256; bin++)
|
||||
{
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
|
||||
|
||||
BOOL first = TRUE;
|
||||
while (nodep)
|
||||
{
|
||||
S32 i;
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
// Iterate through all members of this node
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
nodep->mData[i] = mNull;
|
||||
}
|
||||
|
||||
nodep->mCount = 0;
|
||||
// Done with all objects in this node, go to the next.
|
||||
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
|
||||
nodep = nodep->mNextNodep;
|
||||
|
||||
// Delete the node if it's not the first node
|
||||
if (first)
|
||||
{
|
||||
first = FALSE;
|
||||
curp->mNextNodep = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete curp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline S32 LLUUIDHashMap<DATA_TYPE, SIZE>::getLength() const
|
||||
{
|
||||
S32 count = 0;
|
||||
S32 bin;
|
||||
for (bin = 0; bin < 256; bin++)
|
||||
{
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = (LLUUIDHashNode<DATA_TYPE, SIZE>*) &mNodes[bin];
|
||||
while (nodep)
|
||||
{
|
||||
count += nodep->mCount;
|
||||
nodep = nodep->mNextNodep;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::get(const LLUUID &uuid)
|
||||
{
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
|
||||
|
||||
// Grab the second byte of the UUID, which is the key for the node data
|
||||
const S32 second_byte = uuid.mData[1];
|
||||
while (nodep)
|
||||
{
|
||||
S32 i;
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
// Iterate through all members of this node
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
|
||||
{
|
||||
// The second byte matched, and our equality test passed.
|
||||
// We found it.
|
||||
return nodep->mData[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Done with all objects in this node, go to the next.
|
||||
nodep = nodep->mNextNodep;
|
||||
}
|
||||
return mNull;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::check(const LLUUID &uuid) const
|
||||
{
|
||||
const LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
|
||||
|
||||
// Grab the second byte of the UUID, which is the key for the node data
|
||||
const S32 second_byte = uuid.mData[1];
|
||||
while (nodep)
|
||||
{
|
||||
S32 i;
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
// Iterate through all members of this node
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
|
||||
{
|
||||
// The second byte matched, and our equality test passed.
|
||||
// We found it.
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Done with all objects in this node, go to the next.
|
||||
nodep = nodep->mNextNodep;
|
||||
}
|
||||
|
||||
// Didn't find anything
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::set(const LLUUID &uuid, const DATA_TYPE &data)
|
||||
{
|
||||
// Set is just like a normal find, except that if we find a match
|
||||
// we replace it with the input value.
|
||||
// If we don't find a match, we append to the end of the list.
|
||||
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
|
||||
|
||||
const S32 second_byte = uuid.mData[1];
|
||||
while (1)
|
||||
{
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
|
||||
{
|
||||
// We found a match for this key, replace the data with
|
||||
// the incoming data.
|
||||
nodep->mData[i] = data;
|
||||
return nodep->mData[i];
|
||||
}
|
||||
}
|
||||
if (!nodep->mNextNodep)
|
||||
{
|
||||
// We've iterated through all of the keys without finding a match
|
||||
if (i < SIZE)
|
||||
{
|
||||
// There's still some space on this node, append
|
||||
// the key and data to it.
|
||||
nodep->mKey[i] = second_byte;
|
||||
nodep->mData[i] = data;
|
||||
nodep->mCount++;
|
||||
|
||||
return nodep->mData[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// This node is full, append a new node to the end.
|
||||
nodep->mNextNodep = new LLUUIDHashNode<DATA_TYPE, SIZE>;
|
||||
nodep->mNextNodep->mKey[0] = second_byte;
|
||||
nodep->mNextNodep->mData[0] = data;
|
||||
nodep->mNextNodep->mCount = 1;
|
||||
|
||||
return nodep->mNextNodep->mData[0];
|
||||
}
|
||||
}
|
||||
|
||||
// No match on this node, go to the next
|
||||
nodep = nodep->mNextNodep;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::remove(const LLUUID &uuid)
|
||||
{
|
||||
if (mIterCount)
|
||||
{
|
||||
// We don't allow remove when we're iterating, it's bad karma!
|
||||
llerrs << "Attempted remove while an outstanding iterator in LLUUIDHashMap!" << llendl;
|
||||
}
|
||||
// Remove is the trickiest operation.
|
||||
// What we want to do is swap the last element of the last
|
||||
// node if we find the one that we want to remove, but we have
|
||||
// to deal with deleting the node from the tail if it's empty, but
|
||||
// NOT if it's the only node left.
|
||||
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE> *nodep = &mNodes[uuid.mData[0]];
|
||||
|
||||
// Not empty, we need to search through the nodes
|
||||
const S32 second_byte = uuid.mData[1];
|
||||
|
||||
// A modification of the standard search algorithm.
|
||||
while (nodep)
|
||||
{
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
|
||||
{
|
||||
// We found the node that we want to remove.
|
||||
// Find the last (and next-to-last) node, and the index of the last
|
||||
// element. We could conceviably start from the node we're on,
|
||||
// but that makes it more complicated, this is easier.
|
||||
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[uuid.mData[0]];
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
|
||||
|
||||
// Find the last and next-to-last
|
||||
while (lastp->mNextNodep)
|
||||
{
|
||||
prevp = lastp;
|
||||
lastp = lastp->mNextNodep;
|
||||
}
|
||||
|
||||
// First, swap in the last to the current location.
|
||||
nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
|
||||
nodep->mData[i] = lastp->mData[lastp->mCount - 1];
|
||||
|
||||
// Now, we delete the entry
|
||||
lastp->mCount--;
|
||||
lastp->mData[lastp->mCount] = mNull;
|
||||
|
||||
if (!lastp->mCount)
|
||||
{
|
||||
// We deleted the last element!
|
||||
if (lastp != &mNodes[uuid.mData[0]])
|
||||
{
|
||||
// Only blitz the node if it's not the head
|
||||
// Set the previous node to point to NULL, then
|
||||
// blitz the empty last node
|
||||
prevp->mNextNodep = NULL;
|
||||
delete lastp;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate to the next node, we've scanned all the entries in this one.
|
||||
nodep = nodep->mNextNodep;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LLUUIDHashMapIter
|
||||
//
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
class LLUUIDHashMapIter
|
||||
{
|
||||
public:
|
||||
LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
|
||||
~LLUUIDHashMapIter();
|
||||
|
||||
|
||||
inline void reset();
|
||||
inline void first();
|
||||
inline void next();
|
||||
inline BOOL done() const;
|
||||
|
||||
DATA_TYPE& operator*() const
|
||||
{
|
||||
return mCurHashNodep->mData[mCurHashNodeKey];
|
||||
}
|
||||
DATA_TYPE* operator->() const
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
|
||||
protected:
|
||||
LLUUIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
|
||||
LLUUIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
|
||||
|
||||
S32 mCurHashMapNodeNum;
|
||||
S32 mCurHashNodeKey;
|
||||
|
||||
DATA_TYPE mNull;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// LLUUIDHashMapIter Implementation
|
||||
//
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLUUIDHashMapIter<DATA_TYPE, SIZE>::LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
|
||||
{
|
||||
mHashMapp = hash_mapp;
|
||||
mCurHashNodep = NULL;
|
||||
mCurHashMapNodeNum = 0;
|
||||
mCurHashNodeKey = 0;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLUUIDHashMapIter<DATA_TYPE, SIZE>::~LLUUIDHashMapIter()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::reset()
|
||||
{
|
||||
if (mCurHashNodep)
|
||||
{
|
||||
// We're partway through an iteration, we can clean up now
|
||||
mHashMapp->mIterCount--;
|
||||
mCurHashNodep = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::first()
|
||||
{
|
||||
// Iterate through until we find the first non-empty node;
|
||||
S32 i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
if (mHashMapp->mNodes[i].mCount)
|
||||
{
|
||||
if (!mCurHashNodep)
|
||||
{
|
||||
// Increment, since it's no longer safe for us to do a remove
|
||||
mHashMapp->mIterCount++;
|
||||
}
|
||||
|
||||
mCurHashNodep = &mHashMapp->mNodes[i];
|
||||
mCurHashMapNodeNum = i;
|
||||
mCurHashNodeKey = 0;
|
||||
//return mCurHashNodep->mData[0];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Completely empty!
|
||||
mCurHashNodep = NULL;
|
||||
//return mNull;
|
||||
return;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline BOOL LLUUIDHashMapIter<DATA_TYPE, SIZE>::done() const
|
||||
{
|
||||
return mCurHashNodep ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::next()
|
||||
{
|
||||
// No current entry, this iterator is done
|
||||
if (!mCurHashNodep)
|
||||
{
|
||||
//return mNull;
|
||||
return;
|
||||
}
|
||||
|
||||
// Go to the next element
|
||||
mCurHashNodeKey++;
|
||||
if (mCurHashNodeKey < mCurHashNodep->mCount)
|
||||
{
|
||||
// We're not done with this node, return the current element
|
||||
//return mCurHashNodep->mData[mCurHashNodeKey];
|
||||
return;
|
||||
}
|
||||
|
||||
// Done with this node, move to the next
|
||||
mCurHashNodep = mCurHashNodep->mNextNodep;
|
||||
if (mCurHashNodep)
|
||||
{
|
||||
// Return the first element
|
||||
mCurHashNodeKey = 0;
|
||||
//return mCurHashNodep->mData[0];
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the next non-empty node (keyed on the first byte)
|
||||
mCurHashMapNodeNum++;
|
||||
|
||||
S32 i;
|
||||
for (i = mCurHashMapNodeNum; i < 256; i++)
|
||||
{
|
||||
if (mHashMapp->mNodes[i].mCount)
|
||||
{
|
||||
// We found one that wasn't empty
|
||||
mCurHashNodep = &mHashMapp->mNodes[i];
|
||||
mCurHashMapNodeNum = i;
|
||||
mCurHashNodeKey = 0;
|
||||
//return mCurHashNodep->mData[0];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// OK, we're done, nothing else to iterate
|
||||
mCurHashNodep = NULL;
|
||||
mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
|
||||
//return mNull;
|
||||
}
|
||||
|
||||
#endif // LL_LLUUIDHASHMAP_H
|
||||
|
|
@ -83,7 +83,6 @@
|
|||
#include "llsys.h"
|
||||
#include "llthread.h"
|
||||
#include "lltimer.h"
|
||||
#include "lluuidhashmap.h"
|
||||
#include "stdenums.h"
|
||||
#include "stdtypes.h"
|
||||
#include "timing.h"
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ set(test_SOURCE_FILES
|
|||
llstreamtools_tut.cpp
|
||||
lltemplatemessagebuilder_tut.cpp
|
||||
lltut.cpp
|
||||
lluuidhashmap_tut.cpp
|
||||
message_tut.cpp
|
||||
test.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,455 +0,0 @@
|
|||
/**
|
||||
* @file lluuidhashmap_tut.cpp
|
||||
* @author Adroit
|
||||
* @date 2007-02
|
||||
* @brief Test cases for LLUUIDHashMap
|
||||
*
|
||||
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include <tut/tut.hpp>
|
||||
#include "linden_common.h"
|
||||
#include "lluuidhashmap.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "lldir.h"
|
||||
#include "stringize.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace tut
|
||||
{
|
||||
class UUIDTableEntry
|
||||
{
|
||||
public:
|
||||
UUIDTableEntry()
|
||||
{
|
||||
mID.setNull();
|
||||
mValue = 0;
|
||||
}
|
||||
|
||||
UUIDTableEntry(const LLUUID& id, U32 value)
|
||||
{
|
||||
mID = id;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
~UUIDTableEntry(){};
|
||||
|
||||
static BOOL uuidEq(const LLUUID &uuid, const UUIDTableEntry &id_pair)
|
||||
{
|
||||
if (uuid == id_pair.mID)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const LLUUID& getID() { return mID; }
|
||||
const U32& getValue() { return mValue; }
|
||||
|
||||
protected:
|
||||
LLUUID mID;
|
||||
U32 mValue;
|
||||
};
|
||||
|
||||
struct hashmap_test
|
||||
{
|
||||
};
|
||||
|
||||
typedef test_group<hashmap_test> hash_index_t;
|
||||
typedef hash_index_t::object hash_index_object_t;
|
||||
tut::hash_index_t tut_hash_index("hashmap_test");
|
||||
|
||||
// stress test
|
||||
template<> template<>
|
||||
void hash_index_object_t::test<1>()
|
||||
{
|
||||
set_test_name("stress test");
|
||||
// As of 2012-10-10, I (nat) have observed sporadic failures of this
|
||||
// test: "set/get did not work." The trouble is that since test data
|
||||
// are randomly generated with every run, it is impossible to debug a
|
||||
// test failure. One is left with the uneasy suspicion that
|
||||
// LLUUID::generate() can sometimes produce duplicates even within the
|
||||
// moderately small number requested here. Since rerunning the test
|
||||
// generally allows it to pass, it's too easy to shrug and forget it.
|
||||
// The following code is intended to support reproducing such test
|
||||
// failures. The idea is that, on test failure, we save the generated
|
||||
// data to a canonical filename in a temp directory. Then on every
|
||||
// subsequent run, we check for that filename. If it exists, we reload
|
||||
// that specific data rather than generating fresh data -- which
|
||||
// should presumably reproduce the same test failure. But we inform
|
||||
// the user that to resume normal (random) test runs, s/he need only
|
||||
// delete that file. And since it's in a temp directory, sooner or
|
||||
// later the system will clean it up anyway.
|
||||
const char* tempvar = "TEMP";
|
||||
const char* tempdir = getenv(tempvar); // Windows convention
|
||||
if (! tempdir)
|
||||
{
|
||||
tempvar = "TMPDIR";
|
||||
tempdir = getenv(tempvar); // Mac convention
|
||||
}
|
||||
if (! tempdir)
|
||||
{
|
||||
// reset tempvar to the first var we check; it's just a
|
||||
// recommendation
|
||||
tempvar = "TEMP";
|
||||
tempdir = "/tmp"; // Posix in general
|
||||
}
|
||||
std::string savefile(gDirUtilp->add(tempdir, "lluuidhashmap_tut.save.txt"));
|
||||
const int numElementsToCheck = 32*256*32;
|
||||
std::vector<LLUUID> idList;
|
||||
if ((! getenv("TEAMCITY_PROJECT_NAME")) && gDirUtilp->fileExists(savefile))
|
||||
{
|
||||
// This is not a TeamCity build, and we have saved data from a
|
||||
// previous failed run. Reload that data.
|
||||
std::ifstream inf(savefile.c_str());
|
||||
if (! inf.is_open())
|
||||
{
|
||||
fail(STRINGIZE("Although save file '" << savefile << "' exists, it cannot be opened"));
|
||||
}
|
||||
std::string item;
|
||||
while (std::getline(inf, item))
|
||||
{
|
||||
idList.push_back(LLUUID(item));
|
||||
}
|
||||
std::cout << "Reloaded " << idList.size() << " items from '" << savefile << "'";
|
||||
if (idList.size() != numElementsToCheck)
|
||||
{
|
||||
std::cout << " (expected " << numElementsToCheck << ")";
|
||||
}
|
||||
std::cout << " -- delete this file to generate new data" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a TeamCity build, or (normal case) savefile does not
|
||||
// exist: regenerate idList from scratch.
|
||||
for (int i = 0; i < numElementsToCheck; ++i)
|
||||
{
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
idList.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
LLUUIDHashMap<UUIDTableEntry, 32> hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry());
|
||||
int i;
|
||||
|
||||
for (i = 0; i < idList.size(); ++i)
|
||||
{
|
||||
UUIDTableEntry entry(idList[i], i);
|
||||
hashTable.set(idList[i], entry);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
for (i = 0; i < idList.size(); i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
UUIDTableEntry entryToCheck = hashTable.get(idToCheck);
|
||||
ensure_equals(STRINGIZE("set/get ID (entry " << i << ")").c_str(),
|
||||
entryToCheck.getID(), idToCheck);
|
||||
ensure_equals(STRINGIZE("set/get value (ID " << idToCheck << ")").c_str(),
|
||||
entryToCheck.getValue(), (size_t)i);
|
||||
}
|
||||
|
||||
for (i = 0; i < idList.size(); i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
if (i % 2 != 0)
|
||||
{
|
||||
hashTable.remove(idToCheck);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < idList.size(); i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
ensure("remove or check did not work", (i % 2 == 0 && hashTable.check(idToCheck)) || (i % 2 != 0 && !hashTable.check(idToCheck)));
|
||||
}
|
||||
}
|
||||
catch (const failure&)
|
||||
{
|
||||
// One of the above tests failed. Try to save idList to repro with
|
||||
// a later run.
|
||||
std::ofstream outf(savefile.c_str());
|
||||
if (! outf.is_open())
|
||||
{
|
||||
// Sigh, don't use fail() here because we want to preserve
|
||||
// the original test failure.
|
||||
std::cout << "Cannot open file '" << savefile
|
||||
<< "' to save data -- check and fix " << tempvar << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// outf.is_open()
|
||||
for (int i = 0; i < idList.size(); ++i)
|
||||
{
|
||||
outf << idList[i] << std::endl;
|
||||
}
|
||||
std::cout << "Saved " << idList.size() << " entries to '" << savefile
|
||||
<< "' -- rerun test to debug with these" << std::endl;
|
||||
}
|
||||
// re-raise the same exception -- we WANT this test failure to
|
||||
// be reported! We just needed to save the data on the way out.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// test removing all but one element.
|
||||
template<> template<>
|
||||
void hash_index_object_t::test<2>()
|
||||
{
|
||||
LLUUIDHashMap<UUIDTableEntry, 2> hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry());
|
||||
const int numElementsToCheck = 5;
|
||||
std::vector<LLUUID> idList(numElementsToCheck*10);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
UUIDTableEntry entry(id, i);
|
||||
hashTable.set(id, entry);
|
||||
idList[i] = id;
|
||||
}
|
||||
|
||||
ensure("getLength failed", hashTable.getLength() == numElementsToCheck);
|
||||
|
||||
// remove all but the last element
|
||||
for (i = 0; i < numElementsToCheck-1; i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
hashTable.remove(idToCheck);
|
||||
}
|
||||
|
||||
// there should only be one element left now.
|
||||
ensure("getLength failed", hashTable.getLength() == 1);
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
if (i != numElementsToCheck - 1)
|
||||
{
|
||||
ensure("remove did not work", hashTable.check(idToCheck) == FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
UUIDTableEntry entryToCheck = hashTable.get(idToCheck);
|
||||
ensure("remove did not work", entryToCheck.getID() == idToCheck && entryToCheck.getValue() == (size_t)i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test overriding of value already set.
|
||||
template<> template<>
|
||||
void hash_index_object_t::test<3>()
|
||||
{
|
||||
LLUUIDHashMap<UUIDTableEntry, 5> hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry());
|
||||
const int numElementsToCheck = 10;
|
||||
std::vector<LLUUID> idList(numElementsToCheck);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
UUIDTableEntry entry(id, i);
|
||||
hashTable.set(id, entry);
|
||||
idList[i] = id;
|
||||
}
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID id = idList[i];
|
||||
// set new entry with value = i+numElementsToCheck
|
||||
UUIDTableEntry entry(id, i+numElementsToCheck);
|
||||
hashTable.set(id, entry);
|
||||
}
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
UUIDTableEntry entryToCheck = hashTable.get(idToCheck);
|
||||
ensure("set/get did not work", entryToCheck.getID() == idToCheck && entryToCheck.getValue() == (size_t)(i+numElementsToCheck));
|
||||
}
|
||||
}
|
||||
|
||||
// test removeAll()
|
||||
template<> template<>
|
||||
void hash_index_object_t::test<4>()
|
||||
{
|
||||
LLUUIDHashMap<UUIDTableEntry, 5> hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry());
|
||||
const int numElementsToCheck = 10;
|
||||
std::vector<LLUUID> idList(numElementsToCheck);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
UUIDTableEntry entry(id, i);
|
||||
hashTable.set(id, entry);
|
||||
idList[i] = id;
|
||||
}
|
||||
|
||||
hashTable.removeAll();
|
||||
ensure("removeAll failed", hashTable.getLength() == 0);
|
||||
}
|
||||
|
||||
|
||||
// test sparse map - force it by creating 256 entries that fall into 256 different nodes
|
||||
template<> template<>
|
||||
void hash_index_object_t::test<5>()
|
||||
{
|
||||
LLUUIDHashMap<UUIDTableEntry, 2> hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry());
|
||||
const int numElementsToCheck = 256;
|
||||
std::vector<LLUUID> idList(numElementsToCheck);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
// LLUUIDHashMap uses mData[0] to pick the bucket
|
||||
// overwrite mData[0] so that it ranges from 0 to 255
|
||||
id.mData[0] = i;
|
||||
UUIDTableEntry entry(id, i);
|
||||
hashTable.set(id, entry);
|
||||
idList[i] = id;
|
||||
}
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
UUIDTableEntry entryToCheck = hashTable.get(idToCheck);
|
||||
ensure("set/get did not work for sparse map", entryToCheck.getID() == idToCheck && entryToCheck.getValue() == (size_t)i);
|
||||
}
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
if (i % 2 != 0)
|
||||
{
|
||||
hashTable.remove(idToCheck);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID idToCheck = idList[i];
|
||||
ensure("remove or check did not work for sparse map", (i % 2 == 0 && hashTable.check(idToCheck)) || (i % 2 != 0 && !hashTable.check(idToCheck)));
|
||||
}
|
||||
}
|
||||
|
||||
// iterator
|
||||
template<> template<>
|
||||
void hash_index_object_t::test<6>()
|
||||
{
|
||||
LLUUIDHashMap<UUIDTableEntry, 2> hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry());
|
||||
LLUUIDHashMapIter<UUIDTableEntry, 2> hashIter(&hashTable);
|
||||
const int numElementsToCheck = 256;
|
||||
std::vector<LLUUID> idList(numElementsToCheck);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
// LLUUIDHashMap uses mData[0] to pick the bucket
|
||||
// overwrite mData[0] so that it ranges from 0 to 255
|
||||
// to create a sparse map
|
||||
id.mData[0] = i;
|
||||
UUIDTableEntry entry(id, i);
|
||||
hashTable.set(id, entry);
|
||||
idList[i] = id;
|
||||
}
|
||||
|
||||
hashIter.first();
|
||||
int numElementsIterated = 0;
|
||||
while(!hashIter.done())
|
||||
{
|
||||
numElementsIterated++;
|
||||
UUIDTableEntry tableEntry = *hashIter;
|
||||
LLUUID id = tableEntry.getID();
|
||||
hashIter.next();
|
||||
ensure("Iteration failed for sparse map", tableEntry.getValue() < (size_t)numElementsToCheck && idList[tableEntry.getValue()] == tableEntry.getID());
|
||||
}
|
||||
|
||||
ensure("iteration count failed", numElementsIterated == numElementsToCheck);
|
||||
}
|
||||
|
||||
// remove after middle of iteration
|
||||
template<> template<>
|
||||
void hash_index_object_t::test<7>()
|
||||
{
|
||||
LLUUIDHashMap<UUIDTableEntry, 2> hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry());
|
||||
LLUUIDHashMapIter<UUIDTableEntry, 2> hashIter(&hashTable);
|
||||
const int numElementsToCheck = 256;
|
||||
std::vector<LLUUID> idList(numElementsToCheck);
|
||||
int i;
|
||||
|
||||
LLUUID uuidtoSearch;
|
||||
for (i = 0; i < numElementsToCheck; i++)
|
||||
{
|
||||
LLUUID id;
|
||||
id.generate();
|
||||
// LLUUIDHashMap uses mData[0] to pick the bucket
|
||||
// overwrite mData[0] so that it ranges from 0 to 255
|
||||
// to create a sparse map
|
||||
id.mData[0] = i;
|
||||
UUIDTableEntry entry(id, i);
|
||||
hashTable.set(id, entry);
|
||||
idList[i] = id;
|
||||
|
||||
// pick uuid somewhere in the middle
|
||||
if (i == 5)
|
||||
{
|
||||
uuidtoSearch = id;
|
||||
}
|
||||
}
|
||||
|
||||
hashIter.first();
|
||||
int numElementsIterated = 0;
|
||||
while(!hashIter.done())
|
||||
{
|
||||
numElementsIterated++;
|
||||
UUIDTableEntry tableEntry = *hashIter;
|
||||
LLUUID id = tableEntry.getID();
|
||||
if (uuidtoSearch == id)
|
||||
{
|
||||
break;
|
||||
}
|
||||
hashIter.next();
|
||||
}
|
||||
|
||||
// current iterator implementation will not allow any remove operations
|
||||
// until ALL elements have been iterated over. this seems to be
|
||||
// an unnecessary restriction. Iterator should have a method to
|
||||
// reset() its state so that further operations (inckuding remove)
|
||||
// can be performed on the HashMap without having to iterate thru
|
||||
// all the remaining nodes.
|
||||
|
||||
// hashIter.reset();
|
||||
// hashTable.remove(uuidtoSearch);
|
||||
// ensure("remove after iteration reset failed", hashTable.check(uuidtoSearch) == FALSE);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue