1 #ifndef GIM_HASH_TABLE_H_INCLUDED
2 #define GIM_HASH_TABLE_H_INCLUDED
3 /*! \file gim_trimesh_data.h
4 \author Francisco Leon Najera
5 */
6 /*
7 -----------------------------------------------------------------------------
8 This source file is part of GIMPACT Library.
9 
10 For the latest info, see http://gimpact.sourceforge.net/
11 
12 Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371.
13 email: projectileman@yahoo.com
14 
15  This library is free software; you can redistribute it and/or
16  modify it under the terms of EITHER:
17    (1) The GNU Lesser General Public License as published by the Free
18        Software Foundation; either version 2.1 of the License, or (at
19        your option) any later version. The text of the GNU Lesser
20        General Public License is included with this library in the
21        file GIMPACT-LICENSE-LGPL.TXT.
22    (2) The BSD-style license that is included with this library in
23        the file GIMPACT-LICENSE-BSD.TXT.
24    (3) The zlib/libpng license that is included with this library in
25        the file GIMPACT-LICENSE-ZLIB.TXT.
26 
27  This library is distributed in the hope that it will be useful,
28  but WITHOUT ANY WARRANTY; without even the implied warranty of
29  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
30  GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details.
31 
32 -----------------------------------------------------------------------------
33 */
34 
35 #include "gim_radixsort.h"
36 
37 #define GIM_INVALID_HASH 0xffffffff  //!< A very very high value
38 #define GIM_DEFAULT_HASH_TABLE_SIZE 380
39 #define GIM_DEFAULT_HASH_TABLE_NODE_SIZE 4
40 #define GIM_HASH_TABLE_GROW_FACTOR 2
41 
42 #define GIM_MIN_RADIX_SORT_SIZE 860  //!< calibrated on a PIII
43 
44 template <typename T>
45 struct GIM_HASH_TABLE_NODE
46 {
47 	GUINT m_key;
48 	T m_data;
GIM_HASH_TABLE_NODEGIM_HASH_TABLE_NODE49 	GIM_HASH_TABLE_NODE()
50 	{
51 	}
52 
GIM_HASH_TABLE_NODEGIM_HASH_TABLE_NODE53 	GIM_HASH_TABLE_NODE(const GIM_HASH_TABLE_NODE& value)
54 	{
55 		m_key = value.m_key;
56 		m_data = value.m_data;
57 	}
58 
GIM_HASH_TABLE_NODEGIM_HASH_TABLE_NODE59 	GIM_HASH_TABLE_NODE(GUINT key, const T& data)
60 	{
61 		m_key = key;
62 		m_data = data;
63 	}
64 
65 	bool operator<(const GIM_HASH_TABLE_NODE<T>& other) const
66 	{
67 		///inverse order, further objects are first
68 		if (m_key < other.m_key) return true;
69 		return false;
70 	}
71 
72 	bool operator>(const GIM_HASH_TABLE_NODE<T>& other) const
73 	{
74 		///inverse order, further objects are first
75 		if (m_key > other.m_key) return true;
76 		return false;
77 	}
78 
79 	bool operator==(const GIM_HASH_TABLE_NODE<T>& other) const
80 	{
81 		///inverse order, further objects are first
82 		if (m_key == other.m_key) return true;
83 		return false;
84 	}
85 };
86 
87 ///Macro for getting the key
88 class GIM_HASH_NODE_GET_KEY
89 {
90 public:
91 	template <class T>
operator()92 	inline GUINT operator()(const T& a)
93 	{
94 		return a.m_key;
95 	}
96 };
97 
98 ///Macro for comparing the key and the element
99 class GIM_HASH_NODE_CMP_KEY_MACRO
100 {
101 public:
102 	template <class T>
operator()103 	inline int operator()(const T& a, GUINT key)
104 	{
105 		return ((int)(a.m_key - key));
106 	}
107 };
108 
109 ///Macro for comparing Hash nodes
110 class GIM_HASH_NODE_CMP_MACRO
111 {
112 public:
113 	template <class T>
operator()114 	inline int operator()(const T& a, const T& b)
115 	{
116 		return ((int)(a.m_key - b.m_key));
117 	}
118 };
119 
120 //! Sorting for hash table
121 /*!
122 switch automatically between quicksort and radixsort
123 */
124 template <typename T>
gim_sort_hash_node_array(T * array,GUINT array_count)125 void gim_sort_hash_node_array(T* array, GUINT array_count)
126 {
127 	if (array_count < GIM_MIN_RADIX_SORT_SIZE)
128 	{
129 		gim_heap_sort(array, array_count, GIM_HASH_NODE_CMP_MACRO());
130 	}
131 	else
132 	{
133 		memcopy_elements_func cmpfunc;
134 		gim_radix_sort(array, array_count, GIM_HASH_NODE_GET_KEY(), cmpfunc);
135 	}
136 }
137 
138 // Note: assumes long is at least 32 bits.
139 #define GIM_NUM_PRIME 28
140 
141 static const GUINT gim_prime_list[GIM_NUM_PRIME] =
142 	{
143 		53ul, 97ul, 193ul, 389ul, 769ul,
144 		1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
145 		49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
146 		1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
147 		50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
148 		1610612741ul, 3221225473ul, 4294967291ul};
149 
gim_next_prime(GUINT number)150 inline GUINT gim_next_prime(GUINT number)
151 {
152 	//Find nearest upper prime
153 	GUINT result_ind = 0;
154 	gim_binary_search(gim_prime_list, 0, (GIM_NUM_PRIME - 2), number, result_ind);
155 
156 	// inv: result_ind < 28
157 	return gim_prime_list[result_ind];
158 }
159 
160 //! A compact hash table implementation
161 /*!
162 A memory aligned compact hash table that coud be treated as an array.
163 It could be a simple sorted array without the overhead of the hash key bucked, or could
164 be a formely hash table with an array of keys.
165 You can use switch_to_hashtable() and switch_to_sorted_array for saving space or increase speed.
166 </br>
167 
168 <ul>
169 <li> if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes.
170 When the array size reaches the size equivalent to 'min_hash_table_size', then it becomes a hash table by calling check_for_switching_to_hashtable.
171 <li> If node_size != 0, then this container becomes a hash table for ever
172 </ul>
173 
174 */
175 template <class T>
176 class gim_hash_table
177 {
178 protected:
179 	typedef GIM_HASH_TABLE_NODE<T> _node_type;
180 
181 	//!The nodes
182 	//array< _node_type, SuperAllocator<_node_type> > m_nodes;
183 	gim_array<_node_type> m_nodes;
184 	//SuperBufferedArray< _node_type > m_nodes;
185 	bool m_sorted;
186 
187 	///Hash table data management. The hash table has the indices to the corresponding m_nodes array
188 	GUINT* m_hash_table;  //!<
189 	GUINT m_table_size;   //!<
190 	GUINT m_node_size;    //!<
191 	GUINT m_min_hash_table_size;
192 
193 	//! Returns the cell index
_find_cell(GUINT hashkey)194 	inline GUINT _find_cell(GUINT hashkey)
195 	{
196 		_node_type* nodesptr = m_nodes.pointer();
197 		GUINT start_index = (hashkey % m_table_size) * m_node_size;
198 		GUINT end_index = start_index + m_node_size;
199 
200 		while (start_index < end_index)
201 		{
202 			GUINT value = m_hash_table[start_index];
203 			if (value != GIM_INVALID_HASH)
204 			{
205 				if (nodesptr[value].m_key == hashkey) return start_index;
206 			}
207 			start_index++;
208 		}
209 		return GIM_INVALID_HASH;
210 	}
211 
212 	//! Find the avaliable cell for the hashkey, and return an existing cell if it has the same hash key
_find_avaliable_cell(GUINT hashkey)213 	inline GUINT _find_avaliable_cell(GUINT hashkey)
214 	{
215 		_node_type* nodesptr = m_nodes.pointer();
216 		GUINT avaliable_index = GIM_INVALID_HASH;
217 		GUINT start_index = (hashkey % m_table_size) * m_node_size;
218 		GUINT end_index = start_index + m_node_size;
219 
220 		while (start_index < end_index)
221 		{
222 			GUINT value = m_hash_table[start_index];
223 			if (value == GIM_INVALID_HASH)
224 			{
225 				if (avaliable_index == GIM_INVALID_HASH)
226 				{
227 					avaliable_index = start_index;
228 				}
229 			}
230 			else if (nodesptr[value].m_key == hashkey)
231 			{
232 				return start_index;
233 			}
234 			start_index++;
235 		}
236 		return avaliable_index;
237 	}
238 
239 	//! reserves the memory for the hash table.
240 	/*!
241     \pre hash table must be empty
242     \post reserves the memory for the hash table, an initializes all elements to GIM_INVALID_HASH.
243     */
_reserve_table_memory(GUINT newtablesize)244 	inline void _reserve_table_memory(GUINT newtablesize)
245 	{
246 		if (newtablesize == 0) return;
247 		if (m_node_size == 0) return;
248 
249 		//Get a Prime size
250 
251 		m_table_size = gim_next_prime(newtablesize);
252 
253 		GUINT datasize = m_table_size * m_node_size;
254 		//Alloc the data buffer
255 		m_hash_table = (GUINT*)gim_alloc(datasize * sizeof(GUINT));
256 	}
257 
_invalidate_keys()258 	inline void _invalidate_keys()
259 	{
260 		GUINT datasize = m_table_size * m_node_size;
261 		for (GUINT i = 0; i < datasize; i++)
262 		{
263 			m_hash_table[i] = GIM_INVALID_HASH;  // invalidate keys
264 		}
265 	}
266 
267 	//! Clear all memory for the hash table
_clear_table_memory()268 	inline void _clear_table_memory()
269 	{
270 		if (m_hash_table == NULL) return;
271 		gim_free(m_hash_table);
272 		m_hash_table = NULL;
273 		m_table_size = 0;
274 	}
275 
276 	//! Invalidates the keys (Assigning GIM_INVALID_HASH to all) Reorders the hash keys
_rehash()277 	inline void _rehash()
278 	{
279 		_invalidate_keys();
280 
281 		_node_type* nodesptr = m_nodes.pointer();
282 		for (GUINT i = 0; i < (GUINT)m_nodes.size(); i++)
283 		{
284 			GUINT nodekey = nodesptr[i].m_key;
285 			if (nodekey != GIM_INVALID_HASH)
286 			{
287 				//Search for the avaliable cell in buffer
288 				GUINT index = _find_avaliable_cell(nodekey);
289 
290 				if (m_hash_table[index] != GIM_INVALID_HASH)
291 				{  //The new index is alreade used... discard this new incomming object, repeated key
292 					btAssert(m_hash_table[index] == nodekey);
293 					nodesptr[i].m_key = GIM_INVALID_HASH;
294 				}
295 				else
296 				{
297 					//;
298 					//Assign the value for alloc
299 					m_hash_table[index] = i;
300 				}
301 			}
302 		}
303 	}
304 
305 	//! Resize hash table indices
_resize_table(GUINT newsize)306 	inline void _resize_table(GUINT newsize)
307 	{
308 		//Clear memory
309 		_clear_table_memory();
310 		//Alloc the data
311 		_reserve_table_memory(newsize);
312 		//Invalidate keys and rehash
313 		_rehash();
314 	}
315 
316 	//! Destroy hash table memory
_destroy()317 	inline void _destroy()
318 	{
319 		if (m_hash_table == NULL) return;
320 		_clear_table_memory();
321 	}
322 
323 	//! Finds an avaliable hash table cell, and resizes the table if there isn't space
_assign_hash_table_cell(GUINT hashkey)324 	inline GUINT _assign_hash_table_cell(GUINT hashkey)
325 	{
326 		GUINT cell_index = _find_avaliable_cell(hashkey);
327 
328 		if (cell_index == GIM_INVALID_HASH)
329 		{
330 			//rehashing
331 			_resize_table(m_table_size + 1);
332 			GUINT cell_index = _find_avaliable_cell(hashkey);
333 			btAssert(cell_index != GIM_INVALID_HASH);
334 		}
335 		return cell_index;
336 	}
337 
338 	//! erase by index in hash table
_erase_by_index_hash_table(GUINT index)339 	inline bool _erase_by_index_hash_table(GUINT index)
340 	{
341 		if (index >= m_nodes.size()) return false;
342 		if (m_nodes[index].m_key != GIM_INVALID_HASH)
343 		{
344 			//Search for the avaliable cell in buffer
345 			GUINT cell_index = _find_cell(m_nodes[index].m_key);
346 
347 			btAssert(cell_index != GIM_INVALID_HASH);
348 			btAssert(m_hash_table[cell_index] == index);
349 
350 			m_hash_table[cell_index] = GIM_INVALID_HASH;
351 		}
352 
353 		return this->_erase_unsorted(index);
354 	}
355 
356 	//! erase by key in hash table
_erase_hash_table(GUINT hashkey)357 	inline bool _erase_hash_table(GUINT hashkey)
358 	{
359 		if (hashkey == GIM_INVALID_HASH) return false;
360 
361 		//Search for the avaliable cell in buffer
362 		GUINT cell_index = _find_cell(hashkey);
363 		if (cell_index == GIM_INVALID_HASH) return false;
364 
365 		GUINT index = m_hash_table[cell_index];
366 		m_hash_table[cell_index] = GIM_INVALID_HASH;
367 
368 		return this->_erase_unsorted(index);
369 	}
370 
371 	//! insert an element in hash table
372 	/*!
373     If the element exists, this won't insert the element
374     \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
375     If so, the element has been inserted at the last position of the array.
376     */
_insert_hash_table(GUINT hashkey,const T & value)377 	inline GUINT _insert_hash_table(GUINT hashkey, const T& value)
378 	{
379 		if (hashkey == GIM_INVALID_HASH)
380 		{
381 			//Insert anyway
382 			_insert_unsorted(hashkey, value);
383 			return GIM_INVALID_HASH;
384 		}
385 
386 		GUINT cell_index = _assign_hash_table_cell(hashkey);
387 
388 		GUINT value_key = m_hash_table[cell_index];
389 
390 		if (value_key != GIM_INVALID_HASH) return value_key;  // Not overrited
391 
392 		m_hash_table[cell_index] = m_nodes.size();
393 
394 		_insert_unsorted(hashkey, value);
395 		return GIM_INVALID_HASH;
396 	}
397 
398 	//! insert an element in hash table.
399 	/*!
400     If the element exists, this replaces the element.
401     \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
402     If so, the element has been inserted at the last position of the array.
403     */
_insert_hash_table_replace(GUINT hashkey,const T & value)404 	inline GUINT _insert_hash_table_replace(GUINT hashkey, const T& value)
405 	{
406 		if (hashkey == GIM_INVALID_HASH)
407 		{
408 			//Insert anyway
409 			_insert_unsorted(hashkey, value);
410 			return GIM_INVALID_HASH;
411 		}
412 
413 		GUINT cell_index = _assign_hash_table_cell(hashkey);
414 
415 		GUINT value_key = m_hash_table[cell_index];
416 
417 		if (value_key != GIM_INVALID_HASH)
418 		{  //replaces the existing
419 			m_nodes[value_key] = _node_type(hashkey, value);
420 			return value_key;  // index of the replaced element
421 		}
422 
423 		m_hash_table[cell_index] = m_nodes.size();
424 
425 		_insert_unsorted(hashkey, value);
426 		return GIM_INVALID_HASH;
427 	}
428 
429 	///Sorted array data management. The hash table has the indices to the corresponding m_nodes array
_erase_sorted(GUINT index)430 	inline bool _erase_sorted(GUINT index)
431 	{
432 		if (index >= (GUINT)m_nodes.size()) return false;
433 		m_nodes.erase_sorted(index);
434 		if (m_nodes.size() < 2) m_sorted = false;
435 		return true;
436 	}
437 
438 	//! faster, but unsorted
_erase_unsorted(GUINT index)439 	inline bool _erase_unsorted(GUINT index)
440 	{
441 		if (index >= m_nodes.size()) return false;
442 
443 		GUINT lastindex = m_nodes.size() - 1;
444 		if (index < lastindex && m_hash_table != 0)
445 		{
446 			GUINT hashkey = m_nodes[lastindex].m_key;
447 			if (hashkey != GIM_INVALID_HASH)
448 			{
449 				//update the new position of the last element
450 				GUINT cell_index = _find_cell(hashkey);
451 				btAssert(cell_index != GIM_INVALID_HASH);
452 				//new position of the last element which will be swaped
453 				m_hash_table[cell_index] = index;
454 			}
455 		}
456 		m_nodes.erase(index);
457 		m_sorted = false;
458 		return true;
459 	}
460 
461 	//! Insert in position ordered
462 	/*!
463     Also checks if it is needed to transform this container to a hash table, by calling check_for_switching_to_hashtable
464     */
_insert_in_pos(GUINT hashkey,const T & value,GUINT pos)465 	inline void _insert_in_pos(GUINT hashkey, const T& value, GUINT pos)
466 	{
467 		m_nodes.insert(_node_type(hashkey, value), pos);
468 		this->check_for_switching_to_hashtable();
469 	}
470 
471 	//! Insert an element in an ordered array
_insert_sorted(GUINT hashkey,const T & value)472 	inline GUINT _insert_sorted(GUINT hashkey, const T& value)
473 	{
474 		if (hashkey == GIM_INVALID_HASH || size() == 0)
475 		{
476 			m_nodes.push_back(_node_type(hashkey, value));
477 			return GIM_INVALID_HASH;
478 		}
479 		//Insert at last position
480 		//Sort element
481 
482 		GUINT result_ind = 0;
483 		GUINT last_index = m_nodes.size() - 1;
484 		_node_type* ptr = m_nodes.pointer();
485 
486 		bool found = gim_binary_search_ex(
487 			ptr, 0, last_index, result_ind, hashkey, GIM_HASH_NODE_CMP_KEY_MACRO());
488 
489 		//Insert before found index
490 		if (found)
491 		{
492 			return result_ind;
493 		}
494 		else
495 		{
496 			_insert_in_pos(hashkey, value, result_ind);
497 		}
498 		return GIM_INVALID_HASH;
499 	}
500 
_insert_sorted_replace(GUINT hashkey,const T & value)501 	inline GUINT _insert_sorted_replace(GUINT hashkey, const T& value)
502 	{
503 		if (hashkey == GIM_INVALID_HASH || size() == 0)
504 		{
505 			m_nodes.push_back(_node_type(hashkey, value));
506 			return GIM_INVALID_HASH;
507 		}
508 		//Insert at last position
509 		//Sort element
510 		GUINT result_ind;
511 		GUINT last_index = m_nodes.size() - 1;
512 		_node_type* ptr = m_nodes.pointer();
513 
514 		bool found = gim_binary_search_ex(
515 			ptr, 0, last_index, result_ind, hashkey, GIM_HASH_NODE_CMP_KEY_MACRO());
516 
517 		//Insert before found index
518 		if (found)
519 		{
520 			m_nodes[result_ind] = _node_type(hashkey, value);
521 		}
522 		else
523 		{
524 			_insert_in_pos(hashkey, value, result_ind);
525 		}
526 		return result_ind;
527 	}
528 
529 	//! Fast insertion in m_nodes array
_insert_unsorted(GUINT hashkey,const T & value)530 	inline GUINT _insert_unsorted(GUINT hashkey, const T& value)
531 	{
532 		m_nodes.push_back(_node_type(hashkey, value));
533 		m_sorted = false;
534 		return GIM_INVALID_HASH;
535 	}
536 
537 public:
538 	/*!
539         <li> if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes.
540         When the array size reaches the size equivalent to 'min_hash_table_size', then it becomes a hash table by calling check_for_switching_to_hashtable.
541         <li> If node_size != 0, then this container becomes a hash table for ever
542         </ul>
543     */
544 	gim_hash_table(GUINT reserve_size = GIM_DEFAULT_HASH_TABLE_SIZE,
545 				   GUINT node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE,
546 				   GUINT min_hash_table_size = GIM_INVALID_HASH)
547 	{
548 		m_hash_table = NULL;
549 		m_table_size = 0;
550 		m_sorted = false;
551 		m_node_size = node_size;
552 		m_min_hash_table_size = min_hash_table_size;
553 
554 		if (m_node_size != 0)
555 		{
556 			if (reserve_size != 0)
557 			{
558 				m_nodes.reserve(reserve_size);
559 				_reserve_table_memory(reserve_size);
560 				_invalidate_keys();
561 			}
562 			else
563 			{
564 				m_nodes.reserve(GIM_DEFAULT_HASH_TABLE_SIZE);
565 				_reserve_table_memory(GIM_DEFAULT_HASH_TABLE_SIZE);
566 				_invalidate_keys();
567 			}
568 		}
569 		else if (reserve_size != 0)
570 		{
571 			m_nodes.reserve(reserve_size);
572 		}
573 	}
574 
~gim_hash_table()575 	~gim_hash_table()
576 	{
577 		_destroy();
578 	}
579 
is_hash_table()580 	inline bool is_hash_table()
581 	{
582 		if (m_hash_table) return true;
583 		return false;
584 	}
585 
is_sorted()586 	inline bool is_sorted()
587 	{
588 		if (size() < 2) return true;
589 		return m_sorted;
590 	}
591 
sort()592 	bool sort()
593 	{
594 		if (is_sorted()) return true;
595 		if (m_nodes.size() < 2) return false;
596 
597 		_node_type* ptr = m_nodes.pointer();
598 		GUINT siz = m_nodes.size();
599 		gim_sort_hash_node_array(ptr, siz);
600 		m_sorted = true;
601 
602 		if (m_hash_table)
603 		{
604 			_rehash();
605 		}
606 		return true;
607 	}
608 
switch_to_hashtable()609 	bool switch_to_hashtable()
610 	{
611 		if (m_hash_table) return false;
612 		if (m_node_size == 0) m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
613 		if (m_nodes.size() < GIM_DEFAULT_HASH_TABLE_SIZE)
614 		{
615 			_resize_table(GIM_DEFAULT_HASH_TABLE_SIZE);
616 		}
617 		else
618 		{
619 			_resize_table(m_nodes.size() + 1);
620 		}
621 
622 		return true;
623 	}
624 
switch_to_sorted_array()625 	bool switch_to_sorted_array()
626 	{
627 		if (m_hash_table == NULL) return true;
628 		_clear_table_memory();
629 		return sort();
630 	}
631 
632 	//!If the container reaches the
check_for_switching_to_hashtable()633 	bool check_for_switching_to_hashtable()
634 	{
635 		if (this->m_hash_table) return true;
636 
637 		if (!(m_nodes.size() < m_min_hash_table_size))
638 		{
639 			if (m_node_size == 0)
640 			{
641 				m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
642 			}
643 
644 			_resize_table(m_nodes.size() + 1);
645 			return true;
646 		}
647 		return false;
648 	}
649 
set_sorted(bool value)650 	inline void set_sorted(bool value)
651 	{
652 		m_sorted = value;
653 	}
654 
655 	//! Retrieves the amount of keys.
size()656 	inline GUINT size() const
657 	{
658 		return m_nodes.size();
659 	}
660 
661 	//! Retrieves the hash key.
get_key(GUINT index)662 	inline GUINT get_key(GUINT index) const
663 	{
664 		return m_nodes[index].m_key;
665 	}
666 
667 	//! Retrieves the value by index
668 	/*!
669     */
get_value_by_index(GUINT index)670 	inline T* get_value_by_index(GUINT index)
671 	{
672 		return &m_nodes[index].m_data;
673 	}
674 
675 	inline const T& operator[](GUINT index) const
676 	{
677 		return m_nodes[index].m_data;
678 	}
679 
680 	inline T& operator[](GUINT index)
681 	{
682 		return m_nodes[index].m_data;
683 	}
684 
685 	//! Finds the index of the element with the key
686 	/*!
687     \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
688     If so, the element has been inserted at the last position of the array.
689     */
find(GUINT hashkey)690 	inline GUINT find(GUINT hashkey)
691 	{
692 		if (m_hash_table)
693 		{
694 			GUINT cell_index = _find_cell(hashkey);
695 			if (cell_index == GIM_INVALID_HASH) return GIM_INVALID_HASH;
696 			return m_hash_table[cell_index];
697 		}
698 		GUINT last_index = m_nodes.size();
699 		if (last_index < 2)
700 		{
701 			if (last_index == 0) return GIM_INVALID_HASH;
702 			if (m_nodes[0].m_key == hashkey) return 0;
703 			return GIM_INVALID_HASH;
704 		}
705 		else if (m_sorted)
706 		{
707 			//Binary search
708 			GUINT result_ind = 0;
709 			last_index--;
710 			_node_type* ptr = m_nodes.pointer();
711 
712 			bool found = gim_binary_search_ex(ptr, 0, last_index, result_ind, hashkey, GIM_HASH_NODE_CMP_KEY_MACRO());
713 
714 			if (found) return result_ind;
715 		}
716 		return GIM_INVALID_HASH;
717 	}
718 
719 	//! Retrieves the value associated with the index
720 	/*!
721     \return the found element, or null
722     */
get_value(GUINT hashkey)723 	inline T* get_value(GUINT hashkey)
724 	{
725 		GUINT index = find(hashkey);
726 		if (index == GIM_INVALID_HASH) return NULL;
727 		return &m_nodes[index].m_data;
728 	}
729 
730 	/*!
731     */
erase_by_index(GUINT index)732 	inline bool erase_by_index(GUINT index)
733 	{
734 		if (index > m_nodes.size()) return false;
735 
736 		if (m_hash_table == NULL)
737 		{
738 			if (is_sorted())
739 			{
740 				return this->_erase_sorted(index);
741 			}
742 			else
743 			{
744 				return this->_erase_unsorted(index);
745 			}
746 		}
747 		else
748 		{
749 			return this->_erase_by_index_hash_table(index);
750 		}
751 		return false;
752 	}
753 
erase_by_index_unsorted(GUINT index)754 	inline bool erase_by_index_unsorted(GUINT index)
755 	{
756 		if (index > m_nodes.size()) return false;
757 
758 		if (m_hash_table == NULL)
759 		{
760 			return this->_erase_unsorted(index);
761 		}
762 		else
763 		{
764 			return this->_erase_by_index_hash_table(index);
765 		}
766 		return false;
767 	}
768 
769 	/*!
770 
771     */
erase_by_key(GUINT hashkey)772 	inline bool erase_by_key(GUINT hashkey)
773 	{
774 		if (size() == 0) return false;
775 
776 		if (m_hash_table)
777 		{
778 			return this->_erase_hash_table(hashkey);
779 		}
780 		//Binary search
781 
782 		if (is_sorted() == false) return false;
783 
784 		GUINT result_ind = find(hashkey);
785 		if (result_ind != GIM_INVALID_HASH)
786 		{
787 			return this->_erase_sorted(result_ind);
788 		}
789 		return false;
790 	}
791 
clear()792 	void clear()
793 	{
794 		m_nodes.clear();
795 
796 		if (m_hash_table == NULL) return;
797 		GUINT datasize = m_table_size * m_node_size;
798 		//Initialize the hashkeys.
799 		GUINT i;
800 		for (i = 0; i < datasize; i++)
801 		{
802 			m_hash_table[i] = GIM_INVALID_HASH;  // invalidate keys
803 		}
804 		m_sorted = false;
805 	}
806 
807 	//! Insert an element into the hash
808 	/*!
809     \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position
810     of the existing element.
811     */
insert(GUINT hashkey,const T & element)812 	inline GUINT insert(GUINT hashkey, const T& element)
813 	{
814 		if (m_hash_table)
815 		{
816 			return this->_insert_hash_table(hashkey, element);
817 		}
818 		if (this->is_sorted())
819 		{
820 			return this->_insert_sorted(hashkey, element);
821 		}
822 		return this->_insert_unsorted(hashkey, element);
823 	}
824 
825 	//! Insert an element into the hash, and could overrite an existing object with the same hash.
826 	/*!
827     \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position
828     of the replaced element.
829     */
insert_override(GUINT hashkey,const T & element)830 	inline GUINT insert_override(GUINT hashkey, const T& element)
831 	{
832 		if (m_hash_table)
833 		{
834 			return this->_insert_hash_table_replace(hashkey, element);
835 		}
836 		if (this->is_sorted())
837 		{
838 			return this->_insert_sorted_replace(hashkey, element);
839 		}
840 		this->_insert_unsorted(hashkey, element);
841 		return m_nodes.size();
842 	}
843 
844 	//! Insert an element into the hash,But if this container is a sorted array, this inserts it unsorted
845 	/*!
846     */
insert_unsorted(GUINT hashkey,const T & element)847 	inline GUINT insert_unsorted(GUINT hashkey, const T& element)
848 	{
849 		if (m_hash_table)
850 		{
851 			return this->_insert_hash_table(hashkey, element);
852 		}
853 		return this->_insert_unsorted(hashkey, element);
854 	}
855 };
856 
857 #endif  // GIM_CONTAINERS_H_INCLUDED
858