// Copyright (C) 2007 Id Software, Inc. // #ifndef __HASHTABLE_H__ #define __HASHTABLE_H__ /* =============================================================================== General hash table. Slower than idHashIndex but it can also be used for linked lists and other data structures than just indexes or arrays. =============================================================================== */ template< class Type > class idHashTable { public: idHashTable( int newtablesize = 256 ); idHashTable( const idHashTable &map ); ~idHashTable( void ); // returns total size of allocated memory size_t Allocated( void ) const; // returns total size of allocated memory including size of hash table type size_t Size( void ) const; const Type& Set( const char *key, const Type &value ); bool Get( const char *key, Type **value = NULL ) const; bool Remove( const char *key ); void Clear( void ); void DeleteContents( void ); // the entire contents can be itterated over, but note that the // exact index for a given element may change when new elements are added int Num( void ) const; Type * GetIndex( int index ) const; idStr GetIndexKey( int index ) const; int GetSpread( void ) const; void Swap( idHashTable& rhs ); private: idHashTable< Type >& operator=( const idHashTable< Type >& rhs ); private: struct hashnode_s { idStr key; Type value; hashnode_s *next; hashnode_s( const idStr &k, Type v, hashnode_s *n ) : key( k ), value( v ), next( n ) {}; hashnode_s( const char *k, Type v, hashnode_s *n ) : key( k ), value( v ), next( n ) {}; }; hashnode_s ** heads; int tablesize; int numentries; int tablesizemask; int GetHash( const char *key ) const; }; /* ================ idHashTable::idHashTable ================ */ template< class Type > ID_INLINE idHashTable::idHashTable( int newtablesize ) { assert( idMath::IsPowerOfTwo( newtablesize ) ); tablesize = newtablesize; assert( tablesize > 0 ); heads = new hashnode_s *[ tablesize ]; memset( heads, 0, sizeof( *heads ) * tablesize ); numentries = 0; tablesizemask = tablesize - 1; } /* ================ idHashTable::idHashTable ================ */ template< class Type > ID_INLINE idHashTable::idHashTable( const idHashTable &map ) { int i; hashnode_s *node; hashnode_s **prev; assert( map.tablesize > 0 ); tablesize = map.tablesize; heads = new hashnode_s *[ tablesize ]; numentries = map.numentries; tablesizemask = map.tablesizemask; for( i = 0; i < tablesize; i++ ) { if ( !map.heads[ i ] ) { heads[ i ] = NULL; continue; } prev = &heads[ i ]; for( node = map.heads[ i ]; node != NULL; node = node->next ) { *prev = new hashnode_s( node->key, node->value, NULL ); prev = &( *prev )->next; } } } /* ================ idHashTable::~idHashTable ================ */ template< class Type > ID_INLINE idHashTable::~idHashTable( void ) { Clear(); delete[] heads; heads = NULL; } /* ================ idHashTable::Allocated ================ */ template< class Type > ID_INLINE size_t idHashTable::Allocated( void ) const { return sizeof( heads ) * tablesize + sizeof( *heads ) * numentries; } /* ================ idHashTable::Size ================ */ template< class Type > ID_INLINE size_t idHashTable::Size( void ) const { return sizeof( idHashTable ) + sizeof( heads ) * tablesize + sizeof( *heads ) * numentries; } /* ================ idHashTable::GetHash ================ */ template< class Type > ID_INLINE int idHashTable::GetHash( const char *key ) const { return ( idStr::Hash( key ) & tablesizemask ); } /* ================ idHashTable::Set ================ */ template< class Type > ID_INLINE const Type& idHashTable::Set( const char *key, const Type &value ) { hashnode_s *node, **nextPtr; int hash, s; hash = GetHash( key ); for( nextPtr = &(heads[hash]), node = *nextPtr; node != NULL; nextPtr = &(node->next), node = *nextPtr ) { s = node->key.Cmp( key ); if ( s == 0 ) { node->value = value; return node->value; } if ( s > 0 ) { break; } } numentries++; *nextPtr = new hashnode_s( key, value, heads[ hash ] ); (*nextPtr)->next = node; return (*nextPtr)->value; } /* ================ idHashTable::Get ================ */ template< class Type > ID_INLINE bool idHashTable::Get( const char *key, Type **value ) const { hashnode_s *node; int hash, s; hash = GetHash( key ); for( node = heads[ hash ]; node != NULL; node = node->next ) { s = node->key.Cmp( key ); if ( s == 0 ) { if ( value ) { *value = &node->value; } return true; } if ( s > 0 ) { break; } } if ( value ) { *value = NULL; } return false; } /* ================ idHashTable::GetIndex the entire contents can be iterated over, but note that the exact index for a given element may change when new elements are added ================ */ template< class Type > ID_INLINE Type *idHashTable::GetIndex( int index ) const { hashnode_s *node; int count; int i; if ( ( index < 0 ) || ( index > numentries ) ) { assert( 0 ); return NULL; } count = 0; for( i = 0; i < tablesize; i++ ) { for( node = heads[ i ]; node != NULL; node = node->next ) { if ( count == index ) { return &node->value; } count++; } } return NULL; } /* ================ idHashTable::GetIndexKey the entire contents can be iterated over, but note that the exact index for a given element may change when new elements are added ================ */ template< class Type > ID_INLINE idStr idHashTable::GetIndexKey( int index ) const { hashnode_s *node; int count; int i; if ( ( index < 0 ) || ( index > numentries ) ) { assert( 0 ); return NULL; } count = 0; for( i = 0; i < tablesize; i++ ) { for( node = heads[ i ]; node != NULL; node = node->next ) { if ( count == index ) { return node->key; } count++; } } return NULL; } /* ================ idHashTable::Remove ================ */ template< class Type > ID_INLINE bool idHashTable::Remove( const char *key ) { hashnode_s **head; hashnode_s *node; hashnode_s *prev; int hash; hash = GetHash( key ); head = &heads[ hash ]; if ( *head ) { for( prev = NULL, node = *head; node != NULL; prev = node, node = node->next ) { if ( node->key == key ) { if ( prev ) { prev->next = node->next; } else { *head = node->next; } delete node; numentries--; return true; } } } return false; } /* ================ idHashTable::Clear ================ */ template< class Type > ID_INLINE void idHashTable::Clear( void ) { int i; hashnode_s *node; hashnode_s *next; for( i = 0; i < tablesize; i++ ) { next = heads[ i ]; while( next != NULL ) { node = next; next = next->next; delete node; } heads[ i ] = NULL; } numentries = 0; } /* ================ idHashTable::DeleteContents ================ */ template< class Type > ID_INLINE void idHashTable::DeleteContents( void ) { int i; hashnode_s *node; hashnode_s *next; for( i = 0; i < tablesize; i++ ) { next = heads[ i ]; while( next != NULL ) { node = next; next = next->next; delete node->value; delete node; } heads[ i ] = NULL; } numentries = 0; } /* ================ idHashTable::Num ================ */ template< class Type > ID_INLINE int idHashTable::Num( void ) const { return numentries; } /* ================ idHashTable::GetSpread ================ */ template< class Type > int idHashTable::GetSpread( void ) const { int i, average, error, e; hashnode_s *node; // if no items in hash if ( !numentries ) { return 100; } average = numentries / tablesize; error = 0; for ( i = 0; i < tablesize; i++ ) { numItems = 0; for( node = heads[ i ]; node != NULL; node = node->next ) { numItems++; } e = abs( numItems - average ); if ( e > 1 ) { error += e - 1; } } return 100 - (error * 100 / numentries); } /* ================ idHashTable::Swap ================ */ template< class Type > void idHashTable::Swap( idHashTable& rhs ) { idSwap( heads, rhs.heads ); idSwap( tablesize, rhs.tablesize ); idSwap( numentries, rhs.numentries ); idSwap( tablesizemask, rhs.tablesizemask ); } #endif /* !__HASHTABLE_H__ */