forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			903 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			903 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef GIM_HASH_TABLE_H_INCLUDED
 | |
| #define GIM_HASH_TABLE_H_INCLUDED
 | |
| /*! \file gim_trimesh_data.h
 | |
| \author Francisco Leon Najera
 | |
| */
 | |
| /*
 | |
| -----------------------------------------------------------------------------
 | |
| This source file is part of GIMPACT Library.
 | |
| 
 | |
| For the latest info, see http://gimpact.sourceforge.net/
 | |
| 
 | |
| Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371.
 | |
| email: projectileman@yahoo.com
 | |
| 
 | |
|  This library is free software; you can redistribute it and/or
 | |
|  modify it under the terms of EITHER:
 | |
|    (1) The GNU Lesser General Public License as published by the Free
 | |
|        Software Foundation; either version 2.1 of the License, or (at
 | |
|        your option) any later version. The text of the GNU Lesser
 | |
|        General Public License is included with this library in the
 | |
|        file GIMPACT-LICENSE-LGPL.TXT.
 | |
|    (2) The BSD-style license that is included with this library in
 | |
|        the file GIMPACT-LICENSE-BSD.TXT.
 | |
|    (3) The zlib/libpng license that is included with this library in
 | |
|        the file GIMPACT-LICENSE-ZLIB.TXT.
 | |
| 
 | |
|  This library is distributed in the hope that it will be useful,
 | |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
 | |
|  GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details.
 | |
| 
 | |
| -----------------------------------------------------------------------------
 | |
| */
 | |
| 
 | |
| #include "gim_radixsort.h"
 | |
| 
 | |
| 
 | |
| #define GIM_INVALID_HASH 0xffffffff //!< A very very high value
 | |
| #define GIM_DEFAULT_HASH_TABLE_SIZE 380
 | |
| #define GIM_DEFAULT_HASH_TABLE_NODE_SIZE 4
 | |
| #define GIM_HASH_TABLE_GROW_FACTOR 2
 | |
| 
 | |
| #define GIM_MIN_RADIX_SORT_SIZE 860 //!< calibrated on a PIII
 | |
| 
 | |
| template<typename T>
 | |
| struct GIM_HASH_TABLE_NODE
 | |
| {
 | |
|     GUINT m_key;
 | |
|     T m_data;
 | |
|     GIM_HASH_TABLE_NODE()
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     GIM_HASH_TABLE_NODE(const GIM_HASH_TABLE_NODE & value)
 | |
|     {
 | |
|         m_key = value.m_key;
 | |
|         m_data = value.m_data;
 | |
|     }
 | |
| 
 | |
|     GIM_HASH_TABLE_NODE(GUINT key, const T & data)
 | |
|     {
 | |
|         m_key = key;
 | |
|         m_data = data;
 | |
|     }
 | |
| 
 | |
|     bool operator <(const GIM_HASH_TABLE_NODE<T> & other) const
 | |
| 	{
 | |
| 		///inverse order, further objects are first
 | |
| 		if(m_key <  other.m_key) return true;
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	bool operator >(const GIM_HASH_TABLE_NODE<T> & other) const
 | |
| 	{
 | |
| 		///inverse order, further objects are first
 | |
| 		if(m_key >  other.m_key) return true;
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	bool operator ==(const GIM_HASH_TABLE_NODE<T> & other) const
 | |
| 	{
 | |
| 		///inverse order, further objects are first
 | |
| 		if(m_key ==  other.m_key) return true;
 | |
| 		return false;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| ///Macro for getting the key
 | |
| class GIM_HASH_NODE_GET_KEY
 | |
| {
 | |
| public:
 | |
| 	template<class T>
 | |
| 	inline GUINT operator()( const T& a)
 | |
| 	{
 | |
| 		return a.m_key;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| ///Macro for comparing the key and the element
 | |
| class GIM_HASH_NODE_CMP_KEY_MACRO
 | |
| {
 | |
| public:
 | |
| 	template<class T>
 | |
| 	inline int operator() ( const T& a, GUINT key)
 | |
| 	{
 | |
| 		return ((int)(a.m_key - key));
 | |
| 	}
 | |
| };
 | |
| 
 | |
| ///Macro for comparing Hash nodes
 | |
| class GIM_HASH_NODE_CMP_MACRO
 | |
| {
 | |
| public:
 | |
| 	template<class T>
 | |
| 	inline int operator() ( const T& a, const T& b )
 | |
| 	{
 | |
| 		return ((int)(a.m_key - b.m_key));
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| //! Sorting for hash table
 | |
| /*!
 | |
| switch automatically between quicksort and radixsort
 | |
| */
 | |
| template<typename T>
 | |
| void gim_sort_hash_node_array(T * array, GUINT array_count)
 | |
| {
 | |
|     if(array_count<GIM_MIN_RADIX_SORT_SIZE)
 | |
|     {
 | |
|     	gim_heap_sort(array,array_count,GIM_HASH_NODE_CMP_MACRO());
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|     	memcopy_elements_func cmpfunc;
 | |
|     	gim_radix_sort(array,array_count,GIM_HASH_NODE_GET_KEY(),cmpfunc);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| // Note: assumes long is at least 32 bits.
 | |
| #define GIM_NUM_PRIME 28
 | |
| 
 | |
| static const GUINT gim_prime_list[GIM_NUM_PRIME] =
 | |
| {
 | |
|   53ul,         97ul,         193ul,       389ul,       769ul,
 | |
|   1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
 | |
|   49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
 | |
|   1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
 | |
|   50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
 | |
|   1610612741ul, 3221225473ul, 4294967291ul
 | |
| };
 | |
| 
 | |
| inline GUINT gim_next_prime(GUINT number)
 | |
| {
 | |
|     //Find nearest upper prime
 | |
|     GUINT result_ind = 0;
 | |
|     gim_binary_search(gim_prime_list,0,(GIM_NUM_PRIME-2),number,result_ind);
 | |
| 
 | |
|     // inv: result_ind < 28
 | |
|     return gim_prime_list[result_ind];
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| //! A compact hash table implementation
 | |
| /*!
 | |
| A memory aligned compact hash table that coud be treated as an array.
 | |
| It could be a simple sorted array without the overhead of the hash key bucked, or could
 | |
| be a formely hash table with an array of keys.
 | |
| You can use switch_to_hashtable() and switch_to_sorted_array for saving space or increase speed.
 | |
| </br>
 | |
| 
 | |
| <ul>
 | |
| <li> if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes.
 | |
| 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.
 | |
| <li> If node_size != 0, then this container becomes a hash table for ever
 | |
| </ul>
 | |
| 
 | |
| */
 | |
| template<class T>
 | |
| class gim_hash_table
 | |
| {
 | |
| protected:
 | |
|     typedef GIM_HASH_TABLE_NODE<T> _node_type;
 | |
| 
 | |
|     //!The nodes
 | |
|     //array< _node_type, SuperAllocator<_node_type> > m_nodes;
 | |
|     gim_array< _node_type > m_nodes;
 | |
|     //SuperBufferedArray< _node_type > m_nodes;
 | |
|     bool m_sorted;
 | |
| 
 | |
|     ///Hash table data management. The hash table has the indices to the corresponding m_nodes array
 | |
|     GUINT * m_hash_table;//!<
 | |
|     GUINT m_table_size;//!<
 | |
|     GUINT m_node_size;//!<
 | |
|     GUINT m_min_hash_table_size;
 | |
| 
 | |
| 
 | |
| 
 | |
|     //! Returns the cell index
 | |
|     inline GUINT _find_cell(GUINT hashkey)
 | |
|     {
 | |
|         _node_type * nodesptr = m_nodes.pointer();
 | |
|         GUINT start_index = (hashkey%m_table_size)*m_node_size;
 | |
|         GUINT end_index = start_index + m_node_size;
 | |
| 
 | |
|         while(start_index<end_index)
 | |
|         {
 | |
|             GUINT value = m_hash_table[start_index];
 | |
|             if(value != GIM_INVALID_HASH)
 | |
|             {
 | |
|                 if(nodesptr[value].m_key == hashkey) return start_index;
 | |
|             }
 | |
|             start_index++;
 | |
|         }
 | |
|         return GIM_INVALID_HASH;
 | |
|     }
 | |
| 
 | |
|     //! Find the avaliable cell for the hashkey, and return an existing cell if it has the same hash key
 | |
|     inline GUINT _find_avaliable_cell(GUINT hashkey)
 | |
|     {
 | |
|         _node_type * nodesptr = m_nodes.pointer();
 | |
|         GUINT avaliable_index = GIM_INVALID_HASH;
 | |
|         GUINT start_index = (hashkey%m_table_size)*m_node_size;
 | |
|         GUINT end_index = start_index + m_node_size;
 | |
| 
 | |
|         while(start_index<end_index)
 | |
|         {
 | |
|             GUINT value = m_hash_table[start_index];
 | |
|             if(value == GIM_INVALID_HASH)
 | |
|             {
 | |
|                 if(avaliable_index==GIM_INVALID_HASH)
 | |
|                 {
 | |
|                     avaliable_index = start_index;
 | |
|                 }
 | |
|             }
 | |
|             else if(nodesptr[value].m_key == hashkey)
 | |
|             {
 | |
|                 return start_index;
 | |
|             }
 | |
|             start_index++;
 | |
|         }
 | |
|         return avaliable_index;
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     //! reserves the memory for the hash table.
 | |
|     /*!
 | |
|     \pre hash table must be empty
 | |
|     \post reserves the memory for the hash table, an initializes all elements to GIM_INVALID_HASH.
 | |
|     */
 | |
|     inline void _reserve_table_memory(GUINT newtablesize)
 | |
|     {
 | |
|         if(newtablesize==0) return;
 | |
|         if(m_node_size==0) return;
 | |
| 
 | |
|         //Get a Prime size
 | |
| 
 | |
|         m_table_size = gim_next_prime(newtablesize);
 | |
| 
 | |
|         GUINT datasize = m_table_size*m_node_size;
 | |
|         //Alloc the data buffer
 | |
|         m_hash_table =  (GUINT *)gim_alloc(datasize*sizeof(GUINT));
 | |
|     }
 | |
| 
 | |
|     inline void _invalidate_keys()
 | |
|     {
 | |
|         GUINT datasize = m_table_size*m_node_size;
 | |
|         for(GUINT i=0;i<datasize;i++)
 | |
|         {
 | |
|             m_hash_table[i] = GIM_INVALID_HASH;// invalidate keys
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //! Clear all memory for the hash table
 | |
|     inline void _clear_table_memory()
 | |
|     {
 | |
|         if(m_hash_table==NULL) return;
 | |
|         gim_free(m_hash_table);
 | |
|         m_hash_table = NULL;
 | |
|         m_table_size = 0;
 | |
|     }
 | |
| 
 | |
|     //! Invalidates the keys (Assigning GIM_INVALID_HASH to all) Reorders the hash keys
 | |
|     inline void _rehash()
 | |
|     {
 | |
|         _invalidate_keys();
 | |
| 
 | |
|         _node_type * nodesptr = m_nodes.pointer();
 | |
|         for(GUINT i=0;i<(GUINT)m_nodes.size();i++)
 | |
|         {
 | |
|             GUINT nodekey = nodesptr[i].m_key;
 | |
|             if(nodekey != GIM_INVALID_HASH)
 | |
|             {
 | |
|                 //Search for the avaliable cell in buffer
 | |
|                 GUINT index = _find_avaliable_cell(nodekey);
 | |
| 
 | |
| 
 | |
| 				if(m_hash_table[index]!=GIM_INVALID_HASH)
 | |
| 				{//The new index is alreade used... discard this new incomming object, repeated key
 | |
| 				    btAssert(m_hash_table[index]==nodekey);
 | |
| 					nodesptr[i].m_key = GIM_INVALID_HASH;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					//;
 | |
| 					//Assign the value for alloc
 | |
| 					m_hash_table[index] = i;
 | |
| 				}
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //! Resize hash table indices
 | |
|     inline void _resize_table(GUINT newsize)
 | |
|     {
 | |
|         //Clear memory
 | |
|         _clear_table_memory();
 | |
|         //Alloc the data
 | |
|         _reserve_table_memory(newsize);
 | |
|         //Invalidate keys and rehash
 | |
|         _rehash();
 | |
|     }
 | |
| 
 | |
|     //! Destroy hash table memory
 | |
|     inline void _destroy()
 | |
|     {
 | |
|         if(m_hash_table==NULL) return;
 | |
|         _clear_table_memory();
 | |
|     }
 | |
| 
 | |
|     //! Finds an avaliable hash table cell, and resizes the table if there isn't space
 | |
|     inline GUINT _assign_hash_table_cell(GUINT hashkey)
 | |
|     {
 | |
|         GUINT cell_index = _find_avaliable_cell(hashkey);
 | |
| 
 | |
|         if(cell_index==GIM_INVALID_HASH)
 | |
|         {
 | |
|             //rehashing
 | |
|             _resize_table(m_table_size+1);
 | |
|             GUINT cell_index = _find_avaliable_cell(hashkey);
 | |
|             btAssert(cell_index!=GIM_INVALID_HASH);
 | |
|         }
 | |
|         return cell_index;
 | |
|     }
 | |
| 
 | |
|     //! erase by index in hash table
 | |
|     inline bool _erase_by_index_hash_table(GUINT index)
 | |
|     {
 | |
|         if(index >= m_nodes.size()) return false;
 | |
|         if(m_nodes[index].m_key != GIM_INVALID_HASH)
 | |
|         {
 | |
|             //Search for the avaliable cell in buffer
 | |
|             GUINT cell_index = _find_cell(m_nodes[index].m_key);
 | |
| 
 | |
|             btAssert(cell_index!=GIM_INVALID_HASH);
 | |
|             btAssert(m_hash_table[cell_index]==index);
 | |
| 
 | |
|             m_hash_table[cell_index] = GIM_INVALID_HASH;
 | |
|         }
 | |
| 
 | |
|         return this->_erase_unsorted(index);
 | |
|     }
 | |
| 
 | |
|     //! erase by key in hash table
 | |
|     inline bool _erase_hash_table(GUINT hashkey)
 | |
|     {
 | |
|         if(hashkey == GIM_INVALID_HASH) return false;
 | |
| 
 | |
|         //Search for the avaliable cell in buffer
 | |
|         GUINT cell_index = _find_cell(hashkey);
 | |
|         if(cell_index ==GIM_INVALID_HASH) return false;
 | |
| 
 | |
|         GUINT index = m_hash_table[cell_index];
 | |
|         m_hash_table[cell_index] = GIM_INVALID_HASH;
 | |
| 
 | |
|         return this->_erase_unsorted(index);
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     //! insert an element in hash table
 | |
|     /*!
 | |
|     If the element exists, this won't insert the element
 | |
|     \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
 | |
|     If so, the element has been inserted at the last position of the array.
 | |
|     */
 | |
|     inline GUINT _insert_hash_table(GUINT hashkey, const T & value)
 | |
|     {
 | |
|         if(hashkey==GIM_INVALID_HASH)
 | |
|         {
 | |
|             //Insert anyway
 | |
|             _insert_unsorted(hashkey,value);
 | |
|             return GIM_INVALID_HASH;
 | |
|         }
 | |
| 
 | |
|         GUINT cell_index = _assign_hash_table_cell(hashkey);
 | |
| 
 | |
|         GUINT value_key = m_hash_table[cell_index];
 | |
| 
 | |
|         if(value_key!= GIM_INVALID_HASH) return value_key;// Not overrited
 | |
| 
 | |
|         m_hash_table[cell_index] = m_nodes.size();
 | |
| 
 | |
|         _insert_unsorted(hashkey,value);
 | |
|         return GIM_INVALID_HASH;
 | |
|     }
 | |
| 
 | |
|     //! insert an element in hash table.
 | |
|     /*!
 | |
|     If the element exists, this replaces the element.
 | |
|     \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
 | |
|     If so, the element has been inserted at the last position of the array.
 | |
|     */
 | |
|     inline GUINT _insert_hash_table_replace(GUINT hashkey, const T & value)
 | |
|     {
 | |
|         if(hashkey==GIM_INVALID_HASH)
 | |
|         {
 | |
|             //Insert anyway
 | |
|             _insert_unsorted(hashkey,value);
 | |
|             return GIM_INVALID_HASH;
 | |
|         }
 | |
| 
 | |
|         GUINT cell_index = _assign_hash_table_cell(hashkey);
 | |
| 
 | |
|         GUINT value_key = m_hash_table[cell_index];
 | |
| 
 | |
|         if(value_key!= GIM_INVALID_HASH)
 | |
|         {//replaces the existing
 | |
|             m_nodes[value_key] = _node_type(hashkey,value);
 | |
|             return value_key;// index of the replaced element
 | |
|         }
 | |
| 
 | |
|         m_hash_table[cell_index] = m_nodes.size();
 | |
| 
 | |
|         _insert_unsorted(hashkey,value);
 | |
|         return GIM_INVALID_HASH;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     
 | |
|     ///Sorted array data management. The hash table has the indices to the corresponding m_nodes array
 | |
|     inline bool _erase_sorted(GUINT index)
 | |
|     {
 | |
|         if(index>=(GUINT)m_nodes.size()) return false;
 | |
|         m_nodes.erase_sorted(index);
 | |
| 		if(m_nodes.size()<2) m_sorted = false;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     //! faster, but unsorted
 | |
|     inline bool _erase_unsorted(GUINT index)
 | |
|     {
 | |
|         if(index>=m_nodes.size()) return false;
 | |
| 
 | |
|         GUINT lastindex = m_nodes.size()-1;
 | |
|         if(index<lastindex && m_hash_table!=0)
 | |
|         {
 | |
| 			GUINT hashkey =  m_nodes[lastindex].m_key;
 | |
| 			if(hashkey!=GIM_INVALID_HASH)
 | |
| 			{
 | |
| 				//update the new position of the last element
 | |
| 				GUINT cell_index = _find_cell(hashkey);
 | |
| 				btAssert(cell_index!=GIM_INVALID_HASH);
 | |
| 				//new position of the last element which will be swaped
 | |
| 				m_hash_table[cell_index] = index;
 | |
| 			}
 | |
|         }
 | |
|         m_nodes.erase(index);
 | |
|         m_sorted = false;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     //! Insert in position ordered
 | |
|     /*!
 | |
|     Also checks if it is needed to transform this container to a hash table, by calling check_for_switching_to_hashtable
 | |
|     */
 | |
|     inline void _insert_in_pos(GUINT hashkey, const T & value, GUINT pos)
 | |
|     {
 | |
|         m_nodes.insert(_node_type(hashkey,value),pos);
 | |
|         this->check_for_switching_to_hashtable();
 | |
|     }
 | |
| 
 | |
|     //! Insert an element in an ordered array
 | |
|     inline GUINT _insert_sorted(GUINT hashkey, const T & value)
 | |
|     {
 | |
|         if(hashkey==GIM_INVALID_HASH || size()==0)
 | |
|         {
 | |
|             m_nodes.push_back(_node_type(hashkey,value));
 | |
|             return GIM_INVALID_HASH;
 | |
|         }
 | |
|         //Insert at last position
 | |
|         //Sort element
 | |
| 
 | |
| 
 | |
|         GUINT result_ind=0;
 | |
|         GUINT last_index = m_nodes.size()-1;
 | |
|         _node_type * ptr = m_nodes.pointer();
 | |
| 
 | |
|         bool found = gim_binary_search_ex(
 | |
|         	ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
 | |
| 
 | |
| 
 | |
|         //Insert before found index
 | |
|         if(found)
 | |
|         {
 | |
|             return result_ind;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             _insert_in_pos(hashkey, value, result_ind);
 | |
|         }
 | |
|         return GIM_INVALID_HASH;
 | |
|     }
 | |
| 
 | |
|     inline GUINT _insert_sorted_replace(GUINT hashkey, const T & value)
 | |
|     {
 | |
|         if(hashkey==GIM_INVALID_HASH || size()==0)
 | |
|         {
 | |
|             m_nodes.push_back(_node_type(hashkey,value));
 | |
|             return GIM_INVALID_HASH;
 | |
|         }
 | |
|         //Insert at last position
 | |
|         //Sort element
 | |
|         GUINT result_ind;
 | |
|         GUINT last_index = m_nodes.size()-1;
 | |
|         _node_type * ptr = m_nodes.pointer();
 | |
| 
 | |
|         bool found = gim_binary_search_ex(
 | |
|         	ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
 | |
| 
 | |
|         //Insert before found index
 | |
|         if(found)
 | |
|         {
 | |
|             m_nodes[result_ind] = _node_type(hashkey,value);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             _insert_in_pos(hashkey, value, result_ind);
 | |
|         }
 | |
|         return result_ind;
 | |
|     }
 | |
| 
 | |
|     //! Fast insertion in m_nodes array
 | |
|     inline GUINT  _insert_unsorted(GUINT hashkey, const T & value)
 | |
|     {
 | |
|         m_nodes.push_back(_node_type(hashkey,value));
 | |
|         m_sorted = false;
 | |
|         return GIM_INVALID_HASH;
 | |
|     }
 | |
| 
 | |
|     
 | |
| 
 | |
| public:
 | |
| 
 | |
|     /*!
 | |
|         <li> if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes.
 | |
|         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.
 | |
|         <li> If node_size != 0, then this container becomes a hash table for ever
 | |
|         </ul>
 | |
|     */
 | |
|     gim_hash_table(GUINT reserve_size = GIM_DEFAULT_HASH_TABLE_SIZE,
 | |
|                      GUINT node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE,
 | |
|                      GUINT min_hash_table_size = GIM_INVALID_HASH)
 | |
|     {
 | |
|         m_hash_table = NULL;
 | |
|         m_table_size = 0;
 | |
|         m_sorted = false;
 | |
|         m_node_size = node_size;
 | |
|         m_min_hash_table_size = min_hash_table_size;
 | |
| 
 | |
|         if(m_node_size!=0)
 | |
|         {
 | |
|             if(reserve_size!=0)
 | |
|             {
 | |
|                 m_nodes.reserve(reserve_size);
 | |
|                 _reserve_table_memory(reserve_size);
 | |
|                 _invalidate_keys();
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 m_nodes.reserve(GIM_DEFAULT_HASH_TABLE_SIZE);
 | |
|                 _reserve_table_memory(GIM_DEFAULT_HASH_TABLE_SIZE);
 | |
|                 _invalidate_keys();
 | |
|             }
 | |
|         }
 | |
|         else if(reserve_size!=0)
 | |
|         {
 | |
|             m_nodes.reserve(reserve_size);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     ~gim_hash_table()
 | |
|     {
 | |
|         _destroy();
 | |
|     }
 | |
| 
 | |
|     inline bool is_hash_table()
 | |
|     {
 | |
|         if(m_hash_table) return true;
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     inline bool is_sorted()
 | |
|     {
 | |
|         if(size()<2) return true;
 | |
|         return m_sorted;
 | |
|     }
 | |
| 
 | |
|     bool sort()
 | |
|     {
 | |
|         if(is_sorted()) return true;
 | |
|         if(m_nodes.size()<2) return false;
 | |
| 
 | |
| 
 | |
|         _node_type * ptr = m_nodes.pointer();
 | |
|         GUINT siz = m_nodes.size();
 | |
|         gim_sort_hash_node_array(ptr,siz);
 | |
|         m_sorted=true;
 | |
| 
 | |
| 
 | |
| 
 | |
|         if(m_hash_table)
 | |
|         {
 | |
|             _rehash();
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool switch_to_hashtable()
 | |
|     {
 | |
|         if(m_hash_table) return false;
 | |
|         if(m_node_size==0) m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
 | |
|         if(m_nodes.size()<GIM_DEFAULT_HASH_TABLE_SIZE)
 | |
|         {
 | |
|             _resize_table(GIM_DEFAULT_HASH_TABLE_SIZE);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             _resize_table(m_nodes.size()+1);
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool switch_to_sorted_array()
 | |
|     {
 | |
|         if(m_hash_table==NULL) return true;
 | |
|         _clear_table_memory();
 | |
|         return sort();
 | |
|     }
 | |
| 
 | |
|     //!If the container reaches the
 | |
|     bool check_for_switching_to_hashtable()
 | |
|     {
 | |
|         if(this->m_hash_table) return true;
 | |
| 
 | |
|         if(!(m_nodes.size()< m_min_hash_table_size))
 | |
|         {
 | |
|             if(m_node_size == 0)
 | |
|             {
 | |
|                 m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE;
 | |
|             }
 | |
| 
 | |
|             _resize_table(m_nodes.size()+1);
 | |
|             return true;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     inline void set_sorted(bool value)
 | |
|     {
 | |
|     	m_sorted = value;
 | |
|     }
 | |
| 
 | |
|     //! Retrieves the amount of keys.
 | |
|     inline GUINT size() const
 | |
|     {
 | |
|         return m_nodes.size();
 | |
|     }
 | |
| 
 | |
|     //! Retrieves the hash key.
 | |
|     inline GUINT get_key(GUINT index) const
 | |
|     {
 | |
|         return m_nodes[index].m_key;
 | |
|     }
 | |
| 
 | |
|     //! Retrieves the value by index
 | |
|     /*!
 | |
|     */
 | |
|     inline T * get_value_by_index(GUINT index)
 | |
|     {
 | |
|         return &m_nodes[index].m_data;
 | |
|     }
 | |
| 
 | |
|     inline const T& operator[](GUINT index) const
 | |
|     {
 | |
|         return m_nodes[index].m_data;
 | |
|     }
 | |
| 
 | |
|     inline T& operator[](GUINT index)
 | |
|     {
 | |
|         return m_nodes[index].m_data;
 | |
|     }
 | |
| 
 | |
|     //! Finds the index of the element with the key
 | |
|     /*!
 | |
|     \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted
 | |
|     If so, the element has been inserted at the last position of the array.
 | |
|     */
 | |
|     inline GUINT find(GUINT hashkey)
 | |
|     {
 | |
|         if(m_hash_table)
 | |
|         {
 | |
|             GUINT cell_index = _find_cell(hashkey);
 | |
|             if(cell_index==GIM_INVALID_HASH) return GIM_INVALID_HASH;
 | |
|             return m_hash_table[cell_index];
 | |
|         }
 | |
| 		GUINT last_index = m_nodes.size();
 | |
|         if(last_index<2)
 | |
|         {
 | |
| 			if(last_index==0) return GIM_INVALID_HASH;
 | |
|             if(m_nodes[0].m_key == hashkey) return 0;
 | |
|             return GIM_INVALID_HASH;
 | |
|         }
 | |
|         else if(m_sorted)
 | |
|         {
 | |
|             //Binary search
 | |
|             GUINT result_ind = 0;
 | |
| 			last_index--;
 | |
|             _node_type *  ptr =  m_nodes.pointer();
 | |
| 
 | |
|             bool found = gim_binary_search_ex(ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO());
 | |
| 
 | |
| 
 | |
|             if(found) return result_ind;
 | |
|         }
 | |
|         return GIM_INVALID_HASH;
 | |
|     }
 | |
| 
 | |
|     //! Retrieves the value associated with the index
 | |
|     /*!
 | |
|     \return the found element, or null
 | |
|     */
 | |
|     inline T * get_value(GUINT hashkey)
 | |
|     {
 | |
|         GUINT index = find(hashkey);
 | |
|         if(index == GIM_INVALID_HASH) return NULL;
 | |
|         return &m_nodes[index].m_data;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /*!
 | |
|     */
 | |
|     inline bool erase_by_index(GUINT index)
 | |
|     {
 | |
|         if(index > m_nodes.size()) return false;
 | |
| 
 | |
|         if(m_hash_table == NULL)
 | |
|         {
 | |
|             if(is_sorted())
 | |
|             {
 | |
|                 return this->_erase_sorted(index);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return this->_erase_unsorted(index);
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             return this->_erase_by_index_hash_table(index);
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     inline bool erase_by_index_unsorted(GUINT index)
 | |
|     {
 | |
|         if(index > m_nodes.size()) return false;
 | |
| 
 | |
|         if(m_hash_table == NULL)
 | |
|         {
 | |
|             return this->_erase_unsorted(index);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             return this->_erase_by_index_hash_table(index);
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     /*!
 | |
| 
 | |
|     */
 | |
|     inline bool erase_by_key(GUINT hashkey)
 | |
|     {
 | |
|         if(size()==0) return false;
 | |
| 
 | |
|         if(m_hash_table)
 | |
|         {
 | |
|             return this->_erase_hash_table(hashkey);
 | |
|         }
 | |
|         //Binary search
 | |
| 
 | |
|         if(is_sorted()==false) return false;
 | |
| 
 | |
|         GUINT result_ind = find(hashkey);
 | |
|         if(result_ind!= GIM_INVALID_HASH)
 | |
|         {
 | |
|             return this->_erase_sorted(result_ind);
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     void clear()
 | |
|     {
 | |
|         m_nodes.clear();
 | |
| 
 | |
|         if(m_hash_table==NULL) return;
 | |
|         GUINT datasize = m_table_size*m_node_size;
 | |
|         //Initialize the hashkeys.
 | |
|         GUINT i;
 | |
|         for(i=0;i<datasize;i++)
 | |
|         {
 | |
|             m_hash_table[i] = GIM_INVALID_HASH;// invalidate keys
 | |
|         }
 | |
| 		m_sorted = false;
 | |
|     }
 | |
| 
 | |
|     //! Insert an element into the hash
 | |
|     /*!
 | |
|     \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position
 | |
|     of the existing element.
 | |
|     */
 | |
|     inline GUINT insert(GUINT hashkey, const T & element)
 | |
|     {
 | |
|         if(m_hash_table)
 | |
|         {
 | |
|             return this->_insert_hash_table(hashkey,element);
 | |
|         }
 | |
|         if(this->is_sorted())
 | |
|         {
 | |
|             return this->_insert_sorted(hashkey,element);
 | |
|         }
 | |
|         return this->_insert_unsorted(hashkey,element);
 | |
|     }
 | |
| 
 | |
|     //! Insert an element into the hash, and could overrite an existing object with the same hash.
 | |
|     /*!
 | |
|     \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position
 | |
|     of the replaced element.
 | |
|     */
 | |
|     inline GUINT insert_override(GUINT hashkey, const T & element)
 | |
|     {
 | |
|         if(m_hash_table)
 | |
|         {
 | |
|             return this->_insert_hash_table_replace(hashkey,element);
 | |
|         }
 | |
|         if(this->is_sorted())
 | |
|         {
 | |
|             return this->_insert_sorted_replace(hashkey,element);
 | |
|         }
 | |
|         this->_insert_unsorted(hashkey,element);
 | |
|         return m_nodes.size();
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     //! Insert an element into the hash,But if this container is a sorted array, this inserts it unsorted
 | |
|     /*!
 | |
|     */
 | |
|     inline GUINT insert_unsorted(GUINT hashkey,const T & element)
 | |
|     {
 | |
|         if(m_hash_table)
 | |
|         {
 | |
|             return this->_insert_hash_table(hashkey,element);
 | |
|         }
 | |
|         return this->_insert_unsorted(hashkey,element);
 | |
|     }
 | |
| 
 | |
| 
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| #endif // GIM_CONTAINERS_H_INCLUDED
 |