PageRenderTime 68ms CodeModel.GetById 11ms app.highlight 52ms RepoModel.GetById 1ms 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
  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
 27#ifndef LL_LLUUIDHASHMAP_H
 28#define LL_LLUUIDHASHMAP_H
 29
 30#include "stdtypes.h"
 31#include "llerror.h"
 32#include "lluuid.h"
 33
 34// UUID hash map
 35
 36	/*
 37	LLUUIDHashMap<uuid_pair, 32> foo(test_equals);
 38	LLUUIDHashMapIter<uuid_pair, 32> bar(&foo);
 39
 40	LLDynamicArray<LLUUID> source_ids;
 41	const S32 COUNT = 100000;
 42	S32 q;
 43	for (q = 0; q < COUNT; q++)
 44	{
 45		llinfos << "Creating" << llendl;
 46		LLUUID id;
 47		id.generate();
 48		//llinfos << q << ":" << id << llendl;
 49		uuid_pair pair;
 50		pair.mUUID = id;
 51		pair.mValue = q;
 52		foo.set(id, pair);
 53		source_ids.put(id);
 54		//ms_sleep(1);
 55	}
 56
 57	uuid_pair cur;
 58	llinfos << "Iterating" << llendl;
 59	for (cur = bar.first(); !bar.done(); cur = bar.next())
 60	{
 61		if (source_ids[cur.mValue] != cur.mUUID)
 62		{
 63			llerrs << "Incorrect value iterated!" << llendl;
 64		}
 65		//llinfos << cur.mValue << ":" << cur.mUUID << llendl;
 66		//ms_sleep(1);
 67	}
 68
 69	llinfos << "Finding" << llendl;
 70	for (q = 0; q < COUNT; q++)
 71	{
 72		cur = foo.get(source_ids[q]);
 73		if (source_ids[cur.mValue] != cur.mUUID)
 74		{
 75			llerrs << "Incorrect value found!" << llendl;
 76		}
 77		//llinfos << res.mValue << ":" << res.mUUID << llendl;
 78		//ms_sleep(1);
 79	}
 80	
 81	llinfos << "Removing" << llendl;
 82	for (q = 0; q < COUNT/2; q++)
 83	{
 84		if (!foo.remove(source_ids[q]))
 85		{
 86			llerrs << "Remove failed!" << llendl;
 87		}
 88		//ms_sleep(1);
 89	}
 90
 91	llinfos << "Iterating" << llendl;
 92	for (cur = bar.first(); !bar.done(); cur = bar.next())
 93	{
 94		if (source_ids[cur.mValue] != cur.mUUID)
 95		{
 96			llerrs << "Incorrect value found!" << llendl;
 97		}
 98		//llinfos << cur.mValue << ":" << cur.mUUID << llendl;
 99		//ms_sleep(1);
100	}
101	llinfos << "Done with UUID map test" << llendl;
102
103	return 0;
104	*/
105
106
107//
108// LLUUIDHashNode
109//
110
111template <class DATA, int SIZE>
112class LLUUIDHashNode
113{
114public:
115	LLUUIDHashNode();
116
117public:
118	S32 mCount;
119	U8	mKey[SIZE];
120	DATA mData[SIZE];
121	LLUUIDHashNode<DATA, SIZE> *mNextNodep;
122};
123
124
125//
126// LLUUIDHashNode implementation
127//
128template <class DATA, int SIZE>
129LLUUIDHashNode<DATA, SIZE>::LLUUIDHashNode()
130{
131	mCount = 0;
132	mNextNodep = NULL;
133}
134
135
136template <class DATA_TYPE, int SIZE>
137class LLUUIDHashMap
138{
139public:
140	// basic constructor including sorter
141	LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
142				  const DATA_TYPE &null_data);
143	~LLUUIDHashMap();
144
145	inline DATA_TYPE &get(const LLUUID &uuid);
146	inline BOOL check(const LLUUID &uuid) const;
147	inline DATA_TYPE &set(const LLUUID &uuid, const DATA_TYPE &type);
148	inline BOOL remove(const LLUUID &uuid);
149	void removeAll();
150
151	inline S32 getLength() const; // Warning, NOT O(1!)
152public:
153	BOOL (*mEquals)(const LLUUID &uuid, const DATA_TYPE &data);
154	LLUUIDHashNode<DATA_TYPE, SIZE> mNodes[256];
155
156	S32 mIterCount;
157protected:
158	DATA_TYPE mNull;
159};
160
161
162//
163// LLUUIDHashMap implementation
164//
165
166template <class DATA_TYPE, int SIZE>
167LLUUIDHashMap<DATA_TYPE, SIZE>::LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
168											  const DATA_TYPE &null_data)
169:	mEquals(equals),
170	mIterCount(0),
171	mNull(null_data)
172{ }
173
174template <class DATA_TYPE, int SIZE>
175LLUUIDHashMap<DATA_TYPE, SIZE>::~LLUUIDHashMap()
176{
177	removeAll();
178}
179
180template <class DATA_TYPE, int SIZE>
181void LLUUIDHashMap<DATA_TYPE, SIZE>::removeAll()
182{
183	S32 bin;
184	for (bin = 0; bin < 256; bin++)
185	{
186		LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
187
188		BOOL first = TRUE;
189		while (nodep)
190		{
191			S32 i;
192			const S32 count = nodep->mCount;
193
194			// Iterate through all members of this node
195			for (i = 0; i < count; i++)
196			{
197				nodep->mData[i] = mNull;
198			}
199
200			nodep->mCount = 0;
201			// Done with all objects in this node, go to the next.
202
203			LLUUIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
204			nodep = nodep->mNextNodep;
205
206			// Delete the node if it's not the first node
207			if (first)
208			{
209				first = FALSE;
210				curp->mNextNodep = NULL;
211			}
212			else
213			{
214				delete curp;
215			}
216		}
217	}
218}
219
220template <class DATA_TYPE, int SIZE>
221inline S32 LLUUIDHashMap<DATA_TYPE, SIZE>::getLength() const
222{
223	S32 count = 0;
224	S32 bin;
225	for (bin = 0; bin < 256; bin++)
226	{
227		LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = (LLUUIDHashNode<DATA_TYPE, SIZE>*) &mNodes[bin];
228		while (nodep)
229		{
230			count += nodep->mCount;
231			nodep = nodep->mNextNodep;
232		}
233	}
234	return count;
235}
236
237template <class DATA_TYPE, int SIZE>
238inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::get(const LLUUID &uuid)
239{
240	LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
241
242	// Grab the second byte of the UUID, which is the key for the node data
243	const S32 second_byte = uuid.mData[1];
244	while (nodep)
245	{
246		S32 i;
247		const S32 count = nodep->mCount;
248
249		// Iterate through all members of this node
250		for (i = 0; i < count; i++)
251		{
252			if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
253			{
254				// The second byte matched, and our equality test passed.
255				// We found it.
256				return nodep->mData[i];
257			}
258		}
259
260		// Done with all objects in this node, go to the next.
261		nodep = nodep->mNextNodep;
262	}
263	return mNull;
264}
265
266
267template <class DATA_TYPE, int SIZE>
268inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::check(const LLUUID &uuid) const
269{
270	const LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
271
272	// Grab the second byte of the UUID, which is the key for the node data
273	const S32 second_byte = uuid.mData[1];
274	while (nodep)
275	{
276		S32 i;
277		const S32 count = nodep->mCount;
278
279		// Iterate through all members of this node
280		for (i = 0; i < count; i++)
281		{
282			if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
283			{
284				// The second byte matched, and our equality test passed.
285				// We found it.
286				return TRUE;
287			}
288		}
289
290		// Done with all objects in this node, go to the next.
291		nodep = nodep->mNextNodep;
292	}
293
294	// Didn't find anything
295	return FALSE;
296}
297
298
299template <class DATA_TYPE, int SIZE>
300inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::set(const LLUUID &uuid, const DATA_TYPE &data)
301{
302	// Set is just like a normal find, except that if we find a match
303	// we replace it with the input value.
304	// If we don't find a match, we append to the end of the list.
305
306	LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
307
308	const S32 second_byte = uuid.mData[1];
309	while (1)
310	{
311		const S32 count = nodep->mCount;
312
313		S32 i;
314		for (i = 0; i < count; i++)
315		{
316			if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
317			{
318				// We found a match for this key, replace the data with
319				// the incoming data.
320				nodep->mData[i] = data;
321				return nodep->mData[i];
322			}
323		}
324		if (!nodep->mNextNodep)
325		{
326			// We've iterated through all of the keys without finding a match
327			if (i < SIZE)
328			{
329				// There's still some space on this node, append
330				// the key and data to it.
331				nodep->mKey[i] = second_byte;
332				nodep->mData[i] = data;
333				nodep->mCount++;
334
335				return nodep->mData[i];
336			}
337			else
338			{
339				// This node is full, append a new node to the end.
340				nodep->mNextNodep = new LLUUIDHashNode<DATA_TYPE, SIZE>;
341				nodep->mNextNodep->mKey[0] = second_byte;
342				nodep->mNextNodep->mData[0] = data;
343				nodep->mNextNodep->mCount = 1;
344
345				return nodep->mNextNodep->mData[0];
346			}
347		}
348
349		// No match on this node, go to the next
350		nodep = nodep->mNextNodep;
351	}
352}
353
354
355template <class DATA_TYPE, int SIZE>
356inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::remove(const LLUUID &uuid)
357{
358	if (mIterCount)
359	{
360		// We don't allow remove when we're iterating, it's bad karma!
361		llerrs << "Attempted remove while an outstanding iterator in LLUUIDHashMap!" << llendl;
362	}
363	// Remove is the trickiest operation.
364	// What we want to do is swap the last element of the last
365	// node if we find the one that we want to remove, but we have
366	// to deal with deleting the node from the tail if it's empty, but
367	// NOT if it's the only node left.
368
369	LLUUIDHashNode<DATA_TYPE, SIZE> *nodep = &mNodes[uuid.mData[0]];
370
371	// Not empty, we need to search through the nodes
372	const S32 second_byte = uuid.mData[1];
373
374	// A modification of the standard search algorithm.
375	while (nodep)
376	{
377		const S32 count = nodep->mCount;
378
379		S32 i;
380		for (i = 0; i < count; i++)
381		{
382			if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
383			{
384				// We found the node that we want to remove.
385				// Find the last (and next-to-last) node, and the index of the last
386				// element.  We could conceviably start from the node we're on,
387				// but that makes it more complicated, this is easier.
388
389				LLUUIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[uuid.mData[0]];
390				LLUUIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
391
392				// Find the last and next-to-last
393				while (lastp->mNextNodep)
394				{
395					prevp = lastp;
396					lastp = lastp->mNextNodep;
397				}
398
399				// First, swap in the last to the current location.
400				nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
401				nodep->mData[i] = lastp->mData[lastp->mCount - 1];
402
403				// Now, we delete the entry
404				lastp->mCount--;
405				lastp->mData[lastp->mCount] = mNull;
406
407				if (!lastp->mCount)
408				{
409					// We deleted the last element!
410					if (lastp != &mNodes[uuid.mData[0]])
411					{
412						// Only blitz the node if it's not the head
413						// Set the previous node to point to NULL, then
414						// blitz the empty last node
415						prevp->mNextNodep = NULL;
416						delete lastp;
417					}
418				}
419				return TRUE;
420			}
421		}
422
423		// Iterate to the next node, we've scanned all the entries in this one.
424		nodep = nodep->mNextNodep;
425	}
426	return FALSE;
427}
428
429
430//
431// LLUUIDHashMapIter
432//
433
434template <class DATA_TYPE, int SIZE>
435class LLUUIDHashMapIter
436{
437public:
438	LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
439	~LLUUIDHashMapIter();
440
441
442	inline void reset();
443	inline void first();
444	inline void next();
445	inline BOOL done() const;
446
447	DATA_TYPE& operator*() const
448	{
449		return mCurHashNodep->mData[mCurHashNodeKey];
450	}
451	DATA_TYPE* operator->() const
452	{
453		return &(operator*());
454	}
455
456protected:
457	LLUUIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
458	LLUUIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
459
460	S32 mCurHashMapNodeNum;
461	S32 mCurHashNodeKey;
462
463	DATA_TYPE mNull;
464};
465
466
467//
468// LLUUIDHashMapIter Implementation
469//
470template <class DATA_TYPE, int SIZE>
471LLUUIDHashMapIter<DATA_TYPE, SIZE>::LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
472{
473	mHashMapp = hash_mapp;
474	mCurHashNodep = NULL;
475	mCurHashMapNodeNum = 0;
476	mCurHashNodeKey = 0;
477}
478
479template <class DATA_TYPE, int SIZE>
480LLUUIDHashMapIter<DATA_TYPE, SIZE>::~LLUUIDHashMapIter()
481{
482	reset();
483}
484
485template <class DATA_TYPE, int SIZE>
486inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::reset()
487{
488	if (mCurHashNodep)
489	{
490		// We're partway through an iteration, we can clean up now
491		mHashMapp->mIterCount--;
492		mCurHashNodep = NULL;
493	}
494}
495
496template <class DATA_TYPE, int SIZE>
497inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::first()
498{
499	// Iterate through until we find the first non-empty node;
500	S32 i;
501	for (i = 0; i < 256; i++)
502	{
503		if (mHashMapp->mNodes[i].mCount)
504		{
505			if (!mCurHashNodep)
506			{
507				// Increment, since it's no longer safe for us to do a remove
508				mHashMapp->mIterCount++;
509			}
510
511			mCurHashNodep = &mHashMapp->mNodes[i];
512			mCurHashMapNodeNum = i;
513			mCurHashNodeKey = 0;
514			//return mCurHashNodep->mData[0];
515			return;
516		}
517	}
518
519	// Completely empty!
520	mCurHashNodep = NULL;
521	//return mNull;
522	return;
523}
524
525template <class DATA_TYPE, int SIZE>
526inline BOOL LLUUIDHashMapIter<DATA_TYPE, SIZE>::done() const
527{
528	return mCurHashNodep ? FALSE : TRUE;
529}
530
531template <class DATA_TYPE, int SIZE>
532inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::next()
533{
534	// No current entry, this iterator is done
535	if (!mCurHashNodep)
536	{
537		//return mNull;
538		return;
539	}
540
541	// Go to the next element
542	mCurHashNodeKey++;
543	if (mCurHashNodeKey < mCurHashNodep->mCount)
544	{
545		// We're not done with this node, return the current element
546		//return mCurHashNodep->mData[mCurHashNodeKey];
547		return;
548	}
549
550	// Done with this node, move to the next
551	mCurHashNodep = mCurHashNodep->mNextNodep;
552	if (mCurHashNodep)
553	{
554		// Return the first element
555		mCurHashNodeKey = 0;
556		//return mCurHashNodep->mData[0];
557		return;
558	}
559
560	// Find the next non-empty node (keyed on the first byte)
561	mCurHashMapNodeNum++;
562
563	S32 i;
564	for (i = mCurHashMapNodeNum; i < 256; i++)
565	{
566		if (mHashMapp->mNodes[i].mCount)
567		{
568			// We found one that wasn't empty
569			mCurHashNodep = &mHashMapp->mNodes[i];
570			mCurHashMapNodeNum = i;
571			mCurHashNodeKey = 0;
572			//return mCurHashNodep->mData[0];
573			return;
574		}
575	}
576
577	// OK, we're done, nothing else to iterate
578	mCurHashNodep = NULL;
579	mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
580	//return mNull;
581}
582
583#endif // LL_LLUUIDHASHMAP_H