| 1 | #ifndef GIM_HASH_TABLE_H_INCLUDED | 
|---|
| 2 | #define GIM_HASH_TABLE_H_INCLUDED | 
|---|
| 3 | /*! \file gim_trimesh_data.h | 
|---|
| 4 | \author Francisco Len Nßjera | 
|---|
| 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 |  | 
|---|
| 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 | 
|---|
| 42 |  | 
|---|
| 43 | #define GIM_MIN_RADIX_SORT_SIZE 860 //!< calibrated on a PIII | 
|---|
| 44 |  | 
|---|
| 45 | template<typename T> | 
|---|
| 46 | struct GIM_HASH_TABLE_NODE | 
|---|
| 47 | { | 
|---|
| 48 |     GUINT m_key; | 
|---|
| 49 |     T m_data; | 
|---|
| 50 |     GIM_HASH_TABLE_NODE() | 
|---|
| 51 |     { | 
|---|
| 52 |     } | 
|---|
| 53 |  | 
|---|
| 54 |     GIM_HASH_TABLE_NODE(const GIM_HASH_TABLE_NODE & value) | 
|---|
| 55 |     { | 
|---|
| 56 |         m_key = value.m_key; | 
|---|
| 57 |         m_data = value.m_data; | 
|---|
| 58 |     } | 
|---|
| 59 |  | 
|---|
| 60 |     GIM_HASH_TABLE_NODE(GUINT key, const T & data) | 
|---|
| 61 |     { | 
|---|
| 62 |         m_key = key; | 
|---|
| 63 |         m_data = data; | 
|---|
| 64 |     } | 
|---|
| 65 |  | 
|---|
| 66 |     bool operator <(const GIM_HASH_TABLE_NODE<T> & other) const | 
|---|
| 67 |         { | 
|---|
| 68 |                 ///inverse order, further objects are first | 
|---|
| 69 |                 if(m_key <  other.m_key) return true; | 
|---|
| 70 |                 return false; | 
|---|
| 71 |         } | 
|---|
| 72 |  | 
|---|
| 73 |         bool operator >(const GIM_HASH_TABLE_NODE<T> & other) const | 
|---|
| 74 |         { | 
|---|
| 75 |                 ///inverse order, further objects are first | 
|---|
| 76 |                 if(m_key >  other.m_key) return true; | 
|---|
| 77 |                 return false; | 
|---|
| 78 |         } | 
|---|
| 79 |  | 
|---|
| 80 |         bool operator ==(const GIM_HASH_TABLE_NODE<T> & other) const | 
|---|
| 81 |         { | 
|---|
| 82 |                 ///inverse order, further objects are first | 
|---|
| 83 |                 if(m_key ==  other.m_key) return true; | 
|---|
| 84 |                 return false; | 
|---|
| 85 |         } | 
|---|
| 86 | }; | 
|---|
| 87 |  | 
|---|
| 88 | ///Macro for getting the key | 
|---|
| 89 | class GIM_HASH_NODE_GET_KEY | 
|---|
| 90 | { | 
|---|
| 91 | public: | 
|---|
| 92 |         template<class T> | 
|---|
| 93 |         inline GUINT operator()( const T& a) | 
|---|
| 94 |         { | 
|---|
| 95 |                 return a.m_key; | 
|---|
| 96 |         } | 
|---|
| 97 | }; | 
|---|
| 98 |  | 
|---|
| 99 |  | 
|---|
| 100 |  | 
|---|
| 101 | ///Macro for comparing the key and the element | 
|---|
| 102 | class GIM_HASH_NODE_CMP_KEY_MACRO | 
|---|
| 103 | { | 
|---|
| 104 | public: | 
|---|
| 105 |         template<class T> | 
|---|
| 106 |         inline int operator() ( const T& a, GUINT key) | 
|---|
| 107 |         { | 
|---|
| 108 |                 return ((int)(a.m_key - key)); | 
|---|
| 109 |         } | 
|---|
| 110 | }; | 
|---|
| 111 |  | 
|---|
| 112 | ///Macro for comparing Hash nodes | 
|---|
| 113 | class GIM_HASH_NODE_CMP_MACRO | 
|---|
| 114 | { | 
|---|
| 115 | public: | 
|---|
| 116 |         template<class T> | 
|---|
| 117 |         inline int operator() ( const T& a, const T& b ) | 
|---|
| 118 |         { | 
|---|
| 119 |                 return ((int)(a.m_key - b.m_key)); | 
|---|
| 120 |         } | 
|---|
| 121 | }; | 
|---|
| 122 |  | 
|---|
| 123 |  | 
|---|
| 124 |  | 
|---|
| 125 |  | 
|---|
| 126 |  | 
|---|
| 127 | //! Sorting for hash table | 
|---|
| 128 | /*! | 
|---|
| 129 | switch automatically between quicksort and radixsort | 
|---|
| 130 | */ | 
|---|
| 131 | template<typename T> | 
|---|
| 132 | void gim_sort_hash_node_array(T * array, GUINT array_count) | 
|---|
| 133 | { | 
|---|
| 134 |     if(array_count<GIM_MIN_RADIX_SORT_SIZE) | 
|---|
| 135 |     { | 
|---|
| 136 |         gim_heap_sort(array,array_count,GIM_HASH_NODE_CMP_MACRO()); | 
|---|
| 137 |     } | 
|---|
| 138 |     else | 
|---|
| 139 |     { | 
|---|
| 140 |         memcopy_elements_func cmpfunc; | 
|---|
| 141 |         gim_radix_sort(array,array_count,GIM_HASH_NODE_GET_KEY(),cmpfunc); | 
|---|
| 142 |     } | 
|---|
| 143 | } | 
|---|
| 144 |  | 
|---|
| 145 |  | 
|---|
| 146 |  | 
|---|
| 147 |  | 
|---|
| 148 |  | 
|---|
| 149 |  | 
|---|
| 150 | // Note: assumes long is at least 32 bits. | 
|---|
| 151 | #define GIM_NUM_PRIME 28 | 
|---|
| 152 |  | 
|---|
| 153 | static const GUINT gim_prime_list[GIM_NUM_PRIME] = | 
|---|
| 154 | { | 
|---|
| 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 | 
|---|
| 161 | }; | 
|---|
| 162 |  | 
|---|
| 163 | inline GUINT gim_next_prime(GUINT number) | 
|---|
| 164 | { | 
|---|
| 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); | 
|---|
| 168 |  | 
|---|
| 169 |     // inv: result_ind < 28 | 
|---|
| 170 |     return gim_prime_list[result_ind]; | 
|---|
| 171 | } | 
|---|
| 172 |  | 
|---|
| 173 |  | 
|---|
| 174 |  | 
|---|
| 175 | //! A compact hash table implementation | 
|---|
| 176 | /*! | 
|---|
| 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. | 
|---|
| 181 | </br> | 
|---|
| 182 |  | 
|---|
| 183 | <ul> | 
|---|
| 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 | 
|---|
| 187 | </ul> | 
|---|
| 188 |  | 
|---|
| 189 | */ | 
|---|
| 190 | template<class T> | 
|---|
| 191 | class gim_hash_table | 
|---|
| 192 | { | 
|---|
| 193 | protected: | 
|---|
| 194 |     typedef GIM_HASH_TABLE_NODE<T> _node_type; | 
|---|
| 195 |  | 
|---|
| 196 |     //!The nodes | 
|---|
| 197 |     //array< _node_type, SuperAllocator<_node_type> > m_nodes; | 
|---|
| 198 |     gim_array< _node_type > m_nodes; | 
|---|
| 199 |     //SuperBufferedArray< _node_type > m_nodes; | 
|---|
| 200 |     bool m_sorted; | 
|---|
| 201 |  | 
|---|
| 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; | 
|---|
| 207 |  | 
|---|
| 208 |  | 
|---|
| 209 |  | 
|---|
| 210 |     //! Returns the cell index | 
|---|
| 211 |     inline GUINT _find_cell(GUINT hashkey) | 
|---|
| 212 |     { | 
|---|
| 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; | 
|---|
| 216 |  | 
|---|
| 217 |         while(start_index<end_index) | 
|---|
| 218 |         { | 
|---|
| 219 |             GUINT value = m_hash_table[start_index]; | 
|---|
| 220 |             if(value != GIM_INVALID_HASH) | 
|---|
| 221 |             { | 
|---|
| 222 |                 if(nodesptr[value].m_key == hashkey) return start_index; | 
|---|
| 223 |             } | 
|---|
| 224 |             start_index++; | 
|---|
| 225 |         } | 
|---|
| 226 |         return GIM_INVALID_HASH; | 
|---|
| 227 |     } | 
|---|
| 228 |  | 
|---|
| 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) | 
|---|
| 231 |     { | 
|---|
| 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; | 
|---|
| 236 |  | 
|---|
| 237 |         while(start_index<end_index) | 
|---|
| 238 |         { | 
|---|
| 239 |             GUINT value = m_hash_table[start_index]; | 
|---|
| 240 |             if(value == GIM_INVALID_HASH) | 
|---|
| 241 |             { | 
|---|
| 242 |                 if(avaliable_index==GIM_INVALID_HASH) | 
|---|
| 243 |                 { | 
|---|
| 244 |                     avaliable_index = start_index; | 
|---|
| 245 |                 } | 
|---|
| 246 |             } | 
|---|
| 247 |             else if(nodesptr[value].m_key == hashkey) | 
|---|
| 248 |             { | 
|---|
| 249 |                 return start_index; | 
|---|
| 250 |             } | 
|---|
| 251 |             start_index++; | 
|---|
| 252 |         } | 
|---|
| 253 |         return avaliable_index; | 
|---|
| 254 |     } | 
|---|
| 255 |  | 
|---|
| 256 |  | 
|---|
| 257 |  | 
|---|
| 258 |     //! reserves the memory for the hash table. | 
|---|
| 259 |     /*! | 
|---|
| 260 |     \pre hash table must be empty | 
|---|
| 261 |     \post reserves the memory for the hash table, an initializes all elements to GIM_INVALID_HASH. | 
|---|
| 262 |     */ | 
|---|
| 263 |     inline void _reserve_table_memory(GUINT newtablesize) | 
|---|
| 264 |     { | 
|---|
| 265 |         if(newtablesize==0) return; | 
|---|
| 266 |         if(m_node_size==0) return; | 
|---|
| 267 |  | 
|---|
| 268 |         //Get a Prime size | 
|---|
| 269 |  | 
|---|
| 270 |         m_table_size = gim_next_prime(newtablesize); | 
|---|
| 271 |  | 
|---|
| 272 |         GUINT datasize = m_table_size*m_node_size; | 
|---|
| 273 |         //Alloc the data buffer | 
|---|
| 274 |         m_hash_table =  (GUINT *)gim_alloc(datasize*sizeof(GUINT)); | 
|---|
| 275 |     } | 
|---|
| 276 |  | 
|---|
| 277 |     inline void _invalidate_keys() | 
|---|
| 278 |     { | 
|---|
| 279 |         GUINT datasize = m_table_size*m_node_size; | 
|---|
| 280 |         for(GUINT i=0;i<datasize;i++) | 
|---|
| 281 |         { | 
|---|
| 282 |             m_hash_table[i] = GIM_INVALID_HASH;// invalidate keys | 
|---|
| 283 |         } | 
|---|
| 284 |     } | 
|---|
| 285 |  | 
|---|
| 286 |     //! Clear all memory for the hash table | 
|---|
| 287 |     inline void _clear_table_memory() | 
|---|
| 288 |     { | 
|---|
| 289 |         if(m_hash_table==NULL) return; | 
|---|
| 290 |         gim_free(m_hash_table); | 
|---|
| 291 |         m_hash_table = NULL; | 
|---|
| 292 |         m_table_size = 0; | 
|---|
| 293 |     } | 
|---|
| 294 |  | 
|---|
| 295 |     //! Invalidates the keys (Assigning GIM_INVALID_HASH to all) Reorders the hash keys | 
|---|
| 296 |     inline void _rehash() | 
|---|
| 297 |     { | 
|---|
| 298 |         _invalidate_keys(); | 
|---|
| 299 |  | 
|---|
| 300 |         _node_type * nodesptr = m_nodes.pointer(); | 
|---|
| 301 |         for(GUINT i=0;i<(GUINT)m_nodes.size();i++) | 
|---|
| 302 |         { | 
|---|
| 303 |             GUINT nodekey = nodesptr[i].m_key; | 
|---|
| 304 |             if(nodekey != GIM_INVALID_HASH) | 
|---|
| 305 |             { | 
|---|
| 306 |                 //Search for the avaliable cell in buffer | 
|---|
| 307 |                 GUINT index = _find_avaliable_cell(nodekey); | 
|---|
| 308 |  | 
|---|
| 309 |  | 
|---|
| 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; | 
|---|
| 314 |                                 } | 
|---|
| 315 |                                 else | 
|---|
| 316 |                                 { | 
|---|
| 317 |                                         //; | 
|---|
| 318 |                                         //Assign the value for alloc | 
|---|
| 319 |                                         m_hash_table[index] = i; | 
|---|
| 320 |                                 } | 
|---|
| 321 |             } | 
|---|
| 322 |         } | 
|---|
| 323 |     } | 
|---|
| 324 |  | 
|---|
| 325 |     //! Resize hash table indices | 
|---|
| 326 |     inline void _resize_table(GUINT newsize) | 
|---|
| 327 |     { | 
|---|
| 328 |         //Clear memory | 
|---|
| 329 |         _clear_table_memory(); | 
|---|
| 330 |         //Alloc the data | 
|---|
| 331 |         _reserve_table_memory(newsize); | 
|---|
| 332 |         //Invalidate keys and rehash | 
|---|
| 333 |         _rehash(); | 
|---|
| 334 |     } | 
|---|
| 335 |  | 
|---|
| 336 |     //! Destroy hash table memory | 
|---|
| 337 |     inline void _destroy() | 
|---|
| 338 |     { | 
|---|
| 339 |         if(m_hash_table==NULL) return; | 
|---|
| 340 |         _clear_table_memory(); | 
|---|
| 341 |     } | 
|---|
| 342 |  | 
|---|
| 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) | 
|---|
| 345 |     { | 
|---|
| 346 |         GUINT cell_index = _find_avaliable_cell(hashkey); | 
|---|
| 347 |  | 
|---|
| 348 |         if(cell_index==GIM_INVALID_HASH) | 
|---|
| 349 |         { | 
|---|
| 350 |             //rehashing | 
|---|
| 351 |             _resize_table(m_table_size+1); | 
|---|
| 352 |             GUINT cell_index = _find_avaliable_cell(hashkey); | 
|---|
| 353 |             btAssert(cell_index!=GIM_INVALID_HASH); | 
|---|
| 354 |         } | 
|---|
| 355 |         return cell_index; | 
|---|
| 356 |     } | 
|---|
| 357 |  | 
|---|
| 358 |     //! erase by index in hash table | 
|---|
| 359 |     inline bool _erase_by_index_hash_table(GUINT index) | 
|---|
| 360 |     { | 
|---|
| 361 |         if(index >= m_nodes.size()) return false; | 
|---|
| 362 |         if(m_nodes[index].m_key != GIM_INVALID_HASH) | 
|---|
| 363 |         { | 
|---|
| 364 |             //Search for the avaliable cell in buffer | 
|---|
| 365 |             GUINT cell_index = _find_cell(m_nodes[index].m_key); | 
|---|
| 366 |  | 
|---|
| 367 |             btAssert(cell_index!=GIM_INVALID_HASH); | 
|---|
| 368 |             btAssert(m_hash_table[cell_index]==index); | 
|---|
| 369 |  | 
|---|
| 370 |             m_hash_table[cell_index] = GIM_INVALID_HASH; | 
|---|
| 371 |         } | 
|---|
| 372 |  | 
|---|
| 373 |         return this->_erase_unsorted(index); | 
|---|
| 374 |     } | 
|---|
| 375 |  | 
|---|
| 376 |     //! erase by key in hash table | 
|---|
| 377 |     inline bool _erase_hash_table(GUINT hashkey) | 
|---|
| 378 |     { | 
|---|
| 379 |         if(hashkey == GIM_INVALID_HASH) return false; | 
|---|
| 380 |  | 
|---|
| 381 |         //Search for the avaliable cell in buffer | 
|---|
| 382 |         GUINT cell_index = _find_cell(hashkey); | 
|---|
| 383 |         if(cell_index ==GIM_INVALID_HASH) return false; | 
|---|
| 384 |  | 
|---|
| 385 |         GUINT index = m_hash_table[cell_index]; | 
|---|
| 386 |         m_hash_table[cell_index] = GIM_INVALID_HASH; | 
|---|
| 387 |  | 
|---|
| 388 |         return this->_erase_unsorted(index); | 
|---|
| 389 |     } | 
|---|
| 390 |  | 
|---|
| 391 |  | 
|---|
| 392 |  | 
|---|
| 393 |     //! insert an element in hash table | 
|---|
| 394 |     /*! | 
|---|
| 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. | 
|---|
| 398 |     */ | 
|---|
| 399 |     inline GUINT _insert_hash_table(GUINT hashkey, const T & value) | 
|---|
| 400 |     { | 
|---|
| 401 |         if(hashkey==GIM_INVALID_HASH) | 
|---|
| 402 |         { | 
|---|
| 403 |             //Insert anyway | 
|---|
| 404 |             _insert_unsorted(hashkey,value); | 
|---|
| 405 |             return GIM_INVALID_HASH; | 
|---|
| 406 |         } | 
|---|
| 407 |  | 
|---|
| 408 |         GUINT cell_index = _assign_hash_table_cell(hashkey); | 
|---|
| 409 |  | 
|---|
| 410 |         GUINT value_key = m_hash_table[cell_index]; | 
|---|
| 411 |  | 
|---|
| 412 |         if(value_key!= GIM_INVALID_HASH) return value_key;// Not overrited | 
|---|
| 413 |  | 
|---|
| 414 |         m_hash_table[cell_index] = m_nodes.size(); | 
|---|
| 415 |  | 
|---|
| 416 |         _insert_unsorted(hashkey,value); | 
|---|
| 417 |         return GIM_INVALID_HASH; | 
|---|
| 418 |     } | 
|---|
| 419 |  | 
|---|
| 420 |     //! insert an element in hash table. | 
|---|
| 421 |     /*! | 
|---|
| 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. | 
|---|
| 425 |     */ | 
|---|
| 426 |     inline GUINT _insert_hash_table_replace(GUINT hashkey, const T & value) | 
|---|
| 427 |     { | 
|---|
| 428 |         if(hashkey==GIM_INVALID_HASH) | 
|---|
| 429 |         { | 
|---|
| 430 |             //Insert anyway | 
|---|
| 431 |             _insert_unsorted(hashkey,value); | 
|---|
| 432 |             return GIM_INVALID_HASH; | 
|---|
| 433 |         } | 
|---|
| 434 |  | 
|---|
| 435 |         GUINT cell_index = _assign_hash_table_cell(hashkey); | 
|---|
| 436 |  | 
|---|
| 437 |         GUINT value_key = m_hash_table[cell_index]; | 
|---|
| 438 |  | 
|---|
| 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 | 
|---|
| 443 |         } | 
|---|
| 444 |  | 
|---|
| 445 |         m_hash_table[cell_index] = m_nodes.size(); | 
|---|
| 446 |  | 
|---|
| 447 |         _insert_unsorted(hashkey,value); | 
|---|
| 448 |         return GIM_INVALID_HASH; | 
|---|
| 449 |  | 
|---|
| 450 |     } | 
|---|
| 451 |  | 
|---|
| 452 |      | 
|---|
| 453 |     ///Sorted array data management. The hash table has the indices to the corresponding m_nodes array | 
|---|
| 454 |     inline bool _erase_sorted(GUINT index) | 
|---|
| 455 |     { | 
|---|
| 456 |         if(index>=(GUINT)m_nodes.size()) return false; | 
|---|
| 457 |         m_nodes.erase_sorted(index); | 
|---|
| 458 |                 if(m_nodes.size()<2) m_sorted = false; | 
|---|
| 459 |         return true; | 
|---|
| 460 |     } | 
|---|
| 461 |  | 
|---|
| 462 |     //! faster, but unsorted | 
|---|
| 463 |     inline bool _erase_unsorted(GUINT index) | 
|---|
| 464 |     { | 
|---|
| 465 |         if(index>=m_nodes.size()) return false; | 
|---|
| 466 |  | 
|---|
| 467 |         GUINT lastindex = m_nodes.size()-1; | 
|---|
| 468 |         if(index<lastindex && m_hash_table!=0) | 
|---|
| 469 |         { | 
|---|
| 470 |                         GUINT hashkey =  m_nodes[lastindex].m_key; | 
|---|
| 471 |                         if(hashkey!=GIM_INVALID_HASH) | 
|---|
| 472 |                         { | 
|---|
| 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; | 
|---|
| 478 |                         } | 
|---|
| 479 |         } | 
|---|
| 480 |         m_nodes.erase(index); | 
|---|
| 481 |         m_sorted = false; | 
|---|
| 482 |         return true; | 
|---|
| 483 |     } | 
|---|
| 484 |  | 
|---|
| 485 |     //! Insert in position ordered | 
|---|
| 486 |     /*! | 
|---|
| 487 |     Also checks if it is needed to transform this container to a hash table, by calling check_for_switching_to_hashtable | 
|---|
| 488 |     */ | 
|---|
| 489 |     inline void _insert_in_pos(GUINT hashkey, const T & value, GUINT pos) | 
|---|
| 490 |     { | 
|---|
| 491 |         m_nodes.insert(_node_type(hashkey,value),pos); | 
|---|
| 492 |         this->check_for_switching_to_hashtable(); | 
|---|
| 493 |     } | 
|---|
| 494 |  | 
|---|
| 495 |     //! Insert an element in an ordered array | 
|---|
| 496 |     inline GUINT _insert_sorted(GUINT hashkey, const T & value) | 
|---|
| 497 |     { | 
|---|
| 498 |         if(hashkey==GIM_INVALID_HASH || size()==0) | 
|---|
| 499 |         { | 
|---|
| 500 |             m_nodes.push_back(_node_type(hashkey,value)); | 
|---|
| 501 |             return GIM_INVALID_HASH; | 
|---|
| 502 |         } | 
|---|
| 503 |         //Insert at last position | 
|---|
| 504 |         //Sort element | 
|---|
| 505 |  | 
|---|
| 506 |  | 
|---|
| 507 |         GUINT result_ind=0; | 
|---|
| 508 |         GUINT last_index = m_nodes.size()-1; | 
|---|
| 509 |         _node_type * ptr = m_nodes.pointer(); | 
|---|
| 510 |  | 
|---|
| 511 |         bool found = gim_binary_search_ex( | 
|---|
| 512 |                 ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO()); | 
|---|
| 513 |  | 
|---|
| 514 |  | 
|---|
| 515 |         //Insert before found index | 
|---|
| 516 |         if(found) | 
|---|
| 517 |         { | 
|---|
| 518 |             return result_ind; | 
|---|
| 519 |         } | 
|---|
| 520 |         else | 
|---|
| 521 |         { | 
|---|
| 522 |             _insert_in_pos(hashkey, value, result_ind); | 
|---|
| 523 |         } | 
|---|
| 524 |         return GIM_INVALID_HASH; | 
|---|
| 525 |     } | 
|---|
| 526 |  | 
|---|
| 527 |     inline GUINT _insert_sorted_replace(GUINT hashkey, const T & value) | 
|---|
| 528 |     { | 
|---|
| 529 |         if(hashkey==GIM_INVALID_HASH || size()==0) | 
|---|
| 530 |         { | 
|---|
| 531 |             m_nodes.push_back(_node_type(hashkey,value)); | 
|---|
| 532 |             return GIM_INVALID_HASH; | 
|---|
| 533 |         } | 
|---|
| 534 |         //Insert at last position | 
|---|
| 535 |         //Sort element | 
|---|
| 536 |         GUINT result_ind; | 
|---|
| 537 |         GUINT last_index = m_nodes.size()-1; | 
|---|
| 538 |         _node_type * ptr = m_nodes.pointer(); | 
|---|
| 539 |  | 
|---|
| 540 |         bool found = gim_binary_search_ex( | 
|---|
| 541 |                 ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO()); | 
|---|
| 542 |  | 
|---|
| 543 |         //Insert before found index | 
|---|
| 544 |         if(found) | 
|---|
| 545 |         { | 
|---|
| 546 |             m_nodes[result_ind] = _node_type(hashkey,value); | 
|---|
| 547 |         } | 
|---|
| 548 |         else | 
|---|
| 549 |         { | 
|---|
| 550 |             _insert_in_pos(hashkey, value, result_ind); | 
|---|
| 551 |         } | 
|---|
| 552 |         return result_ind; | 
|---|
| 553 |     } | 
|---|
| 554 |  | 
|---|
| 555 |     //! Fast insertion in m_nodes array | 
|---|
| 556 |     inline GUINT  _insert_unsorted(GUINT hashkey, const T & value) | 
|---|
| 557 |     { | 
|---|
| 558 |         m_nodes.push_back(_node_type(hashkey,value)); | 
|---|
| 559 |         m_sorted = false; | 
|---|
| 560 |         return GIM_INVALID_HASH; | 
|---|
| 561 |     } | 
|---|
| 562 |  | 
|---|
| 563 |      | 
|---|
| 564 |  | 
|---|
| 565 | public: | 
|---|
| 566 |  | 
|---|
| 567 |     /*! | 
|---|
| 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 | 
|---|
| 571 |         </ul> | 
|---|
| 572 |     */ | 
|---|
| 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) | 
|---|
| 576 |     { | 
|---|
| 577 |         m_hash_table = NULL; | 
|---|
| 578 |         m_table_size = 0; | 
|---|
| 579 |         m_sorted = false; | 
|---|
| 580 |         m_node_size = node_size; | 
|---|
| 581 |         m_min_hash_table_size = min_hash_table_size; | 
|---|
| 582 |  | 
|---|
| 583 |         if(m_node_size!=0) | 
|---|
| 584 |         { | 
|---|
| 585 |             if(reserve_size!=0) | 
|---|
| 586 |             { | 
|---|
| 587 |                 m_nodes.reserve(reserve_size); | 
|---|
| 588 |                 _reserve_table_memory(reserve_size); | 
|---|
| 589 |                 _invalidate_keys(); | 
|---|
| 590 |             } | 
|---|
| 591 |             else | 
|---|
| 592 |             { | 
|---|
| 593 |                 m_nodes.reserve(GIM_DEFAULT_HASH_TABLE_SIZE); | 
|---|
| 594 |                 _reserve_table_memory(GIM_DEFAULT_HASH_TABLE_SIZE); | 
|---|
| 595 |                 _invalidate_keys(); | 
|---|
| 596 |             } | 
|---|
| 597 |         } | 
|---|
| 598 |         else if(reserve_size!=0) | 
|---|
| 599 |         { | 
|---|
| 600 |             m_nodes.reserve(reserve_size); | 
|---|
| 601 |         } | 
|---|
| 602 |  | 
|---|
| 603 |     } | 
|---|
| 604 |  | 
|---|
| 605 |     ~gim_hash_table() | 
|---|
| 606 |     { | 
|---|
| 607 |         _destroy(); | 
|---|
| 608 |     } | 
|---|
| 609 |  | 
|---|
| 610 |     inline bool is_hash_table() | 
|---|
| 611 |     { | 
|---|
| 612 |         if(m_hash_table) return true; | 
|---|
| 613 |         return false; | 
|---|
| 614 |     } | 
|---|
| 615 |  | 
|---|
| 616 |     inline bool is_sorted() | 
|---|
| 617 |     { | 
|---|
| 618 |         if(size()<2) return true; | 
|---|
| 619 |         return m_sorted; | 
|---|
| 620 |     } | 
|---|
| 621 |  | 
|---|
| 622 |     bool sort() | 
|---|
| 623 |     { | 
|---|
| 624 |         if(is_sorted()) return true; | 
|---|
| 625 |         if(m_nodes.size()<2) return false; | 
|---|
| 626 |  | 
|---|
| 627 |  | 
|---|
| 628 |         _node_type * ptr = m_nodes.pointer(); | 
|---|
| 629 |         GUINT siz = m_nodes.size(); | 
|---|
| 630 |         gim_sort_hash_node_array(ptr,siz); | 
|---|
| 631 |         m_sorted=true; | 
|---|
| 632 |  | 
|---|
| 633 |  | 
|---|
| 634 |  | 
|---|
| 635 |         if(m_hash_table) | 
|---|
| 636 |         { | 
|---|
| 637 |             _rehash(); | 
|---|
| 638 |         } | 
|---|
| 639 |         return true; | 
|---|
| 640 |     } | 
|---|
| 641 |  | 
|---|
| 642 |     bool switch_to_hashtable() | 
|---|
| 643 |     { | 
|---|
| 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) | 
|---|
| 647 |         { | 
|---|
| 648 |             _resize_table(GIM_DEFAULT_HASH_TABLE_SIZE); | 
|---|
| 649 |         } | 
|---|
| 650 |         else | 
|---|
| 651 |         { | 
|---|
| 652 |             _resize_table(m_nodes.size()+1); | 
|---|
| 653 |         } | 
|---|
| 654 |  | 
|---|
| 655 |         return true; | 
|---|
| 656 |     } | 
|---|
| 657 |  | 
|---|
| 658 |     bool switch_to_sorted_array() | 
|---|
| 659 |     { | 
|---|
| 660 |         if(m_hash_table==NULL) return true; | 
|---|
| 661 |         _clear_table_memory(); | 
|---|
| 662 |         return sort(); | 
|---|
| 663 |     } | 
|---|
| 664 |  | 
|---|
| 665 |     //!If the container reaches the | 
|---|
| 666 |     bool check_for_switching_to_hashtable() | 
|---|
| 667 |     { | 
|---|
| 668 |         if(this->m_hash_table) return true; | 
|---|
| 669 |  | 
|---|
| 670 |         if(!(m_nodes.size()< m_min_hash_table_size)) | 
|---|
| 671 |         { | 
|---|
| 672 |             if(m_node_size == 0) | 
|---|
| 673 |             { | 
|---|
| 674 |                 m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE; | 
|---|
| 675 |             } | 
|---|
| 676 |  | 
|---|
| 677 |             _resize_table(m_nodes.size()+1); | 
|---|
| 678 |             return true; | 
|---|
| 679 |         } | 
|---|
| 680 |         return false; | 
|---|
| 681 |     } | 
|---|
| 682 |  | 
|---|
| 683 |     inline void set_sorted(bool value) | 
|---|
| 684 |     { | 
|---|
| 685 |         m_sorted = value; | 
|---|
| 686 |     } | 
|---|
| 687 |  | 
|---|
| 688 |     //! Retrieves the amount of keys. | 
|---|
| 689 |     inline GUINT size() const | 
|---|
| 690 |     { | 
|---|
| 691 |         return m_nodes.size(); | 
|---|
| 692 |     } | 
|---|
| 693 |  | 
|---|
| 694 |     //! Retrieves the hash key. | 
|---|
| 695 |     inline GUINT get_key(GUINT index) const | 
|---|
| 696 |     { | 
|---|
| 697 |         return m_nodes[index].m_key; | 
|---|
| 698 |     } | 
|---|
| 699 |  | 
|---|
| 700 |     //! Retrieves the value by index | 
|---|
| 701 |     /*! | 
|---|
| 702 |     */ | 
|---|
| 703 |     inline T * get_value_by_index(GUINT index) | 
|---|
| 704 |     { | 
|---|
| 705 |         return &m_nodes[index].m_data; | 
|---|
| 706 |     } | 
|---|
| 707 |  | 
|---|
| 708 |     inline const T& operator[](GUINT index) const | 
|---|
| 709 |     { | 
|---|
| 710 |         return m_nodes[index].m_data; | 
|---|
| 711 |     } | 
|---|
| 712 |  | 
|---|
| 713 |     inline T& operator[](GUINT index) | 
|---|
| 714 |     { | 
|---|
| 715 |         return m_nodes[index].m_data; | 
|---|
| 716 |     } | 
|---|
| 717 |  | 
|---|
| 718 |     //! Finds the index of the element with the key | 
|---|
| 719 |     /*! | 
|---|
| 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. | 
|---|
| 722 |     */ | 
|---|
| 723 |     inline GUINT find(GUINT hashkey) | 
|---|
| 724 |     { | 
|---|
| 725 |         if(m_hash_table) | 
|---|
| 726 |         { | 
|---|
| 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]; | 
|---|
| 730 |         } | 
|---|
| 731 |                 GUINT last_index = m_nodes.size(); | 
|---|
| 732 |         if(last_index<2) | 
|---|
| 733 |         { | 
|---|
| 734 |                         if(last_index==0) return GIM_INVALID_HASH; | 
|---|
| 735 |             if(m_nodes[0].m_key == hashkey) return 0; | 
|---|
| 736 |             return GIM_INVALID_HASH; | 
|---|
| 737 |         } | 
|---|
| 738 |         else if(m_sorted) | 
|---|
| 739 |         { | 
|---|
| 740 |             //Binary search | 
|---|
| 741 |             GUINT result_ind = 0; | 
|---|
| 742 |                         last_index--; | 
|---|
| 743 |             _node_type *  ptr =  m_nodes.pointer(); | 
|---|
| 744 |  | 
|---|
| 745 |             bool found = gim_binary_search_ex(ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO()); | 
|---|
| 746 |  | 
|---|
| 747 |  | 
|---|
| 748 |             if(found) return result_ind; | 
|---|
| 749 |         } | 
|---|
| 750 |         return GIM_INVALID_HASH; | 
|---|
| 751 |     } | 
|---|
| 752 |  | 
|---|
| 753 |     //! Retrieves the value associated with the index | 
|---|
| 754 |     /*! | 
|---|
| 755 |     \return the found element, or null | 
|---|
| 756 |     */ | 
|---|
| 757 |     inline T * get_value(GUINT hashkey) | 
|---|
| 758 |     { | 
|---|
| 759 |         GUINT index = find(hashkey); | 
|---|
| 760 |         if(index == GIM_INVALID_HASH) return NULL; | 
|---|
| 761 |         return &m_nodes[index].m_data; | 
|---|
| 762 |     } | 
|---|
| 763 |  | 
|---|
| 764 |  | 
|---|
| 765 |     /*! | 
|---|
| 766 |     */ | 
|---|
| 767 |     inline bool erase_by_index(GUINT index) | 
|---|
| 768 |     { | 
|---|
| 769 |         if(index > m_nodes.size()) return false; | 
|---|
| 770 |  | 
|---|
| 771 |         if(m_hash_table == NULL) | 
|---|
| 772 |         { | 
|---|
| 773 |             if(is_sorted()) | 
|---|
| 774 |             { | 
|---|
| 775 |                 return this->_erase_sorted(index); | 
|---|
| 776 |             } | 
|---|
| 777 |             else | 
|---|
| 778 |             { | 
|---|
| 779 |                 return this->_erase_unsorted(index); | 
|---|
| 780 |             } | 
|---|
| 781 |         } | 
|---|
| 782 |         else | 
|---|
| 783 |         { | 
|---|
| 784 |             return this->_erase_by_index_hash_table(index); | 
|---|
| 785 |         } | 
|---|
| 786 |         return false; | 
|---|
| 787 |     } | 
|---|
| 788 |  | 
|---|
| 789 |  | 
|---|
| 790 |  | 
|---|
| 791 |     inline bool erase_by_index_unsorted(GUINT index) | 
|---|
| 792 |     { | 
|---|
| 793 |         if(index > m_nodes.size()) return false; | 
|---|
| 794 |  | 
|---|
| 795 |         if(m_hash_table == NULL) | 
|---|
| 796 |         { | 
|---|
| 797 |             return this->_erase_unsorted(index); | 
|---|
| 798 |         } | 
|---|
| 799 |         else | 
|---|
| 800 |         { | 
|---|
| 801 |             return this->_erase_by_index_hash_table(index); | 
|---|
| 802 |         } | 
|---|
| 803 |         return false; | 
|---|
| 804 |     } | 
|---|
| 805 |  | 
|---|
| 806 |  | 
|---|
| 807 |  | 
|---|
| 808 |     /*! | 
|---|
| 809 |  | 
|---|
| 810 |     */ | 
|---|
| 811 |     inline bool erase_by_key(GUINT hashkey) | 
|---|
| 812 |     { | 
|---|
| 813 |         if(size()==0) return false; | 
|---|
| 814 |  | 
|---|
| 815 |         if(m_hash_table) | 
|---|
| 816 |         { | 
|---|
| 817 |             return this->_erase_hash_table(hashkey); | 
|---|
| 818 |         } | 
|---|
| 819 |         //Binary search | 
|---|
| 820 |  | 
|---|
| 821 |         if(is_sorted()==false) return false; | 
|---|
| 822 |  | 
|---|
| 823 |         GUINT result_ind = find(hashkey); | 
|---|
| 824 |         if(result_ind!= GIM_INVALID_HASH) | 
|---|
| 825 |         { | 
|---|
| 826 |             return this->_erase_sorted(result_ind); | 
|---|
| 827 |         } | 
|---|
| 828 |         return false; | 
|---|
| 829 |     } | 
|---|
| 830 |  | 
|---|
| 831 |     void clear() | 
|---|
| 832 |     { | 
|---|
| 833 |         m_nodes.clear(); | 
|---|
| 834 |  | 
|---|
| 835 |         if(m_hash_table==NULL) return; | 
|---|
| 836 |         GUINT datasize = m_table_size*m_node_size; | 
|---|
| 837 |         //Initialize the hashkeys. | 
|---|
| 838 |         GUINT i; | 
|---|
| 839 |         for(i=0;i<datasize;i++) | 
|---|
| 840 |         { | 
|---|
| 841 |             m_hash_table[i] = GIM_INVALID_HASH;// invalidate keys | 
|---|
| 842 |         } | 
|---|
| 843 |                 m_sorted = false; | 
|---|
| 844 |     } | 
|---|
| 845 |  | 
|---|
| 846 |     //! Insert an element into the hash | 
|---|
| 847 |     /*! | 
|---|
| 848 |     \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position | 
|---|
| 849 |     of the existing element. | 
|---|
| 850 |     */ | 
|---|
| 851 |     inline GUINT insert(GUINT hashkey, const T & element) | 
|---|
| 852 |     { | 
|---|
| 853 |         if(m_hash_table) | 
|---|
| 854 |         { | 
|---|
| 855 |             return this->_insert_hash_table(hashkey,element); | 
|---|
| 856 |         } | 
|---|
| 857 |         if(this->is_sorted()) | 
|---|
| 858 |         { | 
|---|
| 859 |             return this->_insert_sorted(hashkey,element); | 
|---|
| 860 |         } | 
|---|
| 861 |         return this->_insert_unsorted(hashkey,element); | 
|---|
| 862 |     } | 
|---|
| 863 |  | 
|---|
| 864 |     //! Insert an element into the hash, and could overrite an existing object with the same hash. | 
|---|
| 865 |     /*! | 
|---|
| 866 |     \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position | 
|---|
| 867 |     of the replaced element. | 
|---|
| 868 |     */ | 
|---|
| 869 |     inline GUINT insert_override(GUINT hashkey, const T & element) | 
|---|
| 870 |     { | 
|---|
| 871 |         if(m_hash_table) | 
|---|
| 872 |         { | 
|---|
| 873 |             return this->_insert_hash_table_replace(hashkey,element); | 
|---|
| 874 |         } | 
|---|
| 875 |         if(this->is_sorted()) | 
|---|
| 876 |         { | 
|---|
| 877 |             return this->_insert_sorted_replace(hashkey,element); | 
|---|
| 878 |         } | 
|---|
| 879 |         this->_insert_unsorted(hashkey,element); | 
|---|
| 880 |         return m_nodes.size(); | 
|---|
| 881 |     } | 
|---|
| 882 |  | 
|---|
| 883 |  | 
|---|
| 884 |  | 
|---|
| 885 |     //! Insert an element into the hash,But if this container is a sorted array, this inserts it unsorted | 
|---|
| 886 |     /*! | 
|---|
| 887 |     */ | 
|---|
| 888 |     inline GUINT insert_unsorted(GUINT hashkey,const T & element) | 
|---|
| 889 |     { | 
|---|
| 890 |         if(m_hash_table) | 
|---|
| 891 |         { | 
|---|
| 892 |             return this->_insert_hash_table(hashkey,element); | 
|---|
| 893 |         } | 
|---|
| 894 |         return this->_insert_unsorted(hashkey,element); | 
|---|
| 895 |     } | 
|---|
| 896 |  | 
|---|
| 897 |  | 
|---|
| 898 | }; | 
|---|
| 899 |  | 
|---|
| 900 |  | 
|---|
| 901 |  | 
|---|
| 902 | #endif // GIM_CONTAINERS_H_INCLUDED | 
|---|