522 lines
11 KiB
C
522 lines
11 KiB
C
|
// Copyright (C) 2007 Id Software, Inc.
|
||
|
//
|
||
|
|
||
|
|
||
|
#ifndef __HASHMAP_GENERIC_H__
|
||
|
#define __HASHMAP_GENERIC_H__
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
Hash policies
|
||
|
============
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdHashGeneratorDefault
|
||
|
============
|
||
|
*/
|
||
|
struct sdHashGeneratorDefault {
|
||
|
template< class type >
|
||
|
static int Hash( const idHashIndex& hasher, const type& value ) {
|
||
|
return hasher.GenerateKey( value );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdHashGeneratorNumeric
|
||
|
============
|
||
|
*/
|
||
|
struct sdHashGeneratorNumeric {
|
||
|
template< class type >
|
||
|
static int Hash( const idHashIndex& hasher, const type& value ) {
|
||
|
return hasher.GenerateKey( value, 0 );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdHashGeneratorIHash
|
||
|
case-insensitive string hash
|
||
|
============
|
||
|
*/
|
||
|
struct sdHashGeneratorIHash {
|
||
|
template< class type >
|
||
|
static int Hash( const idHashIndex& hasher, const type& value ) {
|
||
|
return hasher.GenerateKey( value, false );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
Key comparison policies
|
||
|
============
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdHashCompareDefault
|
||
|
============
|
||
|
*/
|
||
|
struct sdHashCompareDefault {
|
||
|
template< class type1, class type2 >
|
||
|
static bool Compare( const type1& lhs, const type2& rhs ) {
|
||
|
return lhs == rhs;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdHashCompareStrCmp
|
||
|
============
|
||
|
*/
|
||
|
struct sdHashCompareStrCmp {
|
||
|
template< class type1, class type2 >
|
||
|
static bool Compare( const type1& lhs, const type2& rhs ) {
|
||
|
return idStr::Cmp( lhs, rhs ) == 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdHashCompareStrIcmp
|
||
|
============
|
||
|
*/
|
||
|
struct sdHashCompareStrIcmp {
|
||
|
template< class type1, class type2 >
|
||
|
static bool Compare( const type1& lhs, const type2& rhs ) {
|
||
|
return idStr::Icmp( lhs, rhs ) == 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdHashMapGeneric
|
||
|
allow for generic mapping between arbitrary data types
|
||
|
============
|
||
|
*/
|
||
|
template< class key,
|
||
|
class type,
|
||
|
class hashCompare = sdHashCompareDefault,
|
||
|
class hashGenerator = sdHashGeneratorDefault,
|
||
|
class hashIndexType = idHashIndex
|
||
|
>
|
||
|
class sdHashMapGeneric {
|
||
|
public:
|
||
|
typedef sdPair< key, type > Pair;
|
||
|
typedef const Pair ConstPair;
|
||
|
|
||
|
typedef Pair* Iterator;
|
||
|
typedef const Pair* ConstIterator;
|
||
|
|
||
|
typedef type Type;
|
||
|
typedef const type ConstType;
|
||
|
typedef const key ConstKey;
|
||
|
|
||
|
// < location of insertion, true if the item is new, false if the item already existed >
|
||
|
typedef sdPair< Iterator, bool > InsertResult;
|
||
|
|
||
|
typedef hashGenerator HashGenerator;
|
||
|
typedef hashCompare HashCompare;
|
||
|
|
||
|
sdHashMapGeneric( const sdHashMapGeneric& rhs );
|
||
|
explicit sdHashMapGeneric( int granularity = 16 );
|
||
|
sdHashMapGeneric& operator=( const sdHashMapGeneric& rhs );
|
||
|
|
||
|
void SetGranularity( int newGranularity );
|
||
|
void InitHash( const int newHashSize, const int newIndexSize );
|
||
|
|
||
|
size_t Size( void ) const;
|
||
|
|
||
|
// if the key-value mapping already exists, the value is merely reassigned
|
||
|
// the bool parameter of InsertResult reports the results: it is true if
|
||
|
// the item needed to be added, false if it already existed
|
||
|
InsertResult Set( const key& key_, const type& value );
|
||
|
|
||
|
// this is a convenience accessor equivalent to Set
|
||
|
// this is slightly more expensive than using Set directly in the case of new item insertions
|
||
|
type& operator[]( const key& key_ );
|
||
|
|
||
|
Iterator FindIndex( int index );
|
||
|
ConstIterator FindIndex( int index ) const;
|
||
|
|
||
|
void Clear();
|
||
|
|
||
|
bool Remove( const key& key_ );
|
||
|
|
||
|
// Information
|
||
|
int Num() const;
|
||
|
bool Empty() const;
|
||
|
|
||
|
// Iteration
|
||
|
Iterator Begin();
|
||
|
Iterator End();
|
||
|
|
||
|
ConstIterator Begin() const;
|
||
|
ConstIterator End() const;
|
||
|
|
||
|
void Remove( Iterator iter );
|
||
|
|
||
|
int Count( const key& key_ ) const;
|
||
|
|
||
|
void DeleteKeys();
|
||
|
void DeleteValues();
|
||
|
|
||
|
void Swap( sdHashMapGeneric& rhs );
|
||
|
|
||
|
// These various Find version are templates so that in certain cases (mainly string mappings)
|
||
|
// we can avoid constructing a temporary of type key
|
||
|
template< class OtherKey >
|
||
|
Iterator Find( const OtherKey& key_ ) {
|
||
|
int hashKey = HashGenerator::Hash( hash, key_ );
|
||
|
for( int i = hash.GetFirst( hashKey ); i != idHashIndex::NULL_INDEX; i = hash.GetNext( i ) ) {
|
||
|
if( HashCompare::Compare( pairs[ i ].first, key_ )) {
|
||
|
return &pairs[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return End();
|
||
|
}
|
||
|
template< class OtherKey >
|
||
|
ConstIterator Find( const OtherKey& key_ ) const {
|
||
|
int hashKey = HashGenerator::Hash( hash, key_ );
|
||
|
for( int i = hash.GetFirst( hashKey ); i != idHashIndex::NULL_INDEX; i = hash.GetNext( i ) ) {
|
||
|
if( HashCompare::Compare( pairs[ i ].first, key_ )) {
|
||
|
return &pairs[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return End();
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
InsertResult SetNewItem( const key& key_, const type& value );
|
||
|
|
||
|
private:
|
||
|
typedef idList< Pair > PairList;
|
||
|
|
||
|
hashIndexType hash;
|
||
|
PairList pairs;
|
||
|
};
|
||
|
|
||
|
#define HASHMAP_TEMPLATE_HEADER template< class key, class type, class hashCompare, class hashGenerator, class hashIndexType >
|
||
|
#define HASHMAP_TEMPLATE_TAG sdHashMapGeneric< key, type, hashCompare, hashGenerator, hashIndexType >
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::sdHashMapGeneric
|
||
|
============
|
||
|
*/
|
||
|
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
HASHMAP_TEMPLATE_TAG::sdHashMapGeneric( int granularity ) {
|
||
|
SetGranularity( granularity );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::sdHashMapGeneric
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
HASHMAP_TEMPLATE_TAG::sdHashMapGeneric( const sdHashMapGeneric& rhs ) {
|
||
|
*this = rhs;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::sdHashMapGeneric
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
size_t HASHMAP_TEMPLATE_TAG::Size( void ) const {
|
||
|
return sizeof( *this ) + hash.Size() + pairs.Size();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::operator
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
HASHMAP_TEMPLATE_TAG& HASHMAP_TEMPLATE_TAG::operator=( const sdHashMapGeneric& rhs ) {
|
||
|
if( this != &rhs ) {
|
||
|
pairs = rhs.pairs;
|
||
|
hash = rhs.hash;
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Num
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
int HASHMAP_TEMPLATE_TAG::Num() const {
|
||
|
return pairs.Num();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Empty
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
bool HASHMAP_TEMPLATE_TAG::Empty() const {
|
||
|
return Num() == 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Begin
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::Iterator HASHMAP_TEMPLATE_TAG::Begin() {
|
||
|
return pairs.Begin();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Begin
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::ConstIterator HASHMAP_TEMPLATE_TAG::Begin() const {
|
||
|
return pairs.Begin();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::End
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::Iterator HASHMAP_TEMPLATE_TAG::End() {
|
||
|
return pairs.End();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::End
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::ConstIterator HASHMAP_TEMPLATE_TAG::End() const {
|
||
|
return pairs.End();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Set
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::InsertResult HASHMAP_TEMPLATE_TAG::Set( const key& key_, const type& value ) {
|
||
|
Iterator result = Find( key_ );
|
||
|
if( result != End() ) {
|
||
|
result->second = value;
|
||
|
return InsertResult( result, false );
|
||
|
}
|
||
|
|
||
|
return SetNewItem( key_, value );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::SetNewItem
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::InsertResult HASHMAP_TEMPLATE_TAG::SetNewItem( const key& key_, const type& value ) {
|
||
|
assert( Find( key_ ) == End() );
|
||
|
int hashKey = HashGenerator::Hash( hash, key_ );
|
||
|
Iterator result = &pairs.Alloc();
|
||
|
result->first = key_;
|
||
|
result->second = value;
|
||
|
|
||
|
hash.Add( hashKey, pairs.Num() - 1 );
|
||
|
return InsertResult( result, true );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::SetGranularity
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
void HASHMAP_TEMPLATE_TAG::SetGranularity( int newGranularity ) {
|
||
|
pairs.SetGranularity( newGranularity );
|
||
|
hash.SetGranularity( newGranularity );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::InitHash
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
void HASHMAP_TEMPLATE_TAG::InitHash( const int newHashSize, const int newIndexSize ) {
|
||
|
hash.Init( newHashSize, newIndexSize );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::FindIndex
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::Iterator HASHMAP_TEMPLATE_TAG::FindIndex( int index ) {
|
||
|
return &pairs[ index ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::FindIndex
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
typename HASHMAP_TEMPLATE_TAG::ConstIterator HASHMAP_TEMPLATE_TAG::FindIndex( int index ) const {
|
||
|
return &pairs[ index ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Clear
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
void HASHMAP_TEMPLATE_TAG::Clear() {
|
||
|
pairs.Clear();
|
||
|
hash.Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Remove
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
bool HASHMAP_TEMPLATE_TAG::Remove( const key& key_ ) {
|
||
|
int hashKey = HashGenerator::Hash( hash, key_ );
|
||
|
for( int i = hash.GetFirst( hashKey ); i != idHashIndex::NULL_INDEX; i = hash.GetNext( i ) ) {
|
||
|
if( HashCompare::Compare( pairs[ i ].first, key_ )) {
|
||
|
hash.RemoveIndex( hashKey, i );
|
||
|
pairs.RemoveIndex( i );
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::operator[]
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
type& HASHMAP_TEMPLATE_TAG::operator[]( const key& key_ ) {
|
||
|
Iterator iter = Find( key_ );
|
||
|
if( iter != End() ) {
|
||
|
return iter->second;
|
||
|
}
|
||
|
|
||
|
InsertResult result = SetNewItem( key_, type() );
|
||
|
return result.first->second;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Remove
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
void HASHMAP_TEMPLATE_TAG::Remove( Iterator iter ) {
|
||
|
Remove( iter->first );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::Count
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
int HASHMAP_TEMPLATE_TAG::Count( const key& key_ ) const {
|
||
|
int hashKey = HashGenerator::Hash( hash, key_ );
|
||
|
|
||
|
int count = 0;
|
||
|
for( int i = hash.GetFirst( hashKey ); i != idHashIndex::NULL_INDEX; i = hash.GetNext( i ) ) {
|
||
|
count++;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::DeleteKeys
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
void HASHMAP_TEMPLATE_TAG::DeleteKeys() {
|
||
|
for( int i = 0; i < pairs.Num(); i++ ) {
|
||
|
delete pairs[ i ].first;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::DeleteValues
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
void HASHMAP_TEMPLATE_TAG::DeleteValues() {
|
||
|
for( int i = 0; i < pairs.Num(); i++ ) {
|
||
|
delete pairs[ i ].second;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
HASHMAP_TEMPLATE_TAG::DeleteValues
|
||
|
============
|
||
|
*/
|
||
|
HASHMAP_TEMPLATE_HEADER
|
||
|
ID_INLINE
|
||
|
void HASHMAP_TEMPLATE_TAG::Swap( sdHashMapGeneric& rhs ) {
|
||
|
pairs.Swap( rhs.pairs );
|
||
|
hash.Swap( rhs.hash );
|
||
|
}
|
||
|
|
||
|
#undef HASHMAP_TEMPLATE_HEADER
|
||
|
#undef HASHMAP_TEMPLATE_TAG
|
||
|
|
||
|
#endif // !__HASHMAP_GENERIC_H__
|