/** * @file llptrskipmap.h * @brief Just like a LLSkipMap, but since it's pointers, you can call * deleteAllData * * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. * $License$ */ #ifndef LL_LLPTRSKIPMAP_H #define LL_LLPTRSKIPMAP_H #include "llerror.h" #include "llrand.h" template class LLPtrSkipMapNode { public: LLPtrSkipMapNode() { S32 i; for (i = 0; i < BINARY_DEPTH; i++) { mForward[i] = NULL; } U8 *zero = (U8 *)&mIndex; for (i = 0; i < (S32)sizeof(INDEX_T); i++) { *(zero + i) = 0; } zero = (U8 *)&mData; for (i = 0; i < (S32)sizeof(DATA_T); i++) { *(zero + i) = 0; } } LLPtrSkipMapNode(const INDEX_T &index) : mIndex(index) { S32 i; for (i = 0; i < BINARY_DEPTH; i++) { mForward[i] = NULL; } U8 *zero = (U8 *)&mData; for (i = 0; i < (S32)sizeof(DATA_T); i++) { *(zero + i) = 0; } } LLPtrSkipMapNode(const INDEX_T &index, DATA_T datap) : mIndex(index) { S32 i; for (i = 0; i < BINARY_DEPTH; i++) { mForward[i] = NULL; } mData = datap; } ~LLPtrSkipMapNode() { } // delete associated data and NULLs out pointer void deleteData() { delete mData; mData = 0; } // NULLs out pointer void removeData() { mData = 0; } INDEX_T mIndex; DATA_T mData; LLPtrSkipMapNode *mForward[BINARY_DEPTH]; private: // Disallow copying of LLPtrSkipMapNodes by not implementing these methods. LLPtrSkipMapNode(const LLPtrSkipMapNode &); LLPtrSkipMapNode &operator=(const LLPtrSkipMapNode &rhs); }; //--------------------------------------------------------------------------- template class LLPtrSkipMap { public: typedef BOOL (*compare)(const DATA_T& first, const DATA_T& second); typedef compare insert_func; typedef compare equals_func; void init(); // basic constructor LLPtrSkipMap(); // basic constructor including sorter LLPtrSkipMap(insert_func insert_first, equals_func equals); ~LLPtrSkipMap(); void setInsertFirst(insert_func insert_first); void setEquals(equals_func equals); DATA_T &addData(const INDEX_T &index, DATA_T datap); DATA_T &addData(const INDEX_T &index); DATA_T &getData(const INDEX_T &index); DATA_T &operator[](const INDEX_T &index); // If index present, returns data. // If index not present, adds and returns NULL. DATA_T &getData(const INDEX_T &index, BOOL &b_new_entry); // returns data entry before and after index BOOL getInterval(const INDEX_T &index, INDEX_T &index_before, INDEX_T &index_after, DATA_T &data_before, DATA_T &data_after ); // Returns TRUE if data present in map. BOOL checkData(const INDEX_T &index); // Returns TRUE if key is present in map. This is useful if you // are potentially storing NULL pointers in the map BOOL checkKey(const INDEX_T &index); // If there, returns the data. // If not, returns NULL. // Never adds entries to the map. DATA_T getIfThere(const INDEX_T &index); INDEX_T reverseLookup(const DATA_T datap); // returns number of items in the list S32 getLength(); // WARNING! getLength is O(n), not O(1)! BOOL removeData(const INDEX_T &index); BOOL deleteData(const INDEX_T &index); // remove all nodes from the list but do not delete data void removeAllData(); void deleteAllData(); // place mCurrentp on first node void resetList(); // return the data currently pointed to DATA_T getCurrentDataWithoutIncrement(); // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp DATA_T getCurrentData(); // same as getCurrentData() but a more intuitive name for the operation DATA_T getNextData(); INDEX_T getNextKey(); // return the key currently pointed to INDEX_T getCurrentKeyWithoutIncrement(); // remove the Node at mCurentOperatingp // leave mCurrentp and mCurentOperatingp on the next entry void removeCurrentData(); void deleteCurrentData(); // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp DATA_T getFirstData(); INDEX_T getFirstKey(); static BOOL defaultEquals(const INDEX_T &first, const INDEX_T &second) { return first == second; } private: // don't generate implicit copy constructor or copy assignment LLPtrSkipMap(const LLPtrSkipMap &); LLPtrSkipMap &operator=(const LLPtrSkipMap &); private: LLPtrSkipMapNode mHead; LLPtrSkipMapNode *mUpdate[BINARY_DEPTH]; LLPtrSkipMapNode *mCurrentp; LLPtrSkipMapNode *mCurrentOperatingp; S32 mLevel; BOOL (*mInsertFirst)(const INDEX_T &first, const INDEX_T &second); BOOL (*mEquals)(const INDEX_T &first, const INDEX_T &second); S32 mNumberOfSteps; }; ////////////////////////////////////////////////// // // LLPtrSkipMap implementation // // template inline LLPtrSkipMap::LLPtrSkipMap() : 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); } template inline LLPtrSkipMap::LLPtrSkipMap(insert_func insert_first, equals_func equals) : 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 inline LLPtrSkipMap::~LLPtrSkipMap() { removeAllData(); } template inline void LLPtrSkipMap::setInsertFirst(insert_func insert_first) { mInsertFirst = insert_first; } template inline void LLPtrSkipMap::setEquals(equals_func equals) { mEquals = equals; } template inline DATA_T &LLPtrSkipMap::addData(const INDEX_T &index, DATA_T datap) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *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->mIndex, index))) { current = temp; temp = *(current->mForward + level); } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { 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; // replace the existing data if a node is already there if ( (current) &&(mEquals(current->mIndex, index))) { current->mData = datap; return current->mData; } // now add the new node S32 newlevel; for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++) { if (ll_frand() < 0.5f) { break; } } LLPtrSkipMapNode *snode = new LLPtrSkipMapNode(index, datap); 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 snode->mData; } template inline DATA_T &LLPtrSkipMap::addData(const INDEX_T &index) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *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->mIndex, index))) { current = temp; temp = *(current->mForward + level); } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { 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; } LLPtrSkipMapNode *snode = new LLPtrSkipMapNode(index); 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 snode->mData; } template inline DATA_T &LLPtrSkipMap::getData(const INDEX_T &index) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *temp; mNumberOfSteps = 0; // 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->mIndex, index))) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } // we're now just in front of where we want to be . . . take one step forward current = *current->mForward; mNumberOfSteps++; if ( (current) &&(mEquals(current->mIndex, index))) { return current->mData; } // now add the new node S32 newlevel; for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++) { if (ll_frand() < 0.5f) break; } LLPtrSkipMapNode *snode = new LLPtrSkipMapNode(index); 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 snode->mData; } template inline BOOL LLPtrSkipMap::getInterval(const INDEX_T &index, INDEX_T &index_before, INDEX_T &index_after, DATA_T &data_before, DATA_T &data_after) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *temp; mNumberOfSteps = 0; // 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->mIndex, index))) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } BOOL both_found = TRUE; if (current != &mHead) { index_before = current->mIndex; data_before = current->mData; } else { data_before = 0; index_before = 0; both_found = FALSE; } // we're now just in front of where we want to be . . . take one step forward mNumberOfSteps++; current = *current->mForward; if (current) { data_after = current->mData; index_after = current->mIndex; } else { data_after = 0; index_after = 0; both_found = FALSE; } return both_found; } template inline DATA_T &LLPtrSkipMap::operator[](const INDEX_T &index) { return getData(index); } // If index present, returns data. // If index not present, adds and returns NULL. template inline DATA_T &LLPtrSkipMap::getData(const INDEX_T &index, BOOL &b_new_entry) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *temp; mNumberOfSteps = 0; // 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->mIndex, index))) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } // we're now just in front of where we want to be . . . take one step forward mNumberOfSteps++; current = *current->mForward; if ( (current) &&(mEquals(current->mIndex, index))) { return current->mData; } b_new_entry = TRUE; addData(index); return current->mData; } // Returns TRUE if data present in map. template inline BOOL LLPtrSkipMap::checkData(const INDEX_T &index) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *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->mIndex, index))) { current = temp; temp = *(current->mForward + level); } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { 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) { // Gets rid of some compiler ambiguity for the LLPointer<> templated class. if (current->mData) { return mEquals(current->mIndex, index); } } return FALSE; } // Returns TRUE if key is present in map. This is useful if you // are potentially storing NULL pointers in the map template inline BOOL LLPtrSkipMap::checkKey(const INDEX_T &index) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *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->mIndex, index))) { current = temp; temp = *(current->mForward + level); } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { 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->mIndex, index); } return FALSE; } // If there, returns the data. // If not, returns NULL. // Never adds entries to the map. template inline DATA_T LLPtrSkipMap::getIfThere(const INDEX_T &index) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *temp; mNumberOfSteps = 0; // 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->mIndex, index))) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { current = temp; temp = *(current->mForward + level); mNumberOfSteps++; } *(mUpdate + level) = current; } } // we're now just in front of where we want to be . . . take one step forward mNumberOfSteps++; current = *current->mForward; if (current) { if (mEquals(current->mIndex, index)) { return current->mData; } } // Avoid Linux compiler warning on returning NULL. return (DATA_T)0; } template inline INDEX_T LLPtrSkipMap::reverseLookup(const DATA_T datap) { LLPtrSkipMapNode *current = &mHead; while (current) { if (datap == current->mData) { return current->mIndex; } current = *current->mForward; } // not found! return NULL return INDEX_T(); } // returns number of items in the list template inline S32 LLPtrSkipMap::getLength() { U32 length = 0; LLPtrSkipMapNode* temp; for (temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0]) { length++; } return length; } template inline BOOL LLPtrSkipMap::removeData(const INDEX_T &index) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *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->mIndex, index))) { current = temp; temp = *(current->mForward + level); } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { 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->mIndex, index)) { // nope! return FALSE; } else { // do we need to fix current or currentop? if (current == mCurrentp) { mCurrentp = *current->mForward; } if (current == mCurrentOperatingp) { mCurrentOperatingp = *current->mForward; } // 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; } template inline BOOL LLPtrSkipMap::deleteData(const INDEX_T &index) { S32 level; LLPtrSkipMapNode *current = &mHead; LLPtrSkipMapNode *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->mIndex, index))) { current = temp; temp = *(current->mForward + level); } *(mUpdate + level) = current; } } else { for (level = mLevel - 1; level >= 0; level--) { temp = *(current->mForward + level); while ( (temp) &&(temp->mIndex < index)) { 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->mIndex, index)) { // nope! return FALSE; } else { // do we need to fix current or currentop? if (current == mCurrentp) { mCurrentp = *current->mForward; } if (current == mCurrentOperatingp) { mCurrentOperatingp = *current->mForward; } // 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 but do not delete data template void LLPtrSkipMap::removeAllData() { LLPtrSkipMapNode *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 inline void LLPtrSkipMap::deleteAllData() { LLPtrSkipMapNode *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 inline void LLPtrSkipMap::resetList() { mCurrentp = *(mHead.mForward); mCurrentOperatingp = *(mHead.mForward); } // return the data currently pointed to template inline DATA_T LLPtrSkipMap::getCurrentDataWithoutIncrement() { if (mCurrentOperatingp) { return mCurrentOperatingp->mData; } else { //return NULL; // causes warning return (DATA_T)0; // equivalent, but no warning } } // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp template inline DATA_T LLPtrSkipMap::getCurrentData() { if (mCurrentp) { mCurrentOperatingp = mCurrentp; mCurrentp = mCurrentp->mForward[0]; return mCurrentOperatingp->mData; } else { //return NULL; // causes warning return (DATA_T)0; // equivalent, but no warning } } // same as getCurrentData() but a more intuitive name for the operation template inline DATA_T LLPtrSkipMap::getNextData() { if (mCurrentp) { mCurrentOperatingp = mCurrentp; mCurrentp = mCurrentp->mForward[0]; return mCurrentOperatingp->mData; } else { //return NULL; // causes compile warning return (DATA_T)0; // equivalent, but removes warning } } template inline INDEX_T LLPtrSkipMap::getNextKey() { if (mCurrentp) { mCurrentOperatingp = mCurrentp; mCurrentp = mCurrentp->mForward[0]; return mCurrentOperatingp->mIndex; } else { return mHead.mIndex; } } // return the key currently pointed to template inline INDEX_T LLPtrSkipMap::getCurrentKeyWithoutIncrement() { if (mCurrentOperatingp) { return mCurrentOperatingp->mIndex; } else { //return NULL; // causes compile warning return (INDEX_T)0; // equivalent, but removes warning } } // remove the Node at mCurentOperatingp // leave mCurrentp and mCurentOperatingp on the next entry template inline void LLPtrSkipMap::removeCurrentData() { if (mCurrentOperatingp) { removeData(mCurrentOperatingp->mIndex); } } template inline void LLPtrSkipMap::deleteCurrentData() { if (mCurrentOperatingp) { deleteData(mCurrentOperatingp->mIndex); } } // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp template inline DATA_T LLPtrSkipMap::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_T)0; // equivalent, but removes warning } } template inline INDEX_T LLPtrSkipMap::getFirstKey() { mCurrentp = *(mHead.mForward); mCurrentOperatingp = *(mHead.mForward); if (mCurrentp) { mCurrentOperatingp = mCurrentp; mCurrentp = mCurrentp->mForward[0]; return mCurrentOperatingp->mIndex; } else { return mHead.mIndex; } } #endif