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
 |