//----------------------------------------------------------------------------- // // $Logfile:: /Code/DLLs/game/umap.h $ // $Revision:: 2 $ // $Date:: 10/13/03 8:54a $ // // Copyright (C) 1999 by Ritual Entertainment, Inc. // All rights reserved. // // This source is may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // // DESCRIPTION: // Hash table based map class template. Maps text based keys to previously // stored data. // #ifndef __UMAP_H__ #define __UMAP_H__ #include "str.h" #include template< class Type > class UMap : public Class { private: struct hashnode_s { str key; Type value; hashnode_s *next; hashnode_s( const char *k, Type v, hashnode_s *n ) : key( k ), value( v ), next( n ) {}; hashnode_s( const str &k, Type v, hashnode_s *n ) : key( k.c_str() ), value( v ), next( n ) {}; }; hashnode_s **m_heads; Type m_defaultvalue; unsigned m_tablesize; unsigned m_numentries; unsigned m_tablesizemask; unsigned getHash( const char *key ); public: void Clear( void ); unsigned getNumEntries( void ) const; void DeleteDefaultValues ( void ); Type& operator[]( const char *key ); Type& operator[]( const str &key ); const char *getKeyByIndex ( int index ) const; Type& getValueByIndex ( unsigned index ); ~UMap(); UMap( Type defaultvalue, unsigned tablesize = 64 ) : m_defaultvalue( defaultvalue ), m_tablesize( tablesize ) { int i; int bits; assert( m_tablesize > 0 ); m_heads = new hashnode_s *[ m_tablesize ]; memset( m_heads, 0, sizeof( *m_heads ) * m_tablesize ); m_numentries = 0; m_tablesizemask = 0; bits = 0; for( i = 0; i < 32; i++ ) { if ( m_tablesize & ( 1 << i ) ) { bits++; } } if ( bits == 1 ) { m_tablesizemask = m_tablesize - 1; } }; UMap( UMap &map ) : m_defaultvalue( map.m_defaultvalue ), m_tablesize( map.m_tablesize ) { unsigned i; hashnode_s *node; hashnode_s **prev; assert( m_tablesize > 0 ); m_heads = new hashnode_s *[ m_tablesize ]; m_numentries = map.m_numentries; m_tablesizemask = map.m_tablesizemask; for( i = 0; i < m_tablesize; i++ ) { if ( !map.m_heads[ i ] ) { m_heads[ i ] = NULL; continue; } prev = &m_heads[ i ]; for( node = map.m_heads[ i ]; node != NULL; node = node->next ) { *prev = new hashnode_s( node->key, node->value, NULL ); prev = &( *prev )->next; } } }; }; template< class Type > UMap::~UMap() { Clear(); delete m_heads; } template< class Type > unsigned UMap::getHash ( const char *key ) { unsigned h; const char *v; if ( m_tablesizemask ) { h = 0; for( v = key; *v != '\0'; v++ ) { h = ( 64 * h + unsigned( *v ) ) & m_tablesizemask; } } else { h = 0; for( v = key; *v != '\0'; v++ ) { h = ( 64 * h + unsigned( *v ) ) % m_tablesize; } } return h; } template< class Type > Type& UMap::operator[] ( const char *key ) { hashnode_s **head; hashnode_s *node; unsigned hash; // FIXME // if list was always sorted, could just insert new node as soon as we get // node whose key is greater than the search key hash = getHash( key ); head = &m_heads[ hash ]; if ( *head ) { for( node = *head; node != NULL; node = node->next ) { if ( node->key == key ) { return node->value; } } } m_numentries++; *head = new hashnode_s( key, m_defaultvalue, *head ); return ( *head )->value; } template< class Type > const char * UMap::getKeyByIndex ( int index ) const { unsigned head; for ( head = 0; head < m_tablesize; head++ ) { hashnode_s *node; for ( node = m_heads[head]; node; node = node->next ) { if ( !index ) return node->key.c_str (); index--; } } return NULL; } template Type &UMap::getValueByIndex ( unsigned index ) { unsigned head; ASSERT ( index < getNumEntries () ); for ( head = 0; head < m_tablesize; head++ ) { hashnode_s *node; for ( node = m_heads[head]; node; node = node->next ) { if ( !index ) return node->value; index--; } } // Should never, EVER get here. ASSERT ( 0 ); return m_defaultvalue; } template inline void UMap::DeleteDefaultValues ( void ) { unsigned head; for ( head = 0; head < m_tablesize; head++ ) { hashnode_s *node; while ( m_heads[head] && m_heads[head]->value == m_defaultvalue ) { node = m_heads[head]; m_heads[head] = node->next; delete node; m_numentries--; } if ( !m_heads[head] ) continue; for ( node = m_heads[head]; node->next; ) { hashnode_s *next = node->next; if ( next->value == m_defaultvalue ) { node->next = next->next; delete next; m_numentries--; } else node = node->next; } } } template< class Type > inline Type& UMap::operator[] ( const str &key ) { return this->[ key.c_str() ]; } template< class Type > void UMap::Clear ( void ) { unsigned i; hashnode_s *node; hashnode_s *next; for( i = 0; i < m_tablesize; i++ ) { next = m_heads[ i ]; while( next != NULL ) { node = next; next = next->next; delete node; } m_heads[ i ] = NULL; } m_numentries = 0; } template< class Type > inline unsigned UMap::getNumEntries ( void ) const { return m_numentries; } #endif /* !__UMAP_H__ */