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
7 -----------------------------------------------------------------------------
8 This source file is part of GIMPACT Library.
10 For the latest info, see http://gimpact.sourceforge.net/
12 Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371.
13 email: projectileman@yahoo.com
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.
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.
32 -----------------------------------------------------------------------------
35 #include "gim_radixsort.h"
38 #define GIM_INVALID_HASH 0xffffffff //!< A very very high value
39 #define GIM_DEFAULT_HASH_TABLE_SIZE 380
40 #define GIM_DEFAULT_HASH_TABLE_NODE_SIZE 4
41 #define GIM_HASH_TABLE_GROW_FACTOR 2
43 #define GIM_MIN_RADIX_SORT_SIZE 860 //!< calibrated on a PIII
46 struct GIM_HASH_TABLE_NODE
54 GIM_HASH_TABLE_NODE(const GIM_HASH_TABLE_NODE & value)
57 m_data = value.m_data;
60 GIM_HASH_TABLE_NODE(GUINT key, const T & data)
66 bool operator <(const GIM_HASH_TABLE_NODE<T> & other) const
68 ///inverse order, further objects are first
69 if(m_key < other.m_key) return true;
73 bool operator >(const GIM_HASH_TABLE_NODE<T> & other) const
75 ///inverse order, further objects are first
76 if(m_key > other.m_key) return true;
80 bool operator ==(const GIM_HASH_TABLE_NODE<T> & other) const
82 ///inverse order, further objects are first
83 if(m_key == other.m_key) return true;
88 ///Macro for getting the key
89 class GIM_HASH_NODE_GET_KEY
93 inline GUINT operator()( const T& a)
101 ///Macro for comparing the key and the element
102 class GIM_HASH_NODE_CMP_KEY_MACRO
106 inline int operator() ( const T& a, GUINT key)
108 return ((int)(a.m_key - key));
112 ///Macro for comparing Hash nodes
113 class GIM_HASH_NODE_CMP_MACRO
117 inline int operator() ( const T& a, const T& b )
119 return ((int)(a.m_key - b.m_key));
127 //! Sorting for hash table
129 switch automatically between quicksort and radixsort
132 void gim_sort_hash_node_array(T * array, GUINT array_count)
134 if(array_count<GIM_MIN_RADIX_SORT_SIZE)
136 gim_heap_sort(array,array_count,GIM_HASH_NODE_CMP_MACRO());
140 memcopy_elements_func cmpfunc;
141 gim_radix_sort(array,array_count,GIM_HASH_NODE_GET_KEY(),cmpfunc);
150 // Note: assumes long is at least 32 bits.
151 #define GIM_NUM_PRIME 28
153 static const GUINT gim_prime_list[GIM_NUM_PRIME] =
155 53ul, 97ul, 193ul, 389ul, 769ul,
156 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
157 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
158 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
159 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
160 1610612741ul, 3221225473ul, 4294967291ul
163 inline GUINT gim_next_prime(GUINT number)
165 //Find nearest upper prime
166 GUINT result_ind = 0;
167 gim_binary_search(gim_prime_list,0,(GIM_NUM_PRIME-2),number,result_ind);
169 // inv: result_ind < 28
170 return gim_prime_list[result_ind];
175 //! A compact hash table implementation
177 A memory aligned compact hash table that coud be treated as an array.
178 It could be a simple sorted array without the overhead of the hash key bucked, or could
179 be a formely hash table with an array of keys.
180 You can use switch_to_hashtable() and switch_to_sorted_array for saving space or increase speed.
184 <li> if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes.
185 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.
186 <li> If node_size != 0, then this container becomes a hash table for ever
194 typedef GIM_HASH_TABLE_NODE<T> _node_type;
197 //array< _node_type, SuperAllocator<_node_type> > m_nodes;
198 gim_array< _node_type > m_nodes;
199 //SuperBufferedArray< _node_type > m_nodes;
202 ///Hash table data management. The hash table has the indices to the corresponding m_nodes array
203 GUINT * m_hash_table;//!<
204 GUINT m_table_size;//!<
205 GUINT m_node_size;//!<
206 GUINT m_min_hash_table_size;
210 //! Returns the cell index
211 inline GUINT _find_cell(GUINT hashkey)
213 _node_type * nodesptr = m_nodes.pointer();
214 GUINT start_index = (hashkey%m_table_size)*m_node_size;
215 GUINT end_index = start_index + m_node_size;
217 while(start_index<end_index)
219 GUINT value = m_hash_table[start_index];
220 if(value != GIM_INVALID_HASH)
222 if(nodesptr[value].m_key == hashkey) return start_index;
226 return GIM_INVALID_HASH;
229 //! Find the avaliable cell for the hashkey, and return an existing cell if it has the same hash key
230 inline GUINT _find_avaliable_cell(GUINT hashkey)
232 _node_type * nodesptr = m_nodes.pointer();
233 GUINT avaliable_index = GIM_INVALID_HASH;
234 GUINT start_index = (hashkey%m_table_size)*m_node_size;
235 GUINT end_index = start_index + m_node_size;
237 while(start_index<end_index)
239 GUINT value = m_hash_table[start_index];
240 if(value == GIM_INVALID_HASH)
242 if(avaliable_index==GIM_INVALID_HASH)
244 avaliable_index = start_index;
247 else if(nodesptr[value].m_key == hashkey)
253 return avaliable_index;
258 //! reserves the memory for the hash table.
260 \pre hash table must be empty
261 \post reserves the memory for the hash table, an initializes all elements to GIM_INVALID_HASH.
263 inline void _reserve_table_memory(GUINT newtablesize)
265 if(newtablesize==0) return;
266 if(m_node_size==0) return;
270 m_table_size = gim_next_prime(newtablesize);
272 GUINT datasize = m_table_size*m_node_size;
273 //Alloc the data buffer
274 m_hash_table = (GUINT *)gim_alloc(datasize*sizeof(GUINT));
277 inline void _invalidate_keys()
279 GUINT datasize = m_table_size*m_node_size;
280 for(GUINT i=0;i<datasize;i++)
282 m_hash_table[i] = GIM_INVALID_HASH;// invalidate keys
286 //! Clear all memory for the hash table
287 inline void _clear_table_memory()
289 if(m_hash_table==NULL) return;
290 gim_free(m_hash_table);
295 //! Invalidates the keys (Assigning GIM_INVALID_HASH to all) Reorders the hash keys
296 inline void _rehash()
300 _node_type * nodesptr = m_nodes.pointer();
301 for(GUINT i=0;i<(GUINT)m_nodes.size();i++)
303 GUINT nodekey = nodesptr[i].m_key;
304 if(nodekey != GIM_INVALID_HASH)
306 //Search for the avaliable cell in buffer
307 GUINT index = _find_avaliable_cell(nodekey);
310 if(m_hash_table[index]!=GIM_INVALID_HASH)
311 {//The new index is alreade used... discard this new incomming object, repeated key
312 btAssert(m_hash_table[index]==nodekey);
313 nodesptr[i].m_key = GIM_INVALID_HASH;
318 //Assign the value for alloc
319 m_hash_table[index] = i;
325 //! Resize hash table indices
326 inline void _resize_table(GUINT newsize)
329 _clear_table_memory();
331 _reserve_table_memory(newsize);
332 //Invalidate keys and rehash
336 //! Destroy hash table memory
337 inline void _destroy()
339 if(m_hash_table==NULL) return;
340 _clear_table_memory();
343 //! Finds an avaliable hash table cell, and resizes the table if there isn't space
344 inline GUINT _assign_hash_table_cell(GUINT hashkey)
346 GUINT cell_index = _find_avaliable_cell(hashkey);
348 if(cell_index==GIM_INVALID_HASH)
351 _resize_table(m_table_size+1);
352 GUINT cell_index = _find_avaliable_cell(hashkey);
353 btAssert(cell_index!=GIM_INVALID_HASH);
358 //! erase by index in hash table
359 inline bool _erase_by_index_hash_table(GUINT index)
361 if(index >= m_nodes.size()) return false;
362 if(m_nodes[index].m_key != GIM_INVALID_HASH)
364 //Search for the avaliable cell in buffer
365 GUINT cell_index = _find_cell(m_nodes[index].m_key);
367 btAssert(cell_index!=GIM_INVALID_HASH);
368 btAssert(m_hash_table[cell_index]==index);
370 m_hash_table[cell_index] = GIM_INVALID_HASH;
373 return this->_erase_unsorted(index);
376 //! erase by key in hash table
377 inline bool _erase_hash_table(GUINT hashkey)
379 if(hashkey == GIM_INVALID_HASH) return false;
381 //Search for the avaliable cell in buffer
382 GUINT cell_index = _find_cell(hashkey);
383 if(cell_index ==GIM_INVALID_HASH) return false;
385 GUINT index = m_hash_table[cell_index];
386 m_hash_table[cell_index] = GIM_INVALID_HASH;
388 return this->_erase_unsorted(index);
393 //! insert an element in hash table
395 If the element exists, this won't insert the element
396 \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
397 If so, the element has been inserted at the last position of the array.
399 inline GUINT _insert_hash_table(GUINT hashkey, const T & value)
401 if(hashkey==GIM_INVALID_HASH)
404 _insert_unsorted(hashkey,value);
405 return GIM_INVALID_HASH;
408 GUINT cell_index = _assign_hash_table_cell(hashkey);
410 GUINT value_key = m_hash_table[cell_index];
412 if(value_key!= GIM_INVALID_HASH) return value_key;// Not overrited
414 m_hash_table[cell_index] = m_nodes.size();
416 _insert_unsorted(hashkey,value);
417 return GIM_INVALID_HASH;
420 //! insert an element in hash table.
422 If the element exists, this replaces the element.
423 \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
424 If so, the element has been inserted at the last position of the array.
426 inline GUINT _insert_hash_table_replace(GUINT hashkey, const T & value)
428 if(hashkey==GIM_INVALID_HASH)
431 _insert_unsorted(hashkey,value);
432 return GIM_INVALID_HASH;
435 GUINT cell_index = _assign_hash_table_cell(hashkey);
437 GUINT value_key = m_hash_table[cell_index];
439 if(value_key!= GIM_INVALID_HASH)
440 {//replaces the existing
441 m_nodes[value_key] = _node_type(hashkey,value);
442 return value_key;// index of the replaced element
445 m_hash_table[cell_index] = m_nodes.size();
447 _insert_unsorted(hashkey,value);
448 return GIM_INVALID_HASH;
453 ///Sorted array data management. The hash table has the indices to the corresponding m_nodes array
454 inline bool _erase_sorted(GUINT index)
456 if(index>=(GUINT)m_nodes.size()) return false;
457 m_nodes.erase_sorted(index);
458 if(m_nodes.size()<2) m_sorted = false;
462 //! faster, but unsorted
463 inline bool _erase_unsorted(GUINT index)
465 if(index>=m_nodes.size()) return false;
467 GUINT lastindex = m_nodes.size()-1;
468 if(index<lastindex && m_hash_table!=0)
470 GUINT hashkey = m_nodes[lastindex].m_key;
471 if(hashkey!=GIM_INVALID_HASH)
473 //update the new position of the last element
474 GUINT cell_index = _find_cell(hashkey);
475 btAssert(cell_index!=GIM_INVALID_HASH);
476 //new position of the last element which will be swaped
477 m_hash_table[cell_index] = index;
480 m_nodes.erase(index);
485 //! Insert in position ordered
487 Also checks if it is needed to transform this container to a hash table, by calling check_for_switching_to_hashtable
489 inline void _insert_in_pos(GUINT hashkey, const T & value, GUINT pos)
491 m_nodes.insert(_node_type(hashkey,value),pos);
492 this->check_for_switching_to_hashtable();
495 //! Insert an element in an ordered array
496 inline GUINT _insert_sorted(GUINT hashkey, const T & value)
498 if(hashkey==GIM_INVALID_HASH || size()==0)
500 m_nodes.push_back(_node_type(hashkey,value));
501 return GIM_INVALID_HASH;
503 //Insert at last position
508 GUINT last_index = m_nodes.size()-1;
509 _node_type * ptr = m_nodes.pointer();
511 bool found = gim_binary_search_ex(
512 ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
515 //Insert before found index
522 _insert_in_pos(hashkey, value, result_ind);
524 return GIM_INVALID_HASH;
527 inline GUINT _insert_sorted_replace(GUINT hashkey, const T & value)
529 if(hashkey==GIM_INVALID_HASH || size()==0)
531 m_nodes.push_back(_node_type(hashkey,value));
532 return GIM_INVALID_HASH;
534 //Insert at last position
537 GUINT last_index = m_nodes.size()-1;
538 _node_type * ptr = m_nodes.pointer();
540 bool found = gim_binary_search_ex(
541 ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
543 //Insert before found index
546 m_nodes[result_ind] = _node_type(hashkey,value);
550 _insert_in_pos(hashkey, value, result_ind);
555 //! Fast insertion in m_nodes array
556 inline GUINT _insert_unsorted(GUINT hashkey, const T & value)
558 m_nodes.push_back(_node_type(hashkey,value));
560 return GIM_INVALID_HASH;
568 <li> if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes.
569 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.
570 <li> If node_size != 0, then this container becomes a hash table for ever
573 gim_hash_table(GUINT reserve_size = GIM_DEFAULT_HASH_TABLE_SIZE,
574 GUINT node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE,
575 GUINT min_hash_table_size = GIM_INVALID_HASH)
580 m_node_size = node_size;
581 m_min_hash_table_size = min_hash_table_size;
587 m_nodes.reserve(reserve_size);
588 _reserve_table_memory(reserve_size);
593 m_nodes.reserve(GIM_DEFAULT_HASH_TABLE_SIZE);
594 _reserve_table_memory(GIM_DEFAULT_HASH_TABLE_SIZE);
598 else if(reserve_size!=0)
600 m_nodes.reserve(reserve_size);
610 inline bool is_hash_table()
612 if(m_hash_table) return true;
616 inline bool is_sorted()
618 if(size()<2) return true;
624 if(is_sorted()) return true;
625 if(m_nodes.size()<2) return false;
628 _node_type * ptr = m_nodes.pointer();
629 GUINT siz = m_nodes.size();
630 gim_sort_hash_node_array(ptr,siz);
642 bool switch_to_hashtable()
644 if(m_hash_table) return false;
645 if(m_node_size==0) m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
646 if(m_nodes.size()<GIM_DEFAULT_HASH_TABLE_SIZE)
648 _resize_table(GIM_DEFAULT_HASH_TABLE_SIZE);
652 _resize_table(m_nodes.size()+1);
658 bool switch_to_sorted_array()
660 if(m_hash_table==NULL) return true;
661 _clear_table_memory();
665 //!If the container reaches the
666 bool check_for_switching_to_hashtable()
668 if(this->m_hash_table) return true;
670 if(!(m_nodes.size()< m_min_hash_table_size))
674 m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
677 _resize_table(m_nodes.size()+1);
683 inline void set_sorted(bool value)
688 //! Retrieves the amount of keys.
689 inline GUINT size() const
691 return m_nodes.size();
694 //! Retrieves the hash key.
695 inline GUINT get_key(GUINT index) const
697 return m_nodes[index].m_key;
700 //! Retrieves the value by index
703 inline T * get_value_by_index(GUINT index)
705 return &m_nodes[index].m_data;
708 inline const T& operator[](GUINT index) const
710 return m_nodes[index].m_data;
713 inline T& operator[](GUINT index)
715 return m_nodes[index].m_data;
718 //! Finds the index of the element with the key
720 \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
721 If so, the element has been inserted at the last position of the array.
723 inline GUINT find(GUINT hashkey)
727 GUINT cell_index = _find_cell(hashkey);
728 if(cell_index==GIM_INVALID_HASH) return GIM_INVALID_HASH;
729 return m_hash_table[cell_index];
731 GUINT last_index = m_nodes.size();
734 if(last_index==0) return GIM_INVALID_HASH;
735 if(m_nodes[0].m_key == hashkey) return 0;
736 return GIM_INVALID_HASH;
741 GUINT result_ind = 0;
743 _node_type * ptr = m_nodes.pointer();
745 bool found = gim_binary_search_ex(ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
748 if(found) return result_ind;
750 return GIM_INVALID_HASH;
753 //! Retrieves the value associated with the index
755 \return the found element, or null
757 inline T * get_value(GUINT hashkey)
759 GUINT index = find(hashkey);
760 if(index == GIM_INVALID_HASH) return NULL;
761 return &m_nodes[index].m_data;
767 inline bool erase_by_index(GUINT index)
769 if(index > m_nodes.size()) return false;
771 if(m_hash_table == NULL)
775 return this->_erase_sorted(index);
779 return this->_erase_unsorted(index);
784 return this->_erase_by_index_hash_table(index);
791 inline bool erase_by_index_unsorted(GUINT index)
793 if(index > m_nodes.size()) return false;
795 if(m_hash_table == NULL)
797 return this->_erase_unsorted(index);
801 return this->_erase_by_index_hash_table(index);
811 inline bool erase_by_key(GUINT hashkey)
813 if(size()==0) return false;
817 return this->_erase_hash_table(hashkey);
821 if(is_sorted()==false) return false;
823 GUINT result_ind = find(hashkey);
824 if(result_ind!= GIM_INVALID_HASH)
826 return this->_erase_sorted(result_ind);
835 if(m_hash_table==NULL) return;
836 GUINT datasize = m_table_size*m_node_size;
837 //Initialize the hashkeys.
839 for(i=0;i<datasize;i++)
841 m_hash_table[i] = GIM_INVALID_HASH;// invalidate keys
846 //! Insert an element into the hash
848 \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position
849 of the existing element.
851 inline GUINT insert(GUINT hashkey, const T & element)
855 return this->_insert_hash_table(hashkey,element);
857 if(this->is_sorted())
859 return this->_insert_sorted(hashkey,element);
861 return this->_insert_unsorted(hashkey,element);
864 //! Insert an element into the hash, and could overrite an existing object with the same hash.
866 \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position
867 of the replaced element.
869 inline GUINT insert_override(GUINT hashkey, const T & element)
873 return this->_insert_hash_table_replace(hashkey,element);
875 if(this->is_sorted())
877 return this->_insert_sorted_replace(hashkey,element);
879 this->_insert_unsorted(hashkey,element);
880 return m_nodes.size();
885 //! Insert an element into the hash,But if this container is a sorted array, this inserts it unsorted
888 inline GUINT insert_unsorted(GUINT hashkey,const T & element)
892 return this->_insert_hash_table(hashkey,element);
894 return this->_insert_unsorted(hashkey,element);
902 #endif // GIM_CONTAINERS_H_INCLUDED