lluuidhashmap.h

Go to the documentation of this file.
00001 
00032 #ifndef LL_LLUUIDHASHMAP_H
00033 #define LL_LLUUIDHASHMAP_H
00034 
00035 #include "stdtypes.h"
00036 #include "llerror.h"
00037 #include "lluuid.h"
00038 
00039 // UUID hash map
00040 
00041         /*
00042         LLUUIDHashMap<uuid_pair, 32> foo(test_equals);
00043         LLUUIDHashMapIter<uuid_pair, 32> bar(&foo);
00044 
00045         LLDynamicArray<LLUUID> source_ids;
00046         const S32 COUNT = 100000;
00047         S32 q;
00048         for (q = 0; q < COUNT; q++)
00049         {
00050                 llinfos << "Creating" << llendl;
00051                 LLUUID id;
00052                 id.generate();
00053                 //llinfos << q << ":" << id << llendl;
00054                 uuid_pair pair;
00055                 pair.mUUID = id;
00056                 pair.mValue = q;
00057                 foo.set(id, pair);
00058                 source_ids.put(id);
00059                 //ms_sleep(1);
00060         }
00061 
00062         uuid_pair cur;
00063         llinfos << "Iterating" << llendl;
00064         for (cur = bar.first(); !bar.done(); cur = bar.next())
00065         {
00066                 if (source_ids[cur.mValue] != cur.mUUID)
00067                 {
00068                         llerrs << "Incorrect value iterated!" << llendl;
00069                 }
00070                 //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
00071                 //ms_sleep(1);
00072         }
00073 
00074         llinfos << "Finding" << llendl;
00075         for (q = 0; q < COUNT; q++)
00076         {
00077                 cur = foo.get(source_ids[q]);
00078                 if (source_ids[cur.mValue] != cur.mUUID)
00079                 {
00080                         llerrs << "Incorrect value found!" << llendl;
00081                 }
00082                 //llinfos << res.mValue << ":" << res.mUUID << llendl;
00083                 //ms_sleep(1);
00084         }
00085         
00086         llinfos << "Removing" << llendl;
00087         for (q = 0; q < COUNT/2; q++)
00088         {
00089                 if (!foo.remove(source_ids[q]))
00090                 {
00091                         llerrs << "Remove failed!" << llendl;
00092                 }
00093                 //ms_sleep(1);
00094         }
00095 
00096         llinfos << "Iterating" << llendl;
00097         for (cur = bar.first(); !bar.done(); cur = bar.next())
00098         {
00099                 if (source_ids[cur.mValue] != cur.mUUID)
00100                 {
00101                         llerrs << "Incorrect value found!" << llendl;
00102                 }
00103                 //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
00104                 //ms_sleep(1);
00105         }
00106         llinfos << "Done with UUID map test" << llendl;
00107 
00108         return 0;
00109         */
00110 
00111 
00112 //
00113 // LLUUIDHashNode
00114 //
00115 
00116 template <class DATA, int SIZE>
00117 class LLUUIDHashNode
00118 {
00119 public:
00120         LLUUIDHashNode();
00121 
00122 public:
00123         S32 mCount;
00124         U8      mKey[SIZE];
00125         DATA mData[SIZE];
00126         LLUUIDHashNode<DATA, SIZE> *mNextNodep;
00127 };
00128 
00129 
00130 //
00131 // LLUUIDHashNode implementation
00132 //
00133 template <class DATA, int SIZE>
00134 LLUUIDHashNode<DATA, SIZE>::LLUUIDHashNode()
00135 {
00136         mCount = 0;
00137         mNextNodep = NULL;
00138 }
00139 
00140 
00141 template <class DATA_TYPE, int SIZE>
00142 class LLUUIDHashMap
00143 {
00144 public:
00145         // basic constructor including sorter
00146         LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
00147                                   const DATA_TYPE &null_data);
00148         ~LLUUIDHashMap();
00149 
00150         inline DATA_TYPE &get(const LLUUID &uuid);
00151         inline BOOL check(const LLUUID &uuid) const;
00152         inline DATA_TYPE &set(const LLUUID &uuid, const DATA_TYPE &type);
00153         inline BOOL remove(const LLUUID &uuid);
00154         void removeAll();
00155 
00156         inline S32 getLength() const; // Warning, NOT O(1!)
00157 public:
00158         BOOL (*mEquals)(const LLUUID &uuid, const DATA_TYPE &data);
00159         LLUUIDHashNode<DATA_TYPE, SIZE> mNodes[256];
00160 
00161         S32 mIterCount;
00162 protected:
00163         DATA_TYPE mNull;
00164 };
00165 
00166 
00167 //
00168 // LLUUIDHashMap implementation
00169 //
00170 
00171 template <class DATA_TYPE, int SIZE>
00172 LLUUIDHashMap<DATA_TYPE, SIZE>::LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
00173                                                                                           const DATA_TYPE &null_data)
00174 :       mEquals(equals),
00175         mIterCount(0),
00176         mNull(null_data)
00177 { }
00178 
00179 template <class DATA_TYPE, int SIZE>
00180 LLUUIDHashMap<DATA_TYPE, SIZE>::~LLUUIDHashMap()
00181 {
00182         removeAll();
00183 }
00184 
00185 template <class DATA_TYPE, int SIZE>
00186 void LLUUIDHashMap<DATA_TYPE, SIZE>::removeAll()
00187 {
00188         S32 bin;
00189         for (bin = 0; bin < 256; bin++)
00190         {
00191                 LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
00192 
00193                 BOOL first = TRUE;
00194                 while (nodep)
00195                 {
00196                         S32 i;
00197                         const S32 count = nodep->mCount;
00198 
00199                         // Iterate through all members of this node
00200                         for (i = 0; i < count; i++)
00201                         {
00202                                 nodep->mData[i] = mNull;
00203                         }
00204 
00205                         nodep->mCount = 0;
00206                         // Done with all objects in this node, go to the next.
00207 
00208                         LLUUIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
00209                         nodep = nodep->mNextNodep;
00210 
00211                         // Delete the node if it's not the first node
00212                         if (first)
00213                         {
00214                                 first = FALSE;
00215                                 curp->mNextNodep = NULL;
00216                         }
00217                         else
00218                         {
00219                                 delete curp;
00220                         }
00221                 }
00222         }
00223 }
00224 
00225 template <class DATA_TYPE, int SIZE>
00226 inline S32 LLUUIDHashMap<DATA_TYPE, SIZE>::getLength() const
00227 {
00228         S32 count = 0;
00229         S32 bin;
00230         for (bin = 0; bin < 256; bin++)
00231         {
00232                 LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = (LLUUIDHashNode<DATA_TYPE, SIZE>*) &mNodes[bin];
00233                 while (nodep)
00234                 {
00235                         count += nodep->mCount;
00236                         nodep = nodep->mNextNodep;
00237                 }
00238         }
00239         return count;
00240 }
00241 
00242 template <class DATA_TYPE, int SIZE>
00243 inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::get(const LLUUID &uuid)
00244 {
00245         LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
00246 
00247         // Grab the second byte of the UUID, which is the key for the node data
00248         const S32 second_byte = uuid.mData[1];
00249         while (nodep)
00250         {
00251                 S32 i;
00252                 const S32 count = nodep->mCount;
00253 
00254                 // Iterate through all members of this node
00255                 for (i = 0; i < count; i++)
00256                 {
00257                         if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
00258                         {
00259                                 // The second byte matched, and our equality test passed.
00260                                 // We found it.
00261                                 return nodep->mData[i];
00262                         }
00263                 }
00264 
00265                 // Done with all objects in this node, go to the next.
00266                 nodep = nodep->mNextNodep;
00267         }
00268         return mNull;
00269 }
00270 
00271 
00272 template <class DATA_TYPE, int SIZE>
00273 inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::check(const LLUUID &uuid) const
00274 {
00275         const LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
00276 
00277         // Grab the second byte of the UUID, which is the key for the node data
00278         const S32 second_byte = uuid.mData[1];
00279         while (nodep)
00280         {
00281                 S32 i;
00282                 const S32 count = nodep->mCount;
00283 
00284                 // Iterate through all members of this node
00285                 for (i = 0; i < count; i++)
00286                 {
00287                         if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
00288                         {
00289                                 // The second byte matched, and our equality test passed.
00290                                 // We found it.
00291                                 return TRUE;
00292                         }
00293                 }
00294 
00295                 // Done with all objects in this node, go to the next.
00296                 nodep = nodep->mNextNodep;
00297         }
00298 
00299         // Didn't find anything
00300         return FALSE;
00301 }
00302 
00303 
00304 template <class DATA_TYPE, int SIZE>
00305 inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::set(const LLUUID &uuid, const DATA_TYPE &data)
00306 {
00307         // Set is just like a normal find, except that if we find a match
00308         // we replace it with the input value.
00309         // If we don't find a match, we append to the end of the list.
00310 
00311         LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
00312 
00313         const S32 second_byte = uuid.mData[1];
00314         while (1)
00315         {
00316                 const S32 count = nodep->mCount;
00317 
00318                 S32 i;
00319                 for (i = 0; i < count; i++)
00320                 {
00321                         if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
00322                         {
00323                                 // We found a match for this key, replace the data with
00324                                 // the incoming data.
00325                                 nodep->mData[i] = data;
00326                                 return nodep->mData[i];
00327                         }
00328                 }
00329                 if (!nodep->mNextNodep)
00330                 {
00331                         // We've iterated through all of the keys without finding a match
00332                         if (i < SIZE)
00333                         {
00334                                 // There's still some space on this node, append
00335                                 // the key and data to it.
00336                                 nodep->mKey[i] = second_byte;
00337                                 nodep->mData[i] = data;
00338                                 nodep->mCount++;
00339 
00340                                 return nodep->mData[i];
00341                         }
00342                         else
00343                         {
00344                                 // This node is full, append a new node to the end.
00345                                 nodep->mNextNodep = new LLUUIDHashNode<DATA_TYPE, SIZE>;
00346                                 nodep->mNextNodep->mKey[0] = second_byte;
00347                                 nodep->mNextNodep->mData[0] = data;
00348                                 nodep->mNextNodep->mCount = 1;
00349 
00350                                 return nodep->mNextNodep->mData[0];
00351                         }
00352                 }
00353 
00354                 // No match on this node, go to the next
00355                 nodep = nodep->mNextNodep;
00356         }
00357 }
00358 
00359 
00360 template <class DATA_TYPE, int SIZE>
00361 inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::remove(const LLUUID &uuid)
00362 {
00363         if (mIterCount)
00364         {
00365                 // We don't allow remove when we're iterating, it's bad karma!
00366                 llerrs << "Attempted remove while an outstanding iterator in LLUUIDHashMap!" << llendl;
00367         }
00368         // Remove is the trickiest operation.
00369         // What we want to do is swap the last element of the last
00370         // node if we find the one that we want to remove, but we have
00371         // to deal with deleting the node from the tail if it's empty, but
00372         // NOT if it's the only node left.
00373 
00374         LLUUIDHashNode<DATA_TYPE, SIZE> *nodep = &mNodes[uuid.mData[0]];
00375 
00376         // Not empty, we need to search through the nodes
00377         const S32 second_byte = uuid.mData[1];
00378 
00379         // A modification of the standard search algorithm.
00380         while (nodep)
00381         {
00382                 const S32 count = nodep->mCount;
00383 
00384                 S32 i;
00385                 for (i = 0; i < count; i++)
00386                 {
00387                         if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
00388                         {
00389                                 // We found the node that we want to remove.
00390                                 // Find the last (and next-to-last) node, and the index of the last
00391                                 // element.  We could conceviably start from the node we're on,
00392                                 // but that makes it more complicated, this is easier.
00393 
00394                                 LLUUIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[uuid.mData[0]];
00395                                 LLUUIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
00396 
00397                                 // Find the last and next-to-last
00398                                 while (lastp->mNextNodep)
00399                                 {
00400                                         prevp = lastp;
00401                                         lastp = lastp->mNextNodep;
00402                                 }
00403 
00404                                 // First, swap in the last to the current location.
00405                                 nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
00406                                 nodep->mData[i] = lastp->mData[lastp->mCount - 1];
00407 
00408                                 // Now, we delete the entry
00409                                 lastp->mCount--;
00410                                 lastp->mData[lastp->mCount] = mNull;
00411 
00412                                 if (!lastp->mCount)
00413                                 {
00414                                         // We deleted the last element!
00415                                         if (lastp != &mNodes[uuid.mData[0]])
00416                                         {
00417                                                 // Only blitz the node if it's not the head
00418                                                 // Set the previous node to point to NULL, then
00419                                                 // blitz the empty last node
00420                                                 prevp->mNextNodep = NULL;
00421                                                 delete lastp;
00422                                         }
00423                                 }
00424                                 return TRUE;
00425                         }
00426                 }
00427 
00428                 // Iterate to the next node, we've scanned all the entries in this one.
00429                 nodep = nodep->mNextNodep;
00430         }
00431         return FALSE;
00432 }
00433 
00434 
00435 //
00436 // LLUUIDHashMapIter
00437 //
00438 
00439 template <class DATA_TYPE, int SIZE>
00440 class LLUUIDHashMapIter
00441 {
00442 public:
00443         LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
00444         ~LLUUIDHashMapIter();
00445 
00446 
00447         inline void reset();
00448         inline void first();
00449         inline void next();
00450         inline BOOL done() const;
00451 
00452         DATA_TYPE& operator*() const
00453         {
00454                 return mCurHashNodep->mData[mCurHashNodeKey];
00455         }
00456         DATA_TYPE* operator->() const
00457         {
00458                 return &(operator*());
00459         }
00460 
00461 protected:
00462         LLUUIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
00463         LLUUIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
00464 
00465         S32 mCurHashMapNodeNum;
00466         S32 mCurHashNodeKey;
00467 
00468         DATA_TYPE mNull;
00469 };
00470 
00471 
00472 //
00473 // LLUUIDHashMapIter Implementation
00474 //
00475 template <class DATA_TYPE, int SIZE>
00476 LLUUIDHashMapIter<DATA_TYPE, SIZE>::LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
00477 {
00478         mHashMapp = hash_mapp;
00479         mCurHashNodep = NULL;
00480         mCurHashMapNodeNum = 0;
00481         mCurHashNodeKey = 0;
00482 }
00483 
00484 template <class DATA_TYPE, int SIZE>
00485 LLUUIDHashMapIter<DATA_TYPE, SIZE>::~LLUUIDHashMapIter()
00486 {
00487         reset();
00488 }
00489 
00490 template <class DATA_TYPE, int SIZE>
00491 inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::reset()
00492 {
00493         if (mCurHashNodep)
00494         {
00495                 // We're partway through an iteration, we can clean up now
00496                 mHashMapp->mIterCount--;
00497                 mCurHashNodep = NULL;
00498         }
00499 }
00500 
00501 template <class DATA_TYPE, int SIZE>
00502 inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::first()
00503 {
00504         // Iterate through until we find the first non-empty node;
00505         S32 i;
00506         for (i = 0; i < 256; i++)
00507         {
00508                 if (mHashMapp->mNodes[i].mCount)
00509                 {
00510                         if (!mCurHashNodep)
00511                         {
00512                                 // Increment, since it's no longer safe for us to do a remove
00513                                 mHashMapp->mIterCount++;
00514                         }
00515 
00516                         mCurHashNodep = &mHashMapp->mNodes[i];
00517                         mCurHashMapNodeNum = i;
00518                         mCurHashNodeKey = 0;
00519                         //return mCurHashNodep->mData[0];
00520                         return;
00521                 }
00522         }
00523 
00524         // Completely empty!
00525         mCurHashNodep = NULL;
00526         //return mNull;
00527         return;
00528 }
00529 
00530 template <class DATA_TYPE, int SIZE>
00531 inline BOOL LLUUIDHashMapIter<DATA_TYPE, SIZE>::done() const
00532 {
00533         return mCurHashNodep ? FALSE : TRUE;
00534 }
00535 
00536 template <class DATA_TYPE, int SIZE>
00537 inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::next()
00538 {
00539         // No current entry, this iterator is done
00540         if (!mCurHashNodep)
00541         {
00542                 //return mNull;
00543                 return;
00544         }
00545 
00546         // Go to the next element
00547         mCurHashNodeKey++;
00548         if (mCurHashNodeKey < mCurHashNodep->mCount)
00549         {
00550                 // We're not done with this node, return the current element
00551                 //return mCurHashNodep->mData[mCurHashNodeKey];
00552                 return;
00553         }
00554 
00555         // Done with this node, move to the next
00556         mCurHashNodep = mCurHashNodep->mNextNodep;
00557         if (mCurHashNodep)
00558         {
00559                 // Return the first element
00560                 mCurHashNodeKey = 0;
00561                 //return mCurHashNodep->mData[0];
00562                 return;
00563         }
00564 
00565         // Find the next non-empty node (keyed on the first byte)
00566         mCurHashMapNodeNum++;
00567 
00568         S32 i;
00569         for (i = mCurHashMapNodeNum; i < 256; i++)
00570         {
00571                 if (mHashMapp->mNodes[i].mCount)
00572                 {
00573                         // We found one that wasn't empty
00574                         mCurHashNodep = &mHashMapp->mNodes[i];
00575                         mCurHashMapNodeNum = i;
00576                         mCurHashNodeKey = 0;
00577                         //return mCurHashNodep->mData[0];
00578                         return;
00579                 }
00580         }
00581 
00582         // OK, we're done, nothing else to iterate
00583         mCurHashNodep = NULL;
00584         mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
00585         //return mNull;
00586 }
00587 
00588 #endif // LL_LLUUIDHASHMAP_H

Generated on Fri May 16 08:32:09 2008 for SecondLife by  doxygen 1.5.5