PageRenderTime 60ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llcommon/lluuidhashmap.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 583 lines | 329 code | 80 blank | 174 comment | 42 complexity | 53ed6389bb786d3165d137b38554f7f7 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lluuidhashmap.h
  3. * @brief A uuid based hash map.
  4. *
  5. * $LicenseInfo:firstyear=2003&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #ifndef LL_LLUUIDHASHMAP_H
  27. #define LL_LLUUIDHASHMAP_H
  28. #include "stdtypes.h"
  29. #include "llerror.h"
  30. #include "lluuid.h"
  31. // UUID hash map
  32. /*
  33. LLUUIDHashMap<uuid_pair, 32> foo(test_equals);
  34. LLUUIDHashMapIter<uuid_pair, 32> bar(&foo);
  35. LLDynamicArray<LLUUID> source_ids;
  36. const S32 COUNT = 100000;
  37. S32 q;
  38. for (q = 0; q < COUNT; q++)
  39. {
  40. llinfos << "Creating" << llendl;
  41. LLUUID id;
  42. id.generate();
  43. //llinfos << q << ":" << id << llendl;
  44. uuid_pair pair;
  45. pair.mUUID = id;
  46. pair.mValue = q;
  47. foo.set(id, pair);
  48. source_ids.put(id);
  49. //ms_sleep(1);
  50. }
  51. uuid_pair cur;
  52. llinfos << "Iterating" << llendl;
  53. for (cur = bar.first(); !bar.done(); cur = bar.next())
  54. {
  55. if (source_ids[cur.mValue] != cur.mUUID)
  56. {
  57. llerrs << "Incorrect value iterated!" << llendl;
  58. }
  59. //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
  60. //ms_sleep(1);
  61. }
  62. llinfos << "Finding" << llendl;
  63. for (q = 0; q < COUNT; q++)
  64. {
  65. cur = foo.get(source_ids[q]);
  66. if (source_ids[cur.mValue] != cur.mUUID)
  67. {
  68. llerrs << "Incorrect value found!" << llendl;
  69. }
  70. //llinfos << res.mValue << ":" << res.mUUID << llendl;
  71. //ms_sleep(1);
  72. }
  73. llinfos << "Removing" << llendl;
  74. for (q = 0; q < COUNT/2; q++)
  75. {
  76. if (!foo.remove(source_ids[q]))
  77. {
  78. llerrs << "Remove failed!" << llendl;
  79. }
  80. //ms_sleep(1);
  81. }
  82. llinfos << "Iterating" << llendl;
  83. for (cur = bar.first(); !bar.done(); cur = bar.next())
  84. {
  85. if (source_ids[cur.mValue] != cur.mUUID)
  86. {
  87. llerrs << "Incorrect value found!" << llendl;
  88. }
  89. //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
  90. //ms_sleep(1);
  91. }
  92. llinfos << "Done with UUID map test" << llendl;
  93. return 0;
  94. */
  95. //
  96. // LLUUIDHashNode
  97. //
  98. template <class DATA, int SIZE>
  99. class LLUUIDHashNode
  100. {
  101. public:
  102. LLUUIDHashNode();
  103. public:
  104. S32 mCount;
  105. U8 mKey[SIZE];
  106. DATA mData[SIZE];
  107. LLUUIDHashNode<DATA, SIZE> *mNextNodep;
  108. };
  109. //
  110. // LLUUIDHashNode implementation
  111. //
  112. template <class DATA, int SIZE>
  113. LLUUIDHashNode<DATA, SIZE>::LLUUIDHashNode()
  114. {
  115. mCount = 0;
  116. mNextNodep = NULL;
  117. }
  118. template <class DATA_TYPE, int SIZE>
  119. class LLUUIDHashMap
  120. {
  121. public:
  122. // basic constructor including sorter
  123. LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
  124. const DATA_TYPE &null_data);
  125. ~LLUUIDHashMap();
  126. inline DATA_TYPE &get(const LLUUID &uuid);
  127. inline BOOL check(const LLUUID &uuid) const;
  128. inline DATA_TYPE &set(const LLUUID &uuid, const DATA_TYPE &type);
  129. inline BOOL remove(const LLUUID &uuid);
  130. void removeAll();
  131. inline S32 getLength() const; // Warning, NOT O(1!)
  132. public:
  133. BOOL (*mEquals)(const LLUUID &uuid, const DATA_TYPE &data);
  134. LLUUIDHashNode<DATA_TYPE, SIZE> mNodes[256];
  135. S32 mIterCount;
  136. protected:
  137. DATA_TYPE mNull;
  138. };
  139. //
  140. // LLUUIDHashMap implementation
  141. //
  142. template <class DATA_TYPE, int SIZE>
  143. LLUUIDHashMap<DATA_TYPE, SIZE>::LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
  144. const DATA_TYPE &null_data)
  145. : mEquals(equals),
  146. mIterCount(0),
  147. mNull(null_data)
  148. { }
  149. template <class DATA_TYPE, int SIZE>
  150. LLUUIDHashMap<DATA_TYPE, SIZE>::~LLUUIDHashMap()
  151. {
  152. removeAll();
  153. }
  154. template <class DATA_TYPE, int SIZE>
  155. void LLUUIDHashMap<DATA_TYPE, SIZE>::removeAll()
  156. {
  157. S32 bin;
  158. for (bin = 0; bin < 256; bin++)
  159. {
  160. LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
  161. BOOL first = TRUE;
  162. while (nodep)
  163. {
  164. S32 i;
  165. const S32 count = nodep->mCount;
  166. // Iterate through all members of this node
  167. for (i = 0; i < count; i++)
  168. {
  169. nodep->mData[i] = mNull;
  170. }
  171. nodep->mCount = 0;
  172. // Done with all objects in this node, go to the next.
  173. LLUUIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
  174. nodep = nodep->mNextNodep;
  175. // Delete the node if it's not the first node
  176. if (first)
  177. {
  178. first = FALSE;
  179. curp->mNextNodep = NULL;
  180. }
  181. else
  182. {
  183. delete curp;
  184. }
  185. }
  186. }
  187. }
  188. template <class DATA_TYPE, int SIZE>
  189. inline S32 LLUUIDHashMap<DATA_TYPE, SIZE>::getLength() const
  190. {
  191. S32 count = 0;
  192. S32 bin;
  193. for (bin = 0; bin < 256; bin++)
  194. {
  195. LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = (LLUUIDHashNode<DATA_TYPE, SIZE>*) &mNodes[bin];
  196. while (nodep)
  197. {
  198. count += nodep->mCount;
  199. nodep = nodep->mNextNodep;
  200. }
  201. }
  202. return count;
  203. }
  204. template <class DATA_TYPE, int SIZE>
  205. inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::get(const LLUUID &uuid)
  206. {
  207. LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
  208. // Grab the second byte of the UUID, which is the key for the node data
  209. const S32 second_byte = uuid.mData[1];
  210. while (nodep)
  211. {
  212. S32 i;
  213. const S32 count = nodep->mCount;
  214. // Iterate through all members of this node
  215. for (i = 0; i < count; i++)
  216. {
  217. if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
  218. {
  219. // The second byte matched, and our equality test passed.
  220. // We found it.
  221. return nodep->mData[i];
  222. }
  223. }
  224. // Done with all objects in this node, go to the next.
  225. nodep = nodep->mNextNodep;
  226. }
  227. return mNull;
  228. }
  229. template <class DATA_TYPE, int SIZE>
  230. inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::check(const LLUUID &uuid) const
  231. {
  232. const LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
  233. // Grab the second byte of the UUID, which is the key for the node data
  234. const S32 second_byte = uuid.mData[1];
  235. while (nodep)
  236. {
  237. S32 i;
  238. const S32 count = nodep->mCount;
  239. // Iterate through all members of this node
  240. for (i = 0; i < count; i++)
  241. {
  242. if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
  243. {
  244. // The second byte matched, and our equality test passed.
  245. // We found it.
  246. return TRUE;
  247. }
  248. }
  249. // Done with all objects in this node, go to the next.
  250. nodep = nodep->mNextNodep;
  251. }
  252. // Didn't find anything
  253. return FALSE;
  254. }
  255. template <class DATA_TYPE, int SIZE>
  256. inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::set(const LLUUID &uuid, const DATA_TYPE &data)
  257. {
  258. // Set is just like a normal find, except that if we find a match
  259. // we replace it with the input value.
  260. // If we don't find a match, we append to the end of the list.
  261. LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
  262. const S32 second_byte = uuid.mData[1];
  263. while (1)
  264. {
  265. const S32 count = nodep->mCount;
  266. S32 i;
  267. for (i = 0; i < count; i++)
  268. {
  269. if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
  270. {
  271. // We found a match for this key, replace the data with
  272. // the incoming data.
  273. nodep->mData[i] = data;
  274. return nodep->mData[i];
  275. }
  276. }
  277. if (!nodep->mNextNodep)
  278. {
  279. // We've iterated through all of the keys without finding a match
  280. if (i < SIZE)
  281. {
  282. // There's still some space on this node, append
  283. // the key and data to it.
  284. nodep->mKey[i] = second_byte;
  285. nodep->mData[i] = data;
  286. nodep->mCount++;
  287. return nodep->mData[i];
  288. }
  289. else
  290. {
  291. // This node is full, append a new node to the end.
  292. nodep->mNextNodep = new LLUUIDHashNode<DATA_TYPE, SIZE>;
  293. nodep->mNextNodep->mKey[0] = second_byte;
  294. nodep->mNextNodep->mData[0] = data;
  295. nodep->mNextNodep->mCount = 1;
  296. return nodep->mNextNodep->mData[0];
  297. }
  298. }
  299. // No match on this node, go to the next
  300. nodep = nodep->mNextNodep;
  301. }
  302. }
  303. template <class DATA_TYPE, int SIZE>
  304. inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::remove(const LLUUID &uuid)
  305. {
  306. if (mIterCount)
  307. {
  308. // We don't allow remove when we're iterating, it's bad karma!
  309. llerrs << "Attempted remove while an outstanding iterator in LLUUIDHashMap!" << llendl;
  310. }
  311. // Remove is the trickiest operation.
  312. // What we want to do is swap the last element of the last
  313. // node if we find the one that we want to remove, but we have
  314. // to deal with deleting the node from the tail if it's empty, but
  315. // NOT if it's the only node left.
  316. LLUUIDHashNode<DATA_TYPE, SIZE> *nodep = &mNodes[uuid.mData[0]];
  317. // Not empty, we need to search through the nodes
  318. const S32 second_byte = uuid.mData[1];
  319. // A modification of the standard search algorithm.
  320. while (nodep)
  321. {
  322. const S32 count = nodep->mCount;
  323. S32 i;
  324. for (i = 0; i < count; i++)
  325. {
  326. if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
  327. {
  328. // We found the node that we want to remove.
  329. // Find the last (and next-to-last) node, and the index of the last
  330. // element. We could conceviably start from the node we're on,
  331. // but that makes it more complicated, this is easier.
  332. LLUUIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[uuid.mData[0]];
  333. LLUUIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
  334. // Find the last and next-to-last
  335. while (lastp->mNextNodep)
  336. {
  337. prevp = lastp;
  338. lastp = lastp->mNextNodep;
  339. }
  340. // First, swap in the last to the current location.
  341. nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
  342. nodep->mData[i] = lastp->mData[lastp->mCount - 1];
  343. // Now, we delete the entry
  344. lastp->mCount--;
  345. lastp->mData[lastp->mCount] = mNull;
  346. if (!lastp->mCount)
  347. {
  348. // We deleted the last element!
  349. if (lastp != &mNodes[uuid.mData[0]])
  350. {
  351. // Only blitz the node if it's not the head
  352. // Set the previous node to point to NULL, then
  353. // blitz the empty last node
  354. prevp->mNextNodep = NULL;
  355. delete lastp;
  356. }
  357. }
  358. return TRUE;
  359. }
  360. }
  361. // Iterate to the next node, we've scanned all the entries in this one.
  362. nodep = nodep->mNextNodep;
  363. }
  364. return FALSE;
  365. }
  366. //
  367. // LLUUIDHashMapIter
  368. //
  369. template <class DATA_TYPE, int SIZE>
  370. class LLUUIDHashMapIter
  371. {
  372. public:
  373. LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
  374. ~LLUUIDHashMapIter();
  375. inline void reset();
  376. inline void first();
  377. inline void next();
  378. inline BOOL done() const;
  379. DATA_TYPE& operator*() const
  380. {
  381. return mCurHashNodep->mData[mCurHashNodeKey];
  382. }
  383. DATA_TYPE* operator->() const
  384. {
  385. return &(operator*());
  386. }
  387. protected:
  388. LLUUIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
  389. LLUUIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
  390. S32 mCurHashMapNodeNum;
  391. S32 mCurHashNodeKey;
  392. DATA_TYPE mNull;
  393. };
  394. //
  395. // LLUUIDHashMapIter Implementation
  396. //
  397. template <class DATA_TYPE, int SIZE>
  398. LLUUIDHashMapIter<DATA_TYPE, SIZE>::LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
  399. {
  400. mHashMapp = hash_mapp;
  401. mCurHashNodep = NULL;
  402. mCurHashMapNodeNum = 0;
  403. mCurHashNodeKey = 0;
  404. }
  405. template <class DATA_TYPE, int SIZE>
  406. LLUUIDHashMapIter<DATA_TYPE, SIZE>::~LLUUIDHashMapIter()
  407. {
  408. reset();
  409. }
  410. template <class DATA_TYPE, int SIZE>
  411. inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::reset()
  412. {
  413. if (mCurHashNodep)
  414. {
  415. // We're partway through an iteration, we can clean up now
  416. mHashMapp->mIterCount--;
  417. mCurHashNodep = NULL;
  418. }
  419. }
  420. template <class DATA_TYPE, int SIZE>
  421. inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::first()
  422. {
  423. // Iterate through until we find the first non-empty node;
  424. S32 i;
  425. for (i = 0; i < 256; i++)
  426. {
  427. if (mHashMapp->mNodes[i].mCount)
  428. {
  429. if (!mCurHashNodep)
  430. {
  431. // Increment, since it's no longer safe for us to do a remove
  432. mHashMapp->mIterCount++;
  433. }
  434. mCurHashNodep = &mHashMapp->mNodes[i];
  435. mCurHashMapNodeNum = i;
  436. mCurHashNodeKey = 0;
  437. //return mCurHashNodep->mData[0];
  438. return;
  439. }
  440. }
  441. // Completely empty!
  442. mCurHashNodep = NULL;
  443. //return mNull;
  444. return;
  445. }
  446. template <class DATA_TYPE, int SIZE>
  447. inline BOOL LLUUIDHashMapIter<DATA_TYPE, SIZE>::done() const
  448. {
  449. return mCurHashNodep ? FALSE : TRUE;
  450. }
  451. template <class DATA_TYPE, int SIZE>
  452. inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::next()
  453. {
  454. // No current entry, this iterator is done
  455. if (!mCurHashNodep)
  456. {
  457. //return mNull;
  458. return;
  459. }
  460. // Go to the next element
  461. mCurHashNodeKey++;
  462. if (mCurHashNodeKey < mCurHashNodep->mCount)
  463. {
  464. // We're not done with this node, return the current element
  465. //return mCurHashNodep->mData[mCurHashNodeKey];
  466. return;
  467. }
  468. // Done with this node, move to the next
  469. mCurHashNodep = mCurHashNodep->mNextNodep;
  470. if (mCurHashNodep)
  471. {
  472. // Return the first element
  473. mCurHashNodeKey = 0;
  474. //return mCurHashNodep->mData[0];
  475. return;
  476. }
  477. // Find the next non-empty node (keyed on the first byte)
  478. mCurHashMapNodeNum++;
  479. S32 i;
  480. for (i = mCurHashMapNodeNum; i < 256; i++)
  481. {
  482. if (mHashMapp->mNodes[i].mCount)
  483. {
  484. // We found one that wasn't empty
  485. mCurHashNodep = &mHashMapp->mNodes[i];
  486. mCurHashMapNodeNum = i;
  487. mCurHashNodeKey = 0;
  488. //return mCurHashNodep->mData[0];
  489. return;
  490. }
  491. }
  492. // OK, we're done, nothing else to iterate
  493. mCurHashNodep = NULL;
  494. mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
  495. //return mNull;
  496. }
  497. #endif // LL_LLUUIDHASHMAP_H