diff options
Diffstat (limited to 'indra/llcommon/lllocalidhashmap.h')
-rw-r--r-- | indra/llcommon/lllocalidhashmap.h | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/indra/llcommon/lllocalidhashmap.h b/indra/llcommon/lllocalidhashmap.h new file mode 100644 index 0000000000..12f2b3f2d7 --- /dev/null +++ b/indra/llcommon/lllocalidhashmap.h @@ -0,0 +1,877 @@ +/** + * @file lllocalidhashmap.h + * @brief Map specialized for dealing with local ids + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLLOCALIDHASHMAP_H +#define LL_LLLOCALIDHASHMAP_H + +#include "stdtypes.h" +#include "llerror.h" + +const S32 MAX_ITERS = 4; +// LocalID hash map + +// +// LLLocalIDHashNode +// + +template <class DATA, int SIZE> +class LLLocalIDHashNode +{ +public: + LLLocalIDHashNode(); + +public: + S32 mCount; + U32 mKey[SIZE]; + DATA mData[SIZE]; + LLLocalIDHashNode<DATA, SIZE> *mNextNodep; +}; + + +// +// LLLocalIDHashNode implementation +// +template <class DATA, int SIZE> +LLLocalIDHashNode<DATA, SIZE>::LLLocalIDHashNode() +{ + mCount = 0; + mNextNodep = NULL; +} + +// +// LLLocalIDHashMapIter +// +template <class DATA_TYPE, int SIZE> +class LLLocalIDHashMap; + +template <class DATA_TYPE, int SIZE> +class LLLocalIDHashMapIter +{ +public: + LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp); + ~LLLocalIDHashMapIter(); + + void setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp); + inline void first(); + inline void next(); + inline DATA_TYPE& current(); // *NOTE: Deprecate? Phoenix 2005-04-15 + inline BOOL done() const; + inline S32 currentBin() const; + inline void setBin(S32 bin); + + DATA_TYPE& operator*() const + { + return mCurHashNodep->mData[mCurHashNodeKey]; + } + DATA_TYPE* operator->() const + { + return &(operator*()); + } + + LLLocalIDHashMap<DATA_TYPE, SIZE> *mHashMapp; + LLLocalIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep; + + S32 mCurHashMapNodeNum; + S32 mCurHashNodeKey; + + DATA_TYPE mNull; + + S32 mIterID; +}; + + + +template <class DATA_TYPE, int SIZE> +class LLLocalIDHashMap +{ +public: + friend class LLLocalIDHashMapIter<DATA_TYPE, SIZE>; + + LLLocalIDHashMap(); // DO NOT use this unless you explicitly setNull, or the data type constructs a "null" + // object by default + // basic constructor including sorter + LLLocalIDHashMap(const DATA_TYPE &null_data); + // Hack, this should really be a const ref, but I'm not doing it that way because the sim + // usually uses pointers. + ~LLLocalIDHashMap(); + + inline DATA_TYPE &get(const U32 local_id); + inline BOOL check(const U32 local_id) const; + inline DATA_TYPE &set(const U32 local_id, const DATA_TYPE data); + inline BOOL remove(const U32 local_id); + void removeAll(); + + void setNull(const DATA_TYPE data) { mNull = data; } + + inline S32 getLength() const; // Warning, NOT O(1!) + + void dumpIter(); + void dumpBin(U32 bin); + +protected: + // Only used by the iterator. + void addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter); + void removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter); + + // Remove the item and shift all items afterward down the list, + // fixing up iterators as we go. + BOOL removeWithShift(const U32 local_id); + +protected: + LLLocalIDHashNode<DATA_TYPE, SIZE> mNodes[256]; + + S32 mIterCount; + LLLocalIDHashMapIter<DATA_TYPE, SIZE> *mIters[MAX_ITERS]; + + DATA_TYPE mNull; +}; + + +// +// LLLocalIDHashMap implementation +// + +template <class DATA_TYPE, int SIZE> +LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap() +: mIterCount(0), + mNull() +{ + S32 i; + for (i = 0; i < MAX_ITERS; i++) + { + mIters[i] = NULL; + } +} + +template <class DATA_TYPE, int SIZE> +LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap(const DATA_TYPE &null_data) +: mIterCount(0), + mNull(null_data) +{ + S32 i; + for (i = 0; i < MAX_ITERS; i++) + { + mIters[i] = NULL; + } +} + +template <class DATA_TYPE, int SIZE> +LLLocalIDHashMap<DATA_TYPE, SIZE>::~LLLocalIDHashMap() +{ + S32 i; + for (i = 0; i < MAX_ITERS; i++) + { + if (mIters[i]) + { + mIters[i]->mHashMapp = NULL; + mIterCount--; + } + } + removeAll(); +} + +template <class DATA_TYPE, int SIZE> +void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeAll() +{ + S32 bin; + for (bin = 0; bin < 256; bin++) + { + LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin]; + + BOOL first = TRUE; + do // First node guaranteed to be there + { + 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. + + LLLocalIDHashNode<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; + } + } while (nodep); + } +} + +template <class DATA_TYPE, int SIZE> +void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpIter() +{ + std::cout << "Hash map with " << mIterCount << " iterators" << std::endl; + + std::cout << "Hash Map Iterators:" << std::endl; + S32 i; + for (i = 0; i < MAX_ITERS; i++) + { + if (mIters[i]) + { + llinfos << i << " " << mIters[i]->mCurHashNodep << " " << mIters[i]->mCurHashNodeKey << llendl; + } + else + { + llinfos << i << "null" << llendl; + } + } +} + +template <class DATA_TYPE, int SIZE> +void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpBin(U32 bin) +{ + std::cout << "Dump bin " << bin << std::endl; + + LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin]; + S32 node = 0; + do // First node guaranteed to be there. + { + std::cout << "Bin " << bin + << " node " << node + << " count " << nodep->mCount + << " contains " << std::flush; + + S32 i; + for (i = 0; i < nodep->mCount; i++) + { + std::cout << nodep->mData[i] << " " << std::flush; + } + + std::cout << std::endl; + + nodep = nodep->mNextNodep; + node++; + } while (nodep); +} + +template <class DATA_TYPE, int SIZE> +inline S32 LLLocalIDHashMap<DATA_TYPE, SIZE>::getLength() const +{ + S32 count = 0; + S32 bin; + for (bin = 0; bin < 256; bin++) + { + const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin]; + while (nodep) + { + count += nodep->mCount; + nodep = nodep->mNextNodep; + } + } + return count; +} + +template <class DATA_TYPE, int SIZE> +inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::get(const U32 local_id) +{ + LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff]; + + do // First node guaranteed to be there + { + S32 i; + const S32 count = nodep->mCount; + + // Iterate through all members of this node + for (i = 0; i < count; i++) + { + if (nodep->mKey[i] == local_id) + { + // We found it. + return nodep->mData[i]; + } + } + + // Done with all objects in this node, go to the next. + nodep = nodep->mNextNodep; + } while (nodep); + + return mNull; +} + + +template <class DATA_TYPE, int SIZE> +inline BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::check(const U32 local_id) const +{ + const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff]; + + do // First node guaranteed to be there + { + S32 i; + const S32 count = nodep->mCount; + + // Iterate through all members of this node + for (i = 0; i < count; i++) + { + if (nodep->mKey[i] == local_id) + { + // We found it. + return TRUE; + } + } + + // Done with all objects in this node, go to the next. + nodep = nodep->mNextNodep; + } while (nodep); + + // Didn't find anything + return FALSE; +} + + +template <class DATA_TYPE, int SIZE> +inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::set(const U32 local_id, 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. + + LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff]; + + while (1) + { + const S32 count = nodep->mCount; + + S32 i; + for (i = 0; i < count; i++) + { + if (nodep->mKey[i] == local_id) + { + // 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] = local_id; + 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 LLLocalIDHashNode<DATA_TYPE, SIZE>; + nodep->mNextNodep->mKey[0] = local_id; + 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 LLLocalIDHashMap<DATA_TYPE, SIZE>::remove(const U32 local_id) +{ + // 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. + + const S32 node_index = local_id & 0xff; + + LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index]; + + // A modification of the standard search algorithm. + do // First node guaranteed to be there + { + const S32 count = nodep->mCount; + + S32 i; + for (i = 0; i < count; i++) + { + if (nodep->mKey[i] == local_id) + { + // If we're removing the item currently pointed to by one + // or more iterators, we can just swap in the last item + // and back the iterator(s) up by one. + // Otherwise, we need to do a slow and safe shift of all + // items back to one position to fill the hole and fix up + // all iterators we find. + BOOL need_shift = FALSE; + S32 cur_iter; + if (mIterCount) + { + for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++) + { + if (mIters[cur_iter]) + { + // We only care if the hash map node is on the one + // that we're working on. If it's before, we've already + // traversed it, if it's after, changing the order doesn't + // matter. + if (mIters[cur_iter]->mCurHashMapNodeNum == node_index) + { + if ((mIters[cur_iter]->mCurHashNodep == nodep) + && (mIters[cur_iter]->mCurHashNodeKey == i)) + { + // it's on the one we're deleting, we'll + // fix the iterator quickly below. + } + else + { + // We're trying to remove an item on this + // iterator's chain that this + // iterator doesn't point to! We need to do + // the slow remove-and-shift-down case. + need_shift = TRUE; + } + } + } + } + } + + // Removing an item that isn't pointed to by all iterators + if (need_shift) + { + return removeWithShift(local_id); + } + + // Fix the iterators that point to this node/i pair, the + // one we're deleting + for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++) + { + if (mIters[cur_iter]) + { + // We only care if the hash map node is on the one + // that we're working on. If it's before, we've already + // traversed it, if it's after, changing the order doesn't + // matter. + if (mIters[cur_iter]->mCurHashMapNodeNum == node_index) + { + if ((mIters[cur_iter]->mCurHashNodep == nodep) + && (mIters[cur_iter]->mCurHashNodeKey == i)) + { + // We can handle the case where we're deleting + // the element we're on trivially (sort of). + if (nodep->mCount > 1) + { + // If we're not going to delete this node, + // it's OK. + mIters[cur_iter]->mCurHashNodeKey--; + } + else + { + // We're going to delete this node, because this + // is the last element on it. + + // Find the next node, and then back up one. + mIters[cur_iter]->next(); + mIters[cur_iter]->mCurHashNodeKey--; + } + } + } + } + } + + // 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. + + LLLocalIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[node_index]; + LLLocalIDHashNode<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[local_id & 0xff]) + { + // 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; + } while (nodep); + + return FALSE; +} + +template <class DATA_TYPE, int SIZE> +BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::removeWithShift(const U32 local_id) +{ + const S32 node_index = local_id & 0xFF; + LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index]; + LLLocalIDHashNode<DATA_TYPE, SIZE>* prevp = NULL; + BOOL found = FALSE; + + do // First node guaranteed to be there + { + const S32 count = nodep->mCount; + S32 i; + for (i = 0; i < count; i++) + { + if (nodep->mKey[i] == local_id) + { + // Found the item. Start shifting items from later + // in the list over this item. + found = TRUE; + } + + if (found) + { + // If there is an iterator on this node, we need to + // back it up. + S32 cur_iter; + for (cur_iter = 0; cur_iter <MAX_ITERS; cur_iter++) + { + LLLocalIDHashMapIter<DATA_TYPE, SIZE>* iter; + iter = mIters[cur_iter]; + // If an iterator is on this node,i pair, then back it up. + if (iter + && iter->mCurHashMapNodeNum == node_index + && iter->mCurHashNodep == nodep + && iter->mCurHashNodeKey == i) + { + if (i > 0) + { + // Don't need to move iterator nodep, since + // we're in the same node. + iter->mCurHashNodeKey--; + } + else if (prevp) + { + // need to go the previous node, last item + iter->mCurHashNodep = prevp; + iter->mCurHashNodeKey = prevp->mCount - 1; + } + else + { + // we're on the first item in the list, but + // need to go back anyhow. + + // BUG: If this deletion empties the list, + // iter->done() will be wrong until + // iter->next() is called. + iter->mCurHashNodeKey = -1; + } + } + } + + // Copy data from the next position into this position. + if (i < count-1) + { + // we're not on the last item in the node, + // so we can copy within the node + nodep->mKey[i] = nodep->mKey[i+1]; + nodep->mData[i] = nodep->mData[i+1]; + } + else if (nodep->mNextNodep) + { + // we're on the last item in the node, + // but there's a next node we can copy from + nodep->mKey[i] = nodep->mNextNodep->mKey[0]; + nodep->mData[i] = nodep->mNextNodep->mData[0]; + } + else + { + // We're on the last position in the list. + // No one to copy from. Replace with nothing. + nodep->mKey[i] = 0; + nodep->mData[i] = mNull; + } + } + } + + // Last node in chain, so delete the last node + if (found + && !nodep->mNextNodep) + { + // delete the last item off the last node + nodep->mCount--; + + if (nodep->mCount == 0) + { + // We deleted the last element! + if (nodep != &mNodes[node_index]) + { + // Always have a prevp if we're not the head. + llassert(prevp); + + // 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 nodep; + nodep = NULL; + } + } + + // Deleted last item in chain, so we're done. + return found; + } + + prevp = nodep; + nodep = nodep->mNextNodep; + } while (nodep); + + return found; +} + +template <class DATA_TYPE, int SIZE> +void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter) +{ + S32 i; + for (i = 0; i < MAX_ITERS; i++) + { + if (mIters[i] == iter) + { + mIters[i] = NULL; + mIterCount--; + return; + } + } + llerrs << "Iterator " << iter << " not found for removal in hash map!" << llendl; +} + +template <class DATA_TYPE, int SIZE> +void LLLocalIDHashMap<DATA_TYPE, SIZE>::addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter) +{ + S32 i; + for (i = 0; i < MAX_ITERS; i++) + { + if (mIters[i] == NULL) + { + mIters[i] = iter; + mIterCount++; + return; + } + } + llerrs << "More than " << MAX_ITERS << " iterating over a map simultaneously!" << llendl; +} + + + +// +// LLLocalIDHashMapIter Implementation +// +template <class DATA_TYPE, int SIZE> +LLLocalIDHashMapIter<DATA_TYPE, SIZE>::LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp) +{ + mHashMapp = NULL; + setMap(hash_mapp); +} + +template <class DATA_TYPE, int SIZE> +LLLocalIDHashMapIter<DATA_TYPE, SIZE>::~LLLocalIDHashMapIter() +{ + if (mHashMapp) + { + mHashMapp->removeIter(this); + } +} + +template <class DATA_TYPE, int SIZE> +void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp) +{ + if (mHashMapp) + { + mHashMapp->removeIter(this); + } + mHashMapp = hash_mapp; + if (mHashMapp) + { + mHashMapp->addIter(this); + } + + mCurHashNodep = NULL; + mCurHashMapNodeNum = -1; + mCurHashNodeKey = 0; +} + +template <class DATA_TYPE, int SIZE> +inline void LLLocalIDHashMapIter<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) + { + + 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 LLLocalIDHashMapIter<DATA_TYPE, SIZE>::done() const +{ + return mCurHashNodep ? FALSE : TRUE; +} + +template <class DATA_TYPE, int SIZE> +inline S32 LLLocalIDHashMapIter<DATA_TYPE, SIZE>::currentBin() const +{ + if ( (mCurHashMapNodeNum > 255) + ||(mCurHashMapNodeNum < 0)) + { + return 0; + } + else + { + return mCurHashMapNodeNum; + } +} + +template <class DATA_TYPE, int SIZE> +inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setBin(S32 bin) +{ + // Iterate through until we find the first non-empty node; + S32 i; + bin = llclamp(bin, 0, 255); + for (i = bin; i < 256; i++) + { + if (mHashMapp->mNodes[i].mCount) + { + + mCurHashNodep = &mHashMapp->mNodes[i]; + mCurHashMapNodeNum = i; + mCurHashNodeKey = 0; + return; + } + } + for (i = 0; i < bin; i++) + { + if (mHashMapp->mNodes[i].mCount) + { + + mCurHashNodep = &mHashMapp->mNodes[i]; + mCurHashMapNodeNum = i; + mCurHashNodeKey = 0; + return; + } + } + // Completely empty! + mCurHashNodep = NULL; +} + +template <class DATA_TYPE, int SIZE> +inline DATA_TYPE &LLLocalIDHashMapIter<DATA_TYPE, SIZE>::current() +{ + if (!mCurHashNodep) + { + return mNull; + } + return mCurHashNodep->mData[mCurHashNodeKey]; +} + +template <class DATA_TYPE, int SIZE> +inline void LLLocalIDHashMapIter<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; + return; +} + +#endif // LL_LLLOCALIDHASHMAP_H |