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