00001 #ifndef GIM_HASH_TABLE_H_INCLUDED
00002 #define GIM_HASH_TABLE_H_INCLUDED
00003
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "gim_radixsort.h"
00036
00037
00038 #define GIM_INVALID_HASH 0xffffffff //!< A very very high value
00039 #define GIM_DEFAULT_HASH_TABLE_SIZE 380
00040 #define GIM_DEFAULT_HASH_TABLE_NODE_SIZE 4
00041 #define GIM_HASH_TABLE_GROW_FACTOR 2
00042
00043 #define GIM_MIN_RADIX_SORT_SIZE 860 //!< calibrated on a PIII
00044
00045 template<typename T>
00046 struct GIM_HASH_TABLE_NODE
00047 {
00048 GUINT m_key;
00049 T m_data;
00050 GIM_HASH_TABLE_NODE()
00051 {
00052 }
00053
00054 GIM_HASH_TABLE_NODE(const GIM_HASH_TABLE_NODE & value)
00055 {
00056 m_key = value.m_key;
00057 m_data = value.m_data;
00058 }
00059
00060 GIM_HASH_TABLE_NODE(GUINT key, const T & data)
00061 {
00062 m_key = key;
00063 m_data = data;
00064 }
00065
00066 bool operator <(const GIM_HASH_TABLE_NODE<T> & other) const
00067 {
00069 if(m_key < other.m_key) return true;
00070 return false;
00071 }
00072
00073 bool operator >(const GIM_HASH_TABLE_NODE<T> & other) const
00074 {
00076 if(m_key > other.m_key) return true;
00077 return false;
00078 }
00079
00080 bool operator ==(const GIM_HASH_TABLE_NODE<T> & other) const
00081 {
00083 if(m_key == other.m_key) return true;
00084 return false;
00085 }
00086 };
00087
00089 class GIM_HASH_NODE_GET_KEY
00090 {
00091 public:
00092 template<class T>
00093 inline GUINT operator()( const T& a)
00094 {
00095 return a.m_key;
00096 }
00097 };
00098
00099
00100
00102 class GIM_HASH_NODE_CMP_KEY_MACRO
00103 {
00104 public:
00105 template<class T>
00106 inline int operator() ( const T& a, GUINT key)
00107 {
00108 return ((int)(a.m_key - key));
00109 }
00110 };
00111
00113 class GIM_HASH_NODE_CMP_MACRO
00114 {
00115 public:
00116 template<class T>
00117 inline int operator() ( const T& a, const T& b )
00118 {
00119 return ((int)(a.m_key - b.m_key));
00120 }
00121 };
00122
00123
00124
00125
00126
00128
00131 template<typename T>
00132 void gim_sort_hash_node_array(T * array, GUINT array_count)
00133 {
00134 if(array_count<GIM_MIN_RADIX_SORT_SIZE)
00135 {
00136 gim_heap_sort(array,array_count,GIM_HASH_NODE_CMP_MACRO());
00137 }
00138 else
00139 {
00140 memcopy_elements_func cmpfunc;
00141 gim_radix_sort(array,array_count,GIM_HASH_NODE_GET_KEY(),cmpfunc);
00142 }
00143 }
00144
00145
00146
00147
00148
00149
00150
00151 #define GIM_NUM_PRIME 28
00152
00153 static const GUINT gim_prime_list[GIM_NUM_PRIME] =
00154 {
00155 53ul, 97ul, 193ul, 389ul, 769ul,
00156 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
00157 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
00158 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
00159 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
00160 1610612741ul, 3221225473ul, 4294967291ul
00161 };
00162
00163 inline GUINT gim_next_prime(GUINT number)
00164 {
00165
00166 GUINT result_ind = 0;
00167 gim_binary_search(gim_prime_list,0,(GIM_NUM_PRIME-2),number,result_ind);
00168
00169
00170 return gim_prime_list[result_ind];
00171 }
00172
00173
00174
00176
00190 template<class T>
00191 class gim_hash_table
00192 {
00193 protected:
00194 typedef GIM_HASH_TABLE_NODE<T> _node_type;
00195
00197
00198 gim_array< _node_type > m_nodes;
00199
00200 bool m_sorted;
00201
00203 GUINT * m_hash_table;
00204 GUINT m_table_size;
00205 GUINT m_node_size;
00206 GUINT m_min_hash_table_size;
00207
00208
00209
00211 inline GUINT _find_cell(GUINT hashkey)
00212 {
00213 _node_type * nodesptr = m_nodes.pointer();
00214 GUINT start_index = (hashkey%m_table_size)*m_node_size;
00215 GUINT end_index = start_index + m_node_size;
00216
00217 while(start_index<end_index)
00218 {
00219 GUINT value = m_hash_table[start_index];
00220 if(value != GIM_INVALID_HASH)
00221 {
00222 if(nodesptr[value].m_key == hashkey) return start_index;
00223 }
00224 start_index++;
00225 }
00226 return GIM_INVALID_HASH;
00227 }
00228
00230 inline GUINT _find_avaliable_cell(GUINT hashkey)
00231 {
00232 _node_type * nodesptr = m_nodes.pointer();
00233 GUINT avaliable_index = GIM_INVALID_HASH;
00234 GUINT start_index = (hashkey%m_table_size)*m_node_size;
00235 GUINT end_index = start_index + m_node_size;
00236
00237 while(start_index<end_index)
00238 {
00239 GUINT value = m_hash_table[start_index];
00240 if(value == GIM_INVALID_HASH)
00241 {
00242 if(avaliable_index==GIM_INVALID_HASH)
00243 {
00244 avaliable_index = start_index;
00245 }
00246 }
00247 else if(nodesptr[value].m_key == hashkey)
00248 {
00249 return start_index;
00250 }
00251 start_index++;
00252 }
00253 return avaliable_index;
00254 }
00255
00256
00257
00259
00263 inline void _reserve_table_memory(GUINT newtablesize)
00264 {
00265 if(newtablesize==0) return;
00266 if(m_node_size==0) return;
00267
00268
00269
00270 m_table_size = gim_next_prime(newtablesize);
00271
00272 GUINT datasize = m_table_size*m_node_size;
00273
00274 m_hash_table = (GUINT *)gim_alloc(datasize*sizeof(GUINT));
00275 }
00276
00277 inline void _invalidate_keys()
00278 {
00279 GUINT datasize = m_table_size*m_node_size;
00280 for(GUINT i=0;i<datasize;i++)
00281 {
00282 m_hash_table[i] = GIM_INVALID_HASH;
00283 }
00284 }
00285
00287 inline void _clear_table_memory()
00288 {
00289 if(m_hash_table==NULL) return;
00290 gim_free(m_hash_table);
00291 m_hash_table = NULL;
00292 m_table_size = 0;
00293 }
00294
00296 inline void _rehash()
00297 {
00298 _invalidate_keys();
00299
00300 _node_type * nodesptr = m_nodes.pointer();
00301 for(GUINT i=0;i<(GUINT)m_nodes.size();i++)
00302 {
00303 GUINT nodekey = nodesptr[i].m_key;
00304 if(nodekey != GIM_INVALID_HASH)
00305 {
00306
00307 GUINT index = _find_avaliable_cell(nodekey);
00308
00309
00310 if(m_hash_table[index]!=GIM_INVALID_HASH)
00311 {
00312 btAssert(m_hash_table[index]==nodekey);
00313 nodesptr[i].m_key = GIM_INVALID_HASH;
00314 }
00315 else
00316 {
00317
00318
00319 m_hash_table[index] = i;
00320 }
00321 }
00322 }
00323 }
00324
00326 inline void _resize_table(GUINT newsize)
00327 {
00328
00329 _clear_table_memory();
00330
00331 _reserve_table_memory(newsize);
00332
00333 _rehash();
00334 }
00335
00337 inline void _destroy()
00338 {
00339 if(m_hash_table==NULL) return;
00340 _clear_table_memory();
00341 }
00342
00344 inline GUINT _assign_hash_table_cell(GUINT hashkey)
00345 {
00346 GUINT cell_index = _find_avaliable_cell(hashkey);
00347
00348 if(cell_index==GIM_INVALID_HASH)
00349 {
00350
00351 _resize_table(m_table_size+1);
00352 GUINT cell_index = _find_avaliable_cell(hashkey);
00353 btAssert(cell_index!=GIM_INVALID_HASH);
00354 }
00355 return cell_index;
00356 }
00357
00359 inline bool _erase_by_index_hash_table(GUINT index)
00360 {
00361 if(index >= m_nodes.size()) return false;
00362 if(m_nodes[index].m_key != GIM_INVALID_HASH)
00363 {
00364
00365 GUINT cell_index = _find_cell(m_nodes[index].m_key);
00366
00367 btAssert(cell_index!=GIM_INVALID_HASH);
00368 btAssert(m_hash_table[cell_index]==index);
00369
00370 m_hash_table[cell_index] = GIM_INVALID_HASH;
00371 }
00372
00373 return this->_erase_unsorted(index);
00374 }
00375
00377 inline bool _erase_hash_table(GUINT hashkey)
00378 {
00379 if(hashkey == GIM_INVALID_HASH) return false;
00380
00381
00382 GUINT cell_index = _find_cell(hashkey);
00383 if(cell_index ==GIM_INVALID_HASH) return false;
00384
00385 GUINT index = m_hash_table[cell_index];
00386 m_hash_table[cell_index] = GIM_INVALID_HASH;
00387
00388 return this->_erase_unsorted(index);
00389 }
00390
00391
00392
00394
00399 inline GUINT _insert_hash_table(GUINT hashkey, const T & value)
00400 {
00401 if(hashkey==GIM_INVALID_HASH)
00402 {
00403
00404 _insert_unsorted(hashkey,value);
00405 return GIM_INVALID_HASH;
00406 }
00407
00408 GUINT cell_index = _assign_hash_table_cell(hashkey);
00409
00410 GUINT value_key = m_hash_table[cell_index];
00411
00412 if(value_key!= GIM_INVALID_HASH) return value_key;
00413
00414 m_hash_table[cell_index] = m_nodes.size();
00415
00416 _insert_unsorted(hashkey,value);
00417 return GIM_INVALID_HASH;
00418 }
00419
00421
00426 inline GUINT _insert_hash_table_replace(GUINT hashkey, const T & value)
00427 {
00428 if(hashkey==GIM_INVALID_HASH)
00429 {
00430
00431 _insert_unsorted(hashkey,value);
00432 return GIM_INVALID_HASH;
00433 }
00434
00435 GUINT cell_index = _assign_hash_table_cell(hashkey);
00436
00437 GUINT value_key = m_hash_table[cell_index];
00438
00439 if(value_key!= GIM_INVALID_HASH)
00440 {
00441 m_nodes[value_key] = _node_type(hashkey,value);
00442 return value_key;
00443 }
00444
00445 m_hash_table[cell_index] = m_nodes.size();
00446
00447 _insert_unsorted(hashkey,value);
00448 return GIM_INVALID_HASH;
00449
00450 }
00451
00452
00454 inline bool _erase_sorted(GUINT index)
00455 {
00456 if(index>=(GUINT)m_nodes.size()) return false;
00457 m_nodes.erase_sorted(index);
00458 if(m_nodes.size()<2) m_sorted = false;
00459 return true;
00460 }
00461
00463 inline bool _erase_unsorted(GUINT index)
00464 {
00465 if(index>=m_nodes.size()) return false;
00466
00467 GUINT lastindex = m_nodes.size()-1;
00468 if(index<lastindex && m_hash_table!=0)
00469 {
00470 GUINT hashkey = m_nodes[lastindex].m_key;
00471 if(hashkey!=GIM_INVALID_HASH)
00472 {
00473
00474 GUINT cell_index = _find_cell(hashkey);
00475 btAssert(cell_index!=GIM_INVALID_HASH);
00476
00477 m_hash_table[cell_index] = index;
00478 }
00479 }
00480 m_nodes.erase(index);
00481 m_sorted = false;
00482 return true;
00483 }
00484
00486
00489 inline void _insert_in_pos(GUINT hashkey, const T & value, GUINT pos)
00490 {
00491 m_nodes.insert(_node_type(hashkey,value),pos);
00492 this->check_for_switching_to_hashtable();
00493 }
00494
00496 inline GUINT _insert_sorted(GUINT hashkey, const T & value)
00497 {
00498 if(hashkey==GIM_INVALID_HASH || size()==0)
00499 {
00500 m_nodes.push_back(_node_type(hashkey,value));
00501 return GIM_INVALID_HASH;
00502 }
00503
00504
00505
00506
00507 GUINT result_ind=0;
00508 GUINT last_index = m_nodes.size()-1;
00509 _node_type * ptr = m_nodes.pointer();
00510
00511 bool found = gim_binary_search_ex(
00512 ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
00513
00514
00515
00516 if(found)
00517 {
00518 return result_ind;
00519 }
00520 else
00521 {
00522 _insert_in_pos(hashkey, value, result_ind);
00523 }
00524 return GIM_INVALID_HASH;
00525 }
00526
00527 inline GUINT _insert_sorted_replace(GUINT hashkey, const T & value)
00528 {
00529 if(hashkey==GIM_INVALID_HASH || size()==0)
00530 {
00531 m_nodes.push_back(_node_type(hashkey,value));
00532 return GIM_INVALID_HASH;
00533 }
00534
00535
00536 GUINT result_ind;
00537 GUINT last_index = m_nodes.size()-1;
00538 _node_type * ptr = m_nodes.pointer();
00539
00540 bool found = gim_binary_search_ex(
00541 ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
00542
00543
00544 if(found)
00545 {
00546 m_nodes[result_ind] = _node_type(hashkey,value);
00547 }
00548 else
00549 {
00550 _insert_in_pos(hashkey, value, result_ind);
00551 }
00552 return result_ind;
00553 }
00554
00556 inline GUINT _insert_unsorted(GUINT hashkey, const T & value)
00557 {
00558 m_nodes.push_back(_node_type(hashkey,value));
00559 m_sorted = false;
00560 return GIM_INVALID_HASH;
00561 }
00562
00563
00564
00565 public:
00566
00573 gim_hash_table(GUINT reserve_size = GIM_DEFAULT_HASH_TABLE_SIZE,
00574 GUINT node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE,
00575 GUINT min_hash_table_size = GIM_INVALID_HASH)
00576 {
00577 m_hash_table = NULL;
00578 m_table_size = 0;
00579 m_sorted = false;
00580 m_node_size = node_size;
00581 m_min_hash_table_size = min_hash_table_size;
00582
00583 if(m_node_size!=0)
00584 {
00585 if(reserve_size!=0)
00586 {
00587 m_nodes.reserve(reserve_size);
00588 _reserve_table_memory(reserve_size);
00589 _invalidate_keys();
00590 }
00591 else
00592 {
00593 m_nodes.reserve(GIM_DEFAULT_HASH_TABLE_SIZE);
00594 _reserve_table_memory(GIM_DEFAULT_HASH_TABLE_SIZE);
00595 _invalidate_keys();
00596 }
00597 }
00598 else if(reserve_size!=0)
00599 {
00600 m_nodes.reserve(reserve_size);
00601 }
00602
00603 }
00604
00605 ~gim_hash_table()
00606 {
00607 _destroy();
00608 }
00609
00610 inline bool is_hash_table()
00611 {
00612 if(m_hash_table) return true;
00613 return false;
00614 }
00615
00616 inline bool is_sorted()
00617 {
00618 if(size()<2) return true;
00619 return m_sorted;
00620 }
00621
00622 bool sort()
00623 {
00624 if(is_sorted()) return true;
00625 if(m_nodes.size()<2) return false;
00626
00627
00628 _node_type * ptr = m_nodes.pointer();
00629 GUINT siz = m_nodes.size();
00630 gim_sort_hash_node_array(ptr,siz);
00631 m_sorted=true;
00632
00633
00634
00635 if(m_hash_table)
00636 {
00637 _rehash();
00638 }
00639 return true;
00640 }
00641
00642 bool switch_to_hashtable()
00643 {
00644 if(m_hash_table) return false;
00645 if(m_node_size==0) m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
00646 if(m_nodes.size()<GIM_DEFAULT_HASH_TABLE_SIZE)
00647 {
00648 _resize_table(GIM_DEFAULT_HASH_TABLE_SIZE);
00649 }
00650 else
00651 {
00652 _resize_table(m_nodes.size()+1);
00653 }
00654
00655 return true;
00656 }
00657
00658 bool switch_to_sorted_array()
00659 {
00660 if(m_hash_table==NULL) return true;
00661 _clear_table_memory();
00662 return sort();
00663 }
00664
00666 bool check_for_switching_to_hashtable()
00667 {
00668 if(this->m_hash_table) return true;
00669
00670 if(!(m_nodes.size()< m_min_hash_table_size))
00671 {
00672 if(m_node_size == 0)
00673 {
00674 m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
00675 }
00676
00677 _resize_table(m_nodes.size()+1);
00678 return true;
00679 }
00680 return false;
00681 }
00682
00683 inline void set_sorted(bool value)
00684 {
00685 m_sorted = value;
00686 }
00687
00689 inline GUINT size() const
00690 {
00691 return m_nodes.size();
00692 }
00693
00695 inline GUINT get_key(GUINT index) const
00696 {
00697 return m_nodes[index].m_key;
00698 }
00699
00701
00703 inline T * get_value_by_index(GUINT index)
00704 {
00705 return &m_nodes[index].m_data;
00706 }
00707
00708 inline const T& operator[](GUINT index) const
00709 {
00710 return m_nodes[index].m_data;
00711 }
00712
00713 inline T& operator[](GUINT index)
00714 {
00715 return m_nodes[index].m_data;
00716 }
00717
00719
00723 inline GUINT find(GUINT hashkey)
00724 {
00725 if(m_hash_table)
00726 {
00727 GUINT cell_index = _find_cell(hashkey);
00728 if(cell_index==GIM_INVALID_HASH) return GIM_INVALID_HASH;
00729 return m_hash_table[cell_index];
00730 }
00731 GUINT last_index = m_nodes.size();
00732 if(last_index<2)
00733 {
00734 if(last_index==0) return GIM_INVALID_HASH;
00735 if(m_nodes[0].m_key == hashkey) return 0;
00736 return GIM_INVALID_HASH;
00737 }
00738 else if(m_sorted)
00739 {
00740
00741 GUINT result_ind = 0;
00742 last_index--;
00743 _node_type * ptr = m_nodes.pointer();
00744
00745 bool found = gim_binary_search_ex(ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
00746
00747
00748 if(found) return result_ind;
00749 }
00750 return GIM_INVALID_HASH;
00751 }
00752
00754
00757 inline T * get_value(GUINT hashkey)
00758 {
00759 GUINT index = find(hashkey);
00760 if(index == GIM_INVALID_HASH) return NULL;
00761 return &m_nodes[index].m_data;
00762 }
00763
00764
00767 inline bool erase_by_index(GUINT index)
00768 {
00769 if(index > m_nodes.size()) return false;
00770
00771 if(m_hash_table == NULL)
00772 {
00773 if(is_sorted())
00774 {
00775 return this->_erase_sorted(index);
00776 }
00777 else
00778 {
00779 return this->_erase_unsorted(index);
00780 }
00781 }
00782 else
00783 {
00784 return this->_erase_by_index_hash_table(index);
00785 }
00786 return false;
00787 }
00788
00789
00790
00791 inline bool erase_by_index_unsorted(GUINT index)
00792 {
00793 if(index > m_nodes.size()) return false;
00794
00795 if(m_hash_table == NULL)
00796 {
00797 return this->_erase_unsorted(index);
00798 }
00799 else
00800 {
00801 return this->_erase_by_index_hash_table(index);
00802 }
00803 return false;
00804 }
00805
00806
00807
00811 inline bool erase_by_key(GUINT hashkey)
00812 {
00813 if(size()==0) return false;
00814
00815 if(m_hash_table)
00816 {
00817 return this->_erase_hash_table(hashkey);
00818 }
00819
00820
00821 if(is_sorted()==false) return false;
00822
00823 GUINT result_ind = find(hashkey);
00824 if(result_ind!= GIM_INVALID_HASH)
00825 {
00826 return this->_erase_sorted(result_ind);
00827 }
00828 return false;
00829 }
00830
00831 void clear()
00832 {
00833 m_nodes.clear();
00834
00835 if(m_hash_table==NULL) return;
00836 GUINT datasize = m_table_size*m_node_size;
00837
00838 GUINT i;
00839 for(i=0;i<datasize;i++)
00840 {
00841 m_hash_table[i] = GIM_INVALID_HASH;
00842 }
00843 m_sorted = false;
00844 }
00845
00847
00851 inline GUINT insert(GUINT hashkey, const T & element)
00852 {
00853 if(m_hash_table)
00854 {
00855 return this->_insert_hash_table(hashkey,element);
00856 }
00857 if(this->is_sorted())
00858 {
00859 return this->_insert_sorted(hashkey,element);
00860 }
00861 return this->_insert_unsorted(hashkey,element);
00862 }
00863
00865
00869 inline GUINT insert_override(GUINT hashkey, const T & element)
00870 {
00871 if(m_hash_table)
00872 {
00873 return this->_insert_hash_table_replace(hashkey,element);
00874 }
00875 if(this->is_sorted())
00876 {
00877 return this->_insert_sorted_replace(hashkey,element);
00878 }
00879 this->_insert_unsorted(hashkey,element);
00880 return m_nodes.size();
00881 }
00882
00883
00884
00886
00888 inline GUINT insert_unsorted(GUINT hashkey,const T & element)
00889 {
00890 if(m_hash_table)
00891 {
00892 return this->_insert_hash_table(hashkey,element);
00893 }
00894 return this->_insert_unsorted(hashkey,element);
00895 }
00896
00897
00898 };
00899
00900
00901
00902 #endif // GIM_CONTAINERS_H_INCLUDED