This repository has been archived on 2023-07-11. You can view files and clone it, but cannot push or open issues or pull requests.
MyGameFramework/lib/eastl/include/EASTL/internal/hashtable.h
Gered c5cdddbeaa initial commit
current versions of all of my basic framework sources, build configurations/scripts, and supporting assets
2013-01-31 12:53:05 -05:00

2262 lines
92 KiB
C++

/*
Copyright (C) 2005,2009-2010 Electronic Arts, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
///////////////////////////////////////////////////////////////////////////////
// EASTL/internal/hashtable.h
//
// Written and maintained by Paul Pedriana - 2005.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a hashtable, much like the C++ TR1 hash_set/hash_map.
// proposed classes.
// The primary distinctions between this hashtable and TR1 hash tables are:
// - hashtable is savvy to an environment that doesn't have exception handling,
// as is sometimes the case with console or embedded environments.
// - hashtable is slightly more space-efficient than a conventional std hashtable
// implementation on platforms with 64 bit size_t. This is
// because std STL uses size_t (64 bits) in data structures whereby 32 bits
// of data would be fine.
// - hashtable can contain objects with alignment requirements. TR1 hash tables
// cannot do so without a bit of tedious non-portable effort.
// - hashtable supports debug memory naming natively.
// - hashtable provides a find function that lets you specify a type that is
// different from the hash table key type. This is particularly useful for
// the storing of string objects but finding them by char pointers.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file is currently partially based on the TR1 (technical report 1)
// reference implementation of the hash_set/hash_map C++ classes
// as of about 4/2005.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_INTERNAL_HASHTABLE_H
#define EASTL_INTERNAL_HASHTABLE_H
#include <EASTL/internal/config.h>
#include <EASTL/type_traits.h>
#include <EASTL/allocator.h>
#include <EASTL/iterator.h>
#include <EASTL/functional.h>
#include <EASTL/utility.h>
#include <EASTL/algorithm.h>
#include <string.h>
#ifdef _MSC_VER
#pragma warning(push, 0)
#include <new>
#include <stddef.h>
#pragma warning(pop)
#else
#include <new>
#include <stddef.h>
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4512) // 'class' : assignment operator could not be generated.
#pragma warning(disable: 4530) // C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
#endif
namespace eastl
{
/// EASTL_HASHTABLE_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
///
#ifndef EASTL_HASHTABLE_DEFAULT_NAME
#define EASTL_HASHTABLE_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " hashtable" // Unless the user overrides something, this is "EASTL hashtable".
#endif
/// EASTL_HASHTABLE_DEFAULT_ALLOCATOR
///
#ifndef EASTL_HASHTABLE_DEFAULT_ALLOCATOR
#define EASTL_HASHTABLE_DEFAULT_ALLOCATOR allocator_type(EASTL_HASHTABLE_DEFAULT_NAME)
#endif
/// gpEmptyBucketArray
///
/// A shared representation of an empty hash table. This is present so that
/// a new empty hashtable allocates no memory. It has two entries, one for
/// the first lone empty (NULL) bucket, and one for the non-NULL trailing sentinel.
///
extern EASTL_API void* gpEmptyBucketArray[2];
/// hash_node
///
/// A hash_node stores an element in a hash table, much like a
/// linked list node stores an element in a linked list.
/// A hash_node additionally can, via template parameter,
/// store a hash code in the node to speed up hash calculations
/// and comparisons in some cases.
///
template <typename Value, bool bCacheHashCode>
struct hash_node;
template <typename Value>
struct hash_node<Value, true>
{
Value mValue;
hash_node* mpNext;
eastl_size_t mnHashCode; // See config.h for the definition of eastl_size_t, which defaults to uint32_t.
} EASTL_MAY_ALIAS;
template <typename Value>
struct hash_node<Value, false>
{
Value mValue;
hash_node* mpNext;
} EASTL_MAY_ALIAS;
/// node_iterator_base
///
/// Node iterators iterate nodes within a given bucket.
///
/// We define a base class here because it is shared by both const and
/// non-const iterators.
///
template <typename Value, bool bCacheHashCode>
struct node_iterator_base
{
public:
typedef hash_node<Value, bCacheHashCode> node_type;
node_type* mpNode;
public:
node_iterator_base(node_type* pNode)
: mpNode(pNode) { }
void increment()
{ mpNode = mpNode->mpNext; }
};
/// node_iterator
///
/// Node iterators iterate nodes within a given bucket.
///
/// The bConst parameter defines if the iterator is a const_iterator
/// or an iterator.
///
template <typename Value, bool bConst, bool bCacheHashCode>
struct node_iterator : public node_iterator_base<Value, bCacheHashCode>
{
public:
typedef node_iterator_base<Value, bCacheHashCode> base_type;
typedef node_iterator<Value, bConst, bCacheHashCode> this_type;
typedef typename base_type::node_type node_type;
typedef Value value_type;
typedef typename type_select<bConst, const Value*, Value*>::type pointer;
typedef typename type_select<bConst, const Value&, Value&>::type reference;
typedef ptrdiff_t difference_type;
typedef EASTL_ITC_NS::forward_iterator_tag iterator_category;
public:
explicit node_iterator(node_type* pNode = NULL)
: base_type(pNode) { }
node_iterator(const node_iterator<Value, true, bCacheHashCode>& x)
: base_type(x.mpNode) { }
reference operator*() const
{ return base_type::mpNode->mValue; }
pointer operator->() const
{ return &(base_type::mpNode->mValue); }
node_iterator& operator++()
{ base_type::increment(); return *this; }
node_iterator operator++(int)
{ node_iterator temp(*this); base_type::increment(); return temp; }
}; // node_iterator
/// hashtable_iterator_base
///
/// A hashtable_iterator iterates the entire hash table and not just
/// nodes within a single bucket. Users in general will use a hash
/// table iterator much more often, as it is much like other container
/// iterators (e.g. vector::iterator).
///
/// We define a base class here because it is shared by both const and
/// non-const iterators.
///
template <typename Value, bool bCacheHashCode>
struct hashtable_iterator_base
{
public:
typedef hash_node<Value, bCacheHashCode> node_type;
public:
// We use public here because it allows the hashtable class to access
// these without a function call, and we are very strongly avoiding
// function calls in this library, as our primary goal is performance
// over correctness and some compilers (e.g. GCC) are terrible at
// inlining and so avoiding function calls is of major importance.
node_type* mpNode; // Current node within current bucket.
node_type** mpBucket; // Current bucket.
public:
hashtable_iterator_base(node_type* pNode, node_type** pBucket)
: mpNode(pNode), mpBucket(pBucket) { }
void increment_bucket()
{
++mpBucket;
while(*mpBucket == NULL) // We store an extra bucket with some non-NULL value at the end
++mpBucket; // of the bucket array so that finding the end of the bucket
mpNode = *mpBucket; // array is quick and simple.
}
void increment()
{
mpNode = mpNode->mpNext;
while(mpNode == NULL)
mpNode = *++mpBucket;
}
}; // hashtable_iterator_base
/// hashtable_iterator
///
/// A hashtable_iterator iterates the entire hash table and not just
/// nodes within a single bucket. Users in general will use a hash
/// table iterator much more often, as it is much like other container
/// iterators (e.g. vector::iterator).
///
/// The bConst parameter defines if the iterator is a const_iterator
/// or an iterator.
///
template <typename Value, bool bConst, bool bCacheHashCode>
struct hashtable_iterator : public hashtable_iterator_base<Value, bCacheHashCode>
{
public:
typedef hashtable_iterator_base<Value, bCacheHashCode> base_type;
typedef hashtable_iterator<Value, bConst, bCacheHashCode> this_type;
typedef hashtable_iterator<Value, false, bCacheHashCode> this_type_non_const;
typedef typename base_type::node_type node_type;
typedef Value value_type;
typedef typename type_select<bConst, const Value*, Value*>::type pointer;
typedef typename type_select<bConst, const Value&, Value&>::type reference;
typedef ptrdiff_t difference_type;
typedef EASTL_ITC_NS::forward_iterator_tag iterator_category;
public:
hashtable_iterator(node_type* pNode = NULL, node_type** pBucket = NULL)
: base_type(pNode, pBucket) { }
hashtable_iterator(node_type** pBucket)
: base_type(*pBucket, pBucket) { }
hashtable_iterator(const this_type_non_const& x)
: base_type(x.mpNode, x.mpBucket) { }
reference operator*() const
{ return base_type::mpNode->mValue; }
pointer operator->() const
{ return &(base_type::mpNode->mValue); }
hashtable_iterator& operator++()
{ base_type::increment(); return *this; }
hashtable_iterator operator++(int)
{ hashtable_iterator temp(*this); base_type::increment(); return temp; }
const node_type* get_node() const
{ return base_type::mpNode; }
}; // hashtable_iterator
/// ht_distance
///
/// This function returns the same thing as distance() for
/// forward iterators but returns zero for input iterators.
/// The reason why is that input iterators can only be read
/// once, and calling distance() on an input iterator destroys
/// the ability to read it. This ht_distance is used only for
/// optimization and so the code will merely work better with
/// forward iterators that input iterators.
///
template <typename Iterator>
inline typename eastl::iterator_traits<Iterator>::difference_type
distance_fw_impl(Iterator /* first */, Iterator /* last */, EASTL_ITC_NS::input_iterator_tag)
{ return 0; }
template <typename Iterator>
inline typename eastl::iterator_traits<Iterator>::difference_type
distance_fw_impl(Iterator first, Iterator last, EASTL_ITC_NS::forward_iterator_tag)
{ return eastl::distance(first, last); }
template <typename Iterator>
inline typename eastl::iterator_traits<Iterator>::difference_type
ht_distance(Iterator first, Iterator last)
{
typedef typename eastl::iterator_traits<Iterator>::iterator_category IC;
return distance_fw_impl(first, last, IC());
}
/// mod_range_hashing
///
/// Implements the algorithm for conversion of a number in the range of
/// [0, UINT32_MAX) to the range of [0, BucketCount).
///
struct mod_range_hashing
{
// Defined as eastl_size_t instead of size_t because the latter
// wastes memory and is sometimes slower on 64 bit machines.
uint32_t operator()(uint32_t r, uint32_t n) const
{ return r % n; }
};
/// default_ranged_hash
///
/// Default ranged hash function H. In principle it should be a
/// function object composed from objects of type H1 and H2 such that
/// h(k, n) = h2(h1(k), n), but that would mean making extra copies of
/// h1 and h2. So instead we'll just use a tag to tell class template
/// hashtable to do that composition.
///
struct default_ranged_hash{ };
/// prime_rehash_policy
///
/// Default value for rehash policy. Bucket size is (usually) the
/// smallest prime that keeps the load factor small enough.
///
struct EASTL_API prime_rehash_policy
{
public:
float mfMaxLoadFactor;
float mfGrowthFactor;
mutable uint32_t mnNextResize;
public:
prime_rehash_policy(float fMaxLoadFactor = 1.f)
: mfMaxLoadFactor(fMaxLoadFactor), mfGrowthFactor(2.f), mnNextResize(0) { }
float GetMaxLoadFactor() const
{ return mfMaxLoadFactor; }
/// Return a bucket count no greater than nBucketCountHint,
/// Don't update member variables while at it.
static uint32_t GetPrevBucketCountOnly(uint32_t nBucketCountHint);
/// Return a bucket count no greater than nBucketCountHint.
/// This function has a side effect of updating mnNextResize.
uint32_t GetPrevBucketCount(uint32_t nBucketCountHint) const;
/// Return a bucket count no smaller than nBucketCountHint.
/// This function has a side effect of updating mnNextResize.
uint32_t GetNextBucketCount(uint32_t nBucketCountHint) const;
/// Return a bucket count appropriate for nElementCount elements.
/// This function has a side effect of updating mnNextResize.
uint32_t GetBucketCount(uint32_t nElementCount) const;
/// nBucketCount is current bucket count, nElementCount is current element count,
/// and nElementAdd is number of elements to be inserted. Do we need
/// to increase bucket count? If so, return pair(true, n), where
/// n is the new bucket count. If not, return pair(false, 0).
eastl::pair<bool, uint32_t>
GetRehashRequired(uint32_t nBucketCount, uint32_t nElementCount, uint32_t nElementAdd) const;
};
///////////////////////////////////////////////////////////////////////
// Base classes for hashtable. We define these base classes because
// in some cases we want to do different things depending on the
// value of a policy class. In some cases the policy class affects
// which member functions and nested typedefs are defined; we handle that
// by specializing base class templates. Several of the base class templates
// need to access other members of class template hashtable, so we use
// the "curiously recurring template pattern" (parent class is templated
// on type of child class) for them.
///////////////////////////////////////////////////////////////////////
/// rehash_base
///
/// Give hashtable the get_max_load_factor functions if the rehash
/// policy is prime_rehash_policy.
///
template <typename RehashPolicy, typename Hashtable>
struct rehash_base { };
template <typename Hashtable>
struct rehash_base<prime_rehash_policy, Hashtable>
{
// Returns the max load factor, which is the load factor beyond
// which we rebuild the container with a new bucket count.
float get_max_load_factor() const
{
const Hashtable* const pThis = static_cast<const Hashtable*>(this);
return pThis->rehash_policy().GetMaxLoadFactor();
}
// If you want to make the hashtable never rehash (resize),
// set the max load factor to be a very high number (e.g. 100000.f).
void set_max_load_factor(float fMaxLoadFactor)
{
Hashtable* const pThis = static_cast<Hashtable*>(this);
pThis->rehash_policy(prime_rehash_policy(fMaxLoadFactor));
}
};
/// hash_code_base
///
/// Encapsulates two policy issues that aren't quite orthogonal.
/// (1) The difference between using a ranged hash function and using
/// the combination of a hash function and a range-hashing function.
/// In the former case we don't have such things as hash codes, so
/// we have a dummy type as placeholder.
/// (2) Whether or not we cache hash codes. Caching hash codes is
/// meaningless if we have a ranged hash function. This is because
/// a ranged hash function converts an object directly to its
/// bucket index without ostensibly using a hash code.
/// We also put the key extraction and equality comparison function
/// objects here, for convenience.
///
template <typename Key, typename Value, typename ExtractKey, typename Equal,
typename H1, typename H2, typename H, bool bCacheHashCode>
struct hash_code_base;
/// hash_code_base
///
/// Specialization: ranged hash function, no caching hash codes.
/// H1 and H2 are provided but ignored. We define a dummy hash code type.
///
template <typename Key, typename Value, typename ExtractKey, typename Equal, typename H1, typename H2, typename H>
struct hash_code_base<Key, Value, ExtractKey, Equal, H1, H2, H, false>
{
protected:
ExtractKey mExtractKey; // To do: Make this member go away entirely, as it never has any data.
Equal mEqual; // To do: Make this instance use zero space when it is zero size.
H mRangedHash; // To do: Make this instance use zero space when it is zero size
public:
H1 hash_function() const
{ return H1(); }
Equal equal_function() const // Deprecated. Use key_eq() instead, as key_eq is what the new C++ standard
{ return mEqual; } // has specified in its hashtable (unordered_*) proposal.
const Equal& key_eq() const
{ return mEqual; }
Equal& key_eq()
{ return mEqual; }
protected:
typedef void* hash_code_t;
typedef uint32_t bucket_index_t;
hash_code_base(const ExtractKey& extractKey, const Equal& eq, const H1&, const H2&, const H& h)
: mExtractKey(extractKey), mEqual(eq), mRangedHash(h) { }
hash_code_t get_hash_code(const Key& /* key */) const
{ return NULL; }
bucket_index_t bucket_index(hash_code_t, uint32_t) const
{ return (bucket_index_t)0; }
bucket_index_t bucket_index(const Key& key, hash_code_t, uint32_t nBucketCount) const
{ return (bucket_index_t)mRangedHash(key, nBucketCount); }
bucket_index_t bucket_index(const hash_node<Value, false>* pNode, uint32_t nBucketCount) const
{ return (bucket_index_t)mRangedHash(mExtractKey(pNode->mValue), nBucketCount); }
bool compare(const Key& key, hash_code_t, hash_node<Value, false>* pNode) const
{ return mEqual(key, mExtractKey(pNode->mValue)); }
void copy_code(hash_node<Value, false>*, const hash_node<Value, false>*) const
{ } // Nothing to do.
void set_code(hash_node<Value, false>* /* pDest */, hash_code_t /* c */) const
{ } // Nothing to do.
void base_swap(hash_code_base& x)
{
eastl::swap(mExtractKey, x.mExtractKey);
eastl::swap(mEqual, x.mEqual);
eastl::swap(mRangedHash, x.mRangedHash);
}
}; // hash_code_base
// No specialization for ranged hash function while caching hash codes.
// That combination is meaningless, and trying to do it is an error.
/// hash_code_base
///
/// Specialization: ranged hash function, cache hash codes.
/// This combination is meaningless, so we provide only a declaration
/// and no definition.
///
template <typename Key, typename Value, typename ExtractKey, typename Equal, typename H1, typename H2, typename H>
struct hash_code_base<Key, Value, ExtractKey, Equal, H1, H2, H, true>;
/// hash_code_base
///
/// Specialization: hash function and range-hashing function,
/// no caching of hash codes. H is provided but ignored.
/// Provides typedef and accessor required by TR1.
///
template <typename Key, typename Value, typename ExtractKey, typename Equal, typename H1, typename H2>
struct hash_code_base<Key, Value, ExtractKey, Equal, H1, H2, default_ranged_hash, false>
{
protected:
ExtractKey mExtractKey;
Equal mEqual;
H1 m_h1;
H2 m_h2;
public:
typedef H1 hasher;
H1 hash_function() const
{ return m_h1; }
Equal equal_function() const // Deprecated. Use key_eq() instead, as key_eq is what the new C++ standard
{ return mEqual; } // has specified in its hashtable (unordered_*) proposal.
const Equal& key_eq() const
{ return mEqual; }
Equal& key_eq()
{ return mEqual; }
protected:
typedef uint32_t hash_code_t;
typedef uint32_t bucket_index_t;
typedef hash_node<Value, false> node_type;
hash_code_base(const ExtractKey& ex, const Equal& eq, const H1& h1, const H2& h2, const default_ranged_hash&)
: mExtractKey(ex), mEqual(eq), m_h1(h1), m_h2(h2) { }
hash_code_t get_hash_code(const Key& key) const
{ return (hash_code_t)m_h1(key); }
bucket_index_t bucket_index(hash_code_t c, uint32_t nBucketCount) const
{ return (bucket_index_t)m_h2(c, nBucketCount); }
bucket_index_t bucket_index(const Key&, hash_code_t c, uint32_t nBucketCount) const
{ return (bucket_index_t)m_h2(c, nBucketCount); }
bucket_index_t bucket_index(const node_type* pNode, uint32_t nBucketCount) const
{ return (bucket_index_t)m_h2((hash_code_t)m_h1(mExtractKey(pNode->mValue)), nBucketCount); }
bool compare(const Key& key, hash_code_t, node_type* pNode) const
{ return mEqual(key, mExtractKey(pNode->mValue)); }
void copy_code(node_type*, const node_type*) const
{ } // Nothing to do.
void set_code(node_type*, hash_code_t) const
{ } // Nothing to do.
void base_swap(hash_code_base& x)
{
eastl::swap(mExtractKey, x.mExtractKey);
eastl::swap(mEqual, x.mEqual);
eastl::swap(m_h1, x.m_h1);
eastl::swap(m_h2, x.m_h2);
}
}; // hash_code_base
/// hash_code_base
///
/// Specialization: hash function and range-hashing function,
/// caching hash codes. H is provided but ignored.
/// Provides typedef and accessor required by TR1.
///
template <typename Key, typename Value, typename ExtractKey, typename Equal, typename H1, typename H2>
struct hash_code_base<Key, Value, ExtractKey, Equal, H1, H2, default_ranged_hash, true>
{
protected:
ExtractKey mExtractKey;
Equal mEqual;
H1 m_h1;
H2 m_h2;
public:
typedef H1 hasher;
H1 hash_function() const
{ return m_h1; }
Equal equal_function() const // Deprecated. Use key_eq() instead, as key_eq is what the new C++ standard
{ return mEqual; } // has specified in its hashtable (unordered_*) proposal.
const Equal& key_eq() const
{ return mEqual; }
Equal& key_eq()
{ return mEqual; }
protected:
typedef uint32_t hash_code_t;
typedef uint32_t bucket_index_t;
typedef hash_node<Value, true> node_type;
hash_code_base(const ExtractKey& ex, const Equal& eq, const H1& h1, const H2& h2, const default_ranged_hash&)
: mExtractKey(ex), mEqual(eq), m_h1(h1), m_h2(h2) { }
hash_code_t get_hash_code(const Key& key) const
{ return (hash_code_t)m_h1(key); }
bucket_index_t bucket_index(hash_code_t c, uint32_t nBucketCount) const
{ return (bucket_index_t)m_h2(c, nBucketCount); }
bucket_index_t bucket_index(const Key&, hash_code_t c, uint32_t nBucketCount) const
{ return (bucket_index_t)m_h2(c, nBucketCount); }
bucket_index_t bucket_index(const node_type* pNode, uint32_t nBucketCount) const
{ return (bucket_index_t)m_h2((uint32_t)pNode->mnHashCode, nBucketCount); }
bool compare(const Key& key, hash_code_t c, node_type* pNode) const
{ return (pNode->mnHashCode == c) && mEqual(key, mExtractKey(pNode->mValue)); }
void copy_code(node_type* pDest, const node_type* pSource) const
{ pDest->mnHashCode = pSource->mnHashCode; }
void set_code(node_type* pDest, hash_code_t c) const
{ pDest->mnHashCode = c; }
void base_swap(hash_code_base& x)
{
eastl::swap(mExtractKey, x.mExtractKey);
eastl::swap(mEqual, x.mEqual);
eastl::swap(m_h1, x.m_h1);
eastl::swap(m_h2, x.m_h2);
}
}; // hash_code_base
///////////////////////////////////////////////////////////////////////////
/// hashtable
///
/// Key and Value: arbitrary CopyConstructible types.
///
/// ExtractKey: function object that takes a object of type Value
/// and returns a value of type Key.
///
/// Equal: function object that takes two objects of type k and returns
/// a bool-like value that is true if the two objects are considered equal.
///
/// H1: a hash function. A unary function object with argument type
/// Key and result type size_t. Return values should be distributed
/// over the entire range [0, numeric_limits<uint32_t>::max()].
///
/// H2: a range-hashing function (in the terminology of Tavori and
/// Dreizin). This is a function which takes the output of H1 and
/// converts it to the range of [0, n]. Usually it merely takes the
/// output of H1 and mods it to n.
///
/// H: a ranged hash function (Tavori and Dreizin). This is merely
/// a class that combines the functionality of H1 and H2 together,
/// possibly in some way that is somehow improved over H1 and H2
/// It is a binary function whose argument types are Key and size_t
/// and whose result type is uint32_t. Given arguments k and n, the
/// return value is in the range [0, n). Default: h(k, n) = h2(h1(k), n).
/// If H is anything other than the default, H1 and H2 are ignored,
/// as H is thus overriding H1 and H2.
///
/// RehashPolicy: Policy class with three members, all of which govern
/// the bucket count. nBucket(n) returns a bucket count no smaller
/// than n. GetBucketCount(n) returns a bucket count appropriate
/// for an element count of n. GetRehashRequired(nBucketCount, nElementCount, nElementAdd)
/// determines whether, if the current bucket count is nBucket and the
/// current element count is nElementCount, we need to increase the bucket
/// count. If so, returns pair(true, n), where n is the new
/// bucket count. If not, returns pair(false, <anything>).
///
/// Currently it is hard-wired that the number of buckets never
/// shrinks. Should we allow RehashPolicy to change that?
///
/// bCacheHashCode: true if we store the value of the hash
/// function along with the value. This is a time-space tradeoff.
/// Storing it may improve lookup speed by reducing the number of
/// times we need to call the Equal function.
///
/// bMutableIterators: true if hashtable::iterator is a mutable
/// iterator, false if iterator and const_iterator are both const
/// iterators. This is true for hash_map and hash_multimap,
/// false for hash_set and hash_multiset.
///
/// bUniqueKeys: true if the return value of hashtable::count(k)
/// is always at most one, false if it may be an arbitrary number.
/// This is true for hash_set and hash_map and is false for
/// hash_multiset and hash_multimap.
///
///////////////////////////////////////////////////////////////////////
/// Note:
/// If you want to make a hashtable never increase its bucket usage,
/// call set_max_load_factor with a very high value such as 100000.f.
///
/// find_as
/// In order to support the ability to have a hashtable of strings but
/// be able to do efficiently lookups via char pointers (i.e. so they
/// aren't converted to string objects), we provide the find_as
/// function. This function allows you to do a find with a key of a
/// type other than the hashtable key type. See the find_as function
/// for more documentation on this.
///
/// find_by_hash
/// In the interest of supporting fast operations wherever possible,
/// we provide a find_by_hash function which finds a node using its
/// hash code. This is useful for cases where the node's hash is
/// already known, allowing us to avoid a redundant hash operation
/// in the normal find path.
///
template <typename Key, typename Value, typename Allocator, typename ExtractKey,
typename Equal, typename H1, typename H2, typename H,
typename RehashPolicy, bool bCacheHashCode, bool bMutableIterators, bool bUniqueKeys>
class hashtable
: public rehash_base<RehashPolicy, hashtable<Key, Value, Allocator, ExtractKey, Equal, H1, H2, H, RehashPolicy, bCacheHashCode, bMutableIterators, bUniqueKeys> >,
public hash_code_base<Key, Value, ExtractKey, Equal, H1, H2, H, bCacheHashCode>
{
public:
typedef Key key_type;
typedef Value value_type;
typedef typename ExtractKey::result_type mapped_type;
typedef hash_code_base<Key, Value, ExtractKey, Equal, H1, H2, H, bCacheHashCode> hash_code_base_type;
typedef typename hash_code_base_type::hash_code_t hash_code_t;
typedef Allocator allocator_type;
typedef Equal key_equal;
typedef ptrdiff_t difference_type;
typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to uint32_t.
typedef value_type& reference;
typedef const value_type& const_reference;
typedef node_iterator<value_type, !bMutableIterators, bCacheHashCode> local_iterator;
typedef node_iterator<value_type, true, bCacheHashCode> const_local_iterator;
typedef hashtable_iterator<value_type, !bMutableIterators, bCacheHashCode> iterator;
typedef hashtable_iterator<value_type, true, bCacheHashCode> const_iterator;
typedef eastl::reverse_iterator<iterator> reverse_iterator;
typedef eastl::reverse_iterator<const_iterator> const_reverse_iterator;
typedef hash_node<value_type, bCacheHashCode> node_type;
typedef typename type_select<bUniqueKeys, eastl::pair<iterator, bool>, iterator>::type insert_return_type;
typedef hashtable<Key, Value, Allocator, ExtractKey, Equal, H1, H2, H,
RehashPolicy, bCacheHashCode, bMutableIterators, bUniqueKeys> this_type;
typedef RehashPolicy rehash_policy_type;
typedef ExtractKey extract_key_type;
typedef H1 h1_type;
typedef H2 h2_type;
typedef H h_type;
using hash_code_base_type::key_eq;
using hash_code_base_type::hash_function;
using hash_code_base_type::mExtractKey;
using hash_code_base_type::get_hash_code;
using hash_code_base_type::bucket_index;
using hash_code_base_type::compare;
using hash_code_base_type::set_code;
static const bool kCacheHashCode = bCacheHashCode;
enum
{
kKeyAlignment = EASTL_ALIGN_OF(key_type),
kKeyAlignmentOffset = 0, // To do: Make sure this really is zero for all uses of this template.
kValueAlignment = EASTL_ALIGN_OF(value_type),
kValueAlignmentOffset = 0, // To fix: This offset is zero for sets and >0 for maps. Need to fix this.
kAllocFlagBuckets = 0x00400000 // Flag to allocator which indicates that we are allocating buckets and not nodes.
};
protected:
node_type** mpBucketArray;
size_type mnBucketCount;
size_type mnElementCount;
RehashPolicy mRehashPolicy; // To do: Use base class optimization to make this go away.
allocator_type mAllocator; // To do: Use base class optimization to make this go away.
public:
hashtable(size_type nBucketCount, const H1&, const H2&, const H&, const Equal&, const ExtractKey&,
const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR);
template <typename FowardIterator>
hashtable(FowardIterator first, FowardIterator last, size_type nBucketCount,
const H1&, const H2&, const H&, const Equal&, const ExtractKey&,
const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); // allocator arg removed because VC7.1 fails on the default arg. To do: Make a second version of this function without a default arg.
hashtable(const hashtable& x);
~hashtable();
allocator_type& get_allocator();
void set_allocator(const allocator_type& allocator);
this_type& operator=(const this_type& x);
void swap(this_type& x);
public:
iterator begin()
{
iterator i(mpBucketArray);
if(!i.mpNode)
i.increment_bucket();
return i;
}
const_iterator begin() const
{
const_iterator i(mpBucketArray);
if(!i.mpNode)
i.increment_bucket();
return i;
}
iterator end()
{ return iterator(mpBucketArray + mnBucketCount); }
const_iterator end() const
{ return const_iterator(mpBucketArray + mnBucketCount); }
local_iterator begin(size_type n)
{ return local_iterator(mpBucketArray[n]); }
local_iterator end(size_type)
{ return local_iterator(NULL); }
const_local_iterator begin(size_type n) const
{ return const_local_iterator(mpBucketArray[n]); }
const_local_iterator end(size_type) const
{ return const_local_iterator(NULL); }
bool empty() const
{ return mnElementCount == 0; }
size_type size() const
{ return mnElementCount; }
size_type bucket_count() const
{ return mnBucketCount; }
size_type bucket_size(size_type n) const
{ return (size_type)eastl::distance(begin(n), end(n)); }
//size_type bucket(const key_type& k) const
// { return bucket_index(k, (hash code here), (uint32_t)mnBucketCount); }
public:
float load_factor() const
{ return (float)mnElementCount / (float)mnBucketCount; }
// Inherited from the base class.
// Returns the max load factor, which is the load factor beyond
// which we rebuild the container with a new bucket count.
// get_max_load_factor comes from rehash_base.
// float get_max_load_factor() const;
// Inherited from the base class.
// If you want to make the hashtable never rehash (resize),
// set the max load factor to be a very high number (e.g. 100000.f).
// set_max_load_factor comes from rehash_base.
// void set_max_load_factor(float fMaxLoadFactor);
/// Generalization of get_max_load_factor. This is an extension that's
/// not present in TR1.
const rehash_policy_type& rehash_policy() const
{ return mRehashPolicy; }
/// Generalization of set_max_load_factor. This is an extension that's
/// not present in TR1.
void rehash_policy(const rehash_policy_type& rehashPolicy);
public:
insert_return_type insert(const value_type& value);
iterator insert(const_iterator, const value_type& value);
template <typename InputIterator>
void insert(InputIterator first, InputIterator last);
public:
iterator erase(iterator position);
iterator erase(iterator first, iterator last);
reverse_iterator erase(reverse_iterator position);
reverse_iterator erase(reverse_iterator first, reverse_iterator last);
size_type erase(const key_type& k);
void clear();
void clear(bool clearBuckets);
void reset();
void rehash(size_type nBucketCount);
public:
iterator find(const key_type& key);
const_iterator find(const key_type& key) const;
/// Implements a find whereby the user supplies a comparison of a different type
/// than the hashtable value_type. A useful case of this is one whereby you have
/// a container of string objects but want to do searches via passing in char pointers.
/// The problem is that without this kind of find, you need to do the expensive operation
/// of converting the char pointer to a string so it can be used as the argument to the
/// find function.
///
/// Example usage (namespaces omitted for brevity):
/// hash_set<string> hashSet;
/// hashSet.find_as("hello"); // Use default hash and compare.
///
/// Example usage (note that the predicate uses string as first type and char* as second):
/// hash_set<string> hashSet;
/// hashSet.find_as("hello", hash<char*>(), equal_to_2<string, char*>());
///
template <typename U, typename UHash, typename BinaryPredicate>
iterator find_as(const U& u, UHash uhash, BinaryPredicate predicate);
template <typename U, typename UHash, typename BinaryPredicate>
const_iterator find_as(const U& u, UHash uhash, BinaryPredicate predicate) const;
template <typename U>
iterator find_as(const U& u);
template <typename U>
const_iterator find_as(const U& u) const;
/// Implements a find whereby the user supplies the node's hash code.
///
iterator find_by_hash(hash_code_t c);
const_iterator find_by_hash(hash_code_t c) const;
size_type count(const key_type& k) const;
eastl::pair<iterator, iterator> equal_range(const key_type& k);
eastl::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
public:
bool validate() const;
int validate_iterator(const_iterator i) const;
protected:
node_type* DoAllocateNode(const value_type& value);
node_type* DoAllocateNodeFromKey(const key_type& key);
void DoFreeNode(node_type* pNode);
void DoFreeNodes(node_type** pBucketArray, size_type);
node_type** DoAllocateBuckets(size_type n);
void DoFreeBuckets(node_type** pBucketArray, size_type n);
eastl::pair<iterator, bool> DoInsertValue(const value_type& value, true_type);
iterator DoInsertValue(const value_type& value, false_type);
eastl::pair<iterator, bool> DoInsertKey(const key_type& key, true_type);
iterator DoInsertKey(const key_type& key, false_type);
void DoRehash(size_type nBucketCount);
node_type* DoFindNode(node_type* pNode, const key_type& k, hash_code_t c) const;
template <typename U, typename BinaryPredicate>
node_type* DoFindNode(node_type* pNode, const U& u, BinaryPredicate predicate) const;
node_type* DoFindNode(node_type* pNode, hash_code_t c) const;
}; // class hashtable
///////////////////////////////////////////////////////////////////////
// node_iterator_base
///////////////////////////////////////////////////////////////////////
template <typename Value, bool bCacheHashCode>
inline bool operator==(const node_iterator_base<Value, bCacheHashCode>& a, const node_iterator_base<Value, bCacheHashCode>& b)
{ return a.mpNode == b.mpNode; }
template <typename Value, bool bCacheHashCode>
inline bool operator!=(const node_iterator_base<Value, bCacheHashCode>& a, const node_iterator_base<Value, bCacheHashCode>& b)
{ return a.mpNode != b.mpNode; }
///////////////////////////////////////////////////////////////////////
// hashtable_iterator_base
///////////////////////////////////////////////////////////////////////
template <typename Value, bool bCacheHashCode>
inline bool operator==(const hashtable_iterator_base<Value, bCacheHashCode>& a, const hashtable_iterator_base<Value, bCacheHashCode>& b)
{ return a.mpNode == b.mpNode; }
template <typename Value, bool bCacheHashCode>
inline bool operator!=(const hashtable_iterator_base<Value, bCacheHashCode>& a, const hashtable_iterator_base<Value, bCacheHashCode>& b)
{ return a.mpNode != b.mpNode; }
///////////////////////////////////////////////////////////////////////
// hashtable
///////////////////////////////////////////////////////////////////////
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>
::hashtable(size_type nBucketCount, const H1& h1, const H2& h2, const H& h,
const Eq& eq, const EK& ek, const allocator_type& allocator)
: rehash_base<RP, hashtable>(),
hash_code_base<K, V, EK, Eq, H1, H2, H, bC>(ek, eq, h1, h2, h),
mnBucketCount(0),
mnElementCount(0),
mRehashPolicy(),
mAllocator(allocator)
{
if(nBucketCount < 2) // If we are starting in an initially empty state, with no memory allocation done.
reset();
else // Else we are creating a potentially non-empty hashtable...
{
EASTL_ASSERT(nBucketCount < 10000000);
mnBucketCount = (size_type)mRehashPolicy.GetNextBucketCount((uint32_t)nBucketCount);
mpBucketArray = DoAllocateBuckets(mnBucketCount); // mnBucketCount will always be at least 2.
}
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
template <typename FowardIterator>
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::hashtable(FowardIterator first, FowardIterator last, size_type nBucketCount,
const H1& h1, const H2& h2, const H& h,
const Eq& eq, const EK& ek, const allocator_type& allocator)
: rehash_base<rehash_policy_type, hashtable>(),
hash_code_base<key_type, value_type, extract_key_type, key_equal, h1_type, h2_type, h_type, kCacheHashCode>(ek, eq, h1, h2, h),
//mnBucketCount(0), // This gets re-assigned below.
mnElementCount(0),
mRehashPolicy(),
mAllocator(allocator)
{
if(nBucketCount < 2)
{
const size_type nElementCount = (size_type)eastl::ht_distance(first, last);
mnBucketCount = (size_type)mRehashPolicy.GetBucketCount((uint32_t)nElementCount);
}
else
{
EASTL_ASSERT(nBucketCount < 10000000);
mnBucketCount = nBucketCount;
}
mpBucketArray = DoAllocateBuckets(mnBucketCount); // mnBucketCount will always be at least 2.
#if EASTL_EXCEPTIONS_ENABLED
try
{
#endif
for(; first != last; ++first)
insert(*first);
#if EASTL_EXCEPTIONS_ENABLED
}
catch(...)
{
clear();
DoFreeBuckets(mpBucketArray, mnBucketCount);
throw;
}
#endif
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::hashtable(const this_type& x)
: rehash_base<RP, hashtable>(x),
hash_code_base<K, V, EK, Eq, H1, H2, H, bC>(x),
mnBucketCount(x.mnBucketCount),
mnElementCount(x.mnElementCount),
mRehashPolicy(x.mRehashPolicy),
mAllocator(x.mAllocator)
{
if(mnElementCount) // If there is anything to copy...
{
mpBucketArray = DoAllocateBuckets(mnBucketCount); // mnBucketCount will be at least 2.
#if EASTL_EXCEPTIONS_ENABLED
try
{
#endif
for(size_type i = 0; i < x.mnBucketCount; ++i)
{
node_type* pNodeSource = x.mpBucketArray[i];
node_type** ppNodeDest = mpBucketArray + i;
while(pNodeSource)
{
*ppNodeDest = DoAllocateNode(pNodeSource->mValue);
this->copy_code(*ppNodeDest, pNodeSource);
ppNodeDest = &(*ppNodeDest)->mpNext;
pNodeSource = pNodeSource->mpNext;
}
}
#if EASTL_EXCEPTIONS_ENABLED
}
catch(...)
{
clear();
DoFreeBuckets(mpBucketArray, mnBucketCount);
throw;
}
#endif
}
else
{
// In this case, instead of allocate memory and copy nothing from x,
// we reset ourselves to a zero allocation state.
reset();
}
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::allocator_type&
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::get_allocator()
{
return mAllocator;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::set_allocator(const allocator_type& allocator)
{
mAllocator = allocator;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::this_type&
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::operator=(const this_type& x)
{
if(this != &x)
{
clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
mAllocator = x.mAllocator;
#endif
insert(x.begin(), x.end());
}
return *this;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::~hashtable()
{
clear();
DoFreeBuckets(mpBucketArray, mnBucketCount);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type*
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateNode(const value_type& value)
{
node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), kValueAlignment, kValueAlignmentOffset);
#if EASTL_EXCEPTIONS_ENABLED
try
{
#endif
::new(&pNode->mValue) value_type(value);
pNode->mpNext = NULL;
return pNode;
#if EASTL_EXCEPTIONS_ENABLED
}
catch(...)
{
EASTLFree(mAllocator, pNode, sizeof(node_type));
throw;
}
#endif
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type*
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateNodeFromKey(const key_type& key)
{
node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), kValueAlignment, kValueAlignmentOffset);
#if EASTL_EXCEPTIONS_ENABLED
try
{
#endif
::new(&pNode->mValue) value_type(key);
pNode->mpNext = NULL;
return pNode;
#if EASTL_EXCEPTIONS_ENABLED
}
catch(...)
{
EASTLFree(mAllocator, pNode, sizeof(node_type));
throw;
}
#endif
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFreeNode(node_type* pNode)
{
pNode->~node_type();
EASTLFree(mAllocator, pNode, sizeof(node_type));
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFreeNodes(node_type** pNodeArray, size_type n)
{
for(size_type i = 0; i < n; ++i)
{
node_type* pNode = pNodeArray[i];
while(pNode)
{
node_type* const pTempNode = pNode;
pNode = pNode->mpNext;
DoFreeNode(pTempNode);
}
pNodeArray[i] = NULL;
}
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type**
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoAllocateBuckets(size_type n)
{
// We allocate one extra bucket to hold a sentinel, an arbitrary
// non-null pointer. Iterator increment relies on this.
EASTL_ASSERT(n > 1); // We reserve an mnBucketCount of 1 for the shared gpEmptyBucketArray.
EASTL_CT_ASSERT(kAllocFlagBuckets == 0x00400000); // Currently we expect this to be so, because the allocator has a copy of this enum.
node_type** const pBucketArray = (node_type**)EASTLAllocFlags(mAllocator, (n + 1) * sizeof(node_type*), kAllocFlagBuckets);
//eastl::fill(pBucketArray, pBucketArray + n, (node_type*)NULL);
memset(pBucketArray, 0, n * sizeof(node_type*));
pBucketArray[n] = reinterpret_cast<node_type*>((uintptr_t)~0);
return pBucketArray;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFreeBuckets(node_type** pBucketArray, size_type n)
{
// If n <= 1, then pBucketArray is from the shared gpEmptyBucketArray. We don't test
// for pBucketArray == &gpEmptyBucketArray because one library have a different gpEmptyBucketArray
// than another but pass a hashtable to another. So we go by the size.
if(n > 1)
EASTLFree(mAllocator, pBucketArray, (n + 1) * sizeof(node_type*)); // '+1' because DoAllocateBuckets allocates nBucketCount + 1 buckets in order to have a NULL sentinel at the end.
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::swap(this_type& x)
{
if(mAllocator == x.mAllocator) // If allocators are equivalent...
{
// We leave mAllocator as-is.
hash_code_base<K, V, EK, Eq, H1, H2, H, bC>::base_swap(x); // hash_code_base has multiple implementations, so we let them handle the swap.
eastl::swap(mRehashPolicy, x.mRehashPolicy);
eastl::swap(mpBucketArray, x.mpBucketArray);
eastl::swap(mnBucketCount, x.mnBucketCount);
eastl::swap(mnElementCount, x.mnElementCount);
}
else
{
const this_type temp(*this); // Can't call eastl::swap because that would
*this = x; // itself call this member swap function.
x = temp;
}
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::rehash_policy(const rehash_policy_type& rehashPolicy)
{
mRehashPolicy = rehashPolicy;
const size_type nBuckets = rehashPolicy.GetBucketCount((uint32_t)mnElementCount);
if(nBuckets > mnBucketCount)
DoRehash(nBuckets);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find(const key_type& k)
{
const hash_code_t c = get_hash_code(k);
const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
node_type* const pNode = DoFindNode(mpBucketArray[n], k, c);
return pNode ? iterator(pNode, mpBucketArray + n) : iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end()
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::const_iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find(const key_type& k) const
{
const hash_code_t c = get_hash_code(k);
const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
node_type* const pNode = DoFindNode(mpBucketArray[n], k, c);
return pNode ? const_iterator(pNode, mpBucketArray + n) : const_iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end()
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
template <typename U, typename UHash, typename BinaryPredicate>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find_as(const U& other, UHash uhash, BinaryPredicate predicate)
{
const hash_code_t c = (hash_code_t)uhash(other);
const size_type n = (size_type)(c % mnBucketCount); // This assumes we are using the mod range policy.
node_type* const pNode = DoFindNode(mpBucketArray[n], other, predicate);
return pNode ? iterator(pNode, mpBucketArray + n) : iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end()
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
template <typename U, typename UHash, typename BinaryPredicate>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::const_iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find_as(const U& other, UHash uhash, BinaryPredicate predicate) const
{
const hash_code_t c = (hash_code_t)uhash(other);
const size_type n = (size_type)(c % mnBucketCount); // This assumes we are using the mod range policy.
node_type* const pNode = DoFindNode(mpBucketArray[n], other, predicate);
return pNode ? const_iterator(pNode, mpBucketArray + n) : const_iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end()
}
/// hashtable_find
///
/// Helper function that defaults to using hash<U> and equal_to_2<T, U>.
/// This makes it so that by default you don't need to provide these.
/// Note that the default hash functions may not be what you want, though.
///
/// Example usage. Instead of this:
/// hash_set<string> hashSet;
/// hashSet.find("hello", hash<char*>(), equal_to_2<string, char*>());
///
/// You can use this:
/// hash_set<string> hashSet;
/// hashtable_find(hashSet, "hello");
///
template <typename H, typename U>
inline typename H::iterator hashtable_find(H& hashTable, U u)
{ return hashTable.find_as(u, eastl::hash<U>(), eastl::equal_to_2<const typename H::key_type, U>()); }
template <typename H, typename U>
inline typename H::const_iterator hashtable_find(const H& hashTable, U u)
{ return hashTable.find_as(u, eastl::hash<U>(), eastl::equal_to_2<const typename H::key_type, U>()); }
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
template <typename U>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find_as(const U& other)
{ return eastl::hashtable_find(*this, other); }
// VC++ doesn't appear to like the following, though it seems correct to me.
// So we implement the workaround above until we can straighten this out.
//{ return find_as(other, eastl::hash<U>(), eastl::equal_to_2<const key_type, U>()); }
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
template <typename U>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::const_iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find_as(const U& other) const
{ return eastl::hashtable_find(*this, other); }
// VC++ doesn't appear to like the following, though it seems correct to me.
// So we implement the workaround above until we can straighten this out.
//{ return find_as(other, eastl::hash<U>(), eastl::equal_to_2<const key_type, U>()); }
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find_by_hash(hash_code_t c)
{
const size_type n = (size_type)bucket_index(c, (uint32_t)mnBucketCount);
node_type* const pNode = DoFindNode(mpBucketArray[n], c);
return pNode ? iterator(pNode, mpBucketArray + n) : iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end()
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::const_iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::find_by_hash(hash_code_t c) const
{
const size_type n = (size_type)bucket_index(c, (uint32_t)mnBucketCount);
node_type* const pNode = DoFindNode(mpBucketArray[n], c);
return pNode ? const_iterator(pNode, mpBucketArray + n) : const_iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end()
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::size_type
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::count(const key_type& k) const
{
const hash_code_t c = get_hash_code(k);
const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
size_type result = 0;
// To do: Make a specialization for bU (unique keys) == true and take
// advantage of the fact that the count will always be zero or one in that case.
for(node_type* pNode = mpBucketArray[n]; pNode; pNode = pNode->mpNext)
{
if(compare(k, c, pNode))
++result;
}
return result;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator,
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator>
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::equal_range(const key_type& k)
{
const hash_code_t c = get_hash_code(k);
const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
node_type** head = mpBucketArray + n;
node_type* pNode = DoFindNode(*head, k, c);
if(pNode)
{
node_type* p1 = pNode->mpNext;
for(; p1; p1 = p1->mpNext)
{
if(!compare(k, c, p1))
break;
}
iterator first(pNode, head);
iterator last(p1, head);
if(!p1)
last.increment_bucket();
return eastl::pair<iterator, iterator>(first, last);
}
return eastl::pair<iterator, iterator>(iterator(mpBucketArray + mnBucketCount), // iterator(mpBucketArray + mnBucketCount) == end()
iterator(mpBucketArray + mnBucketCount));
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::const_iterator,
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::const_iterator>
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::equal_range(const key_type& k) const
{
const hash_code_t c = get_hash_code(k);
const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
node_type** head = mpBucketArray + n;
node_type* pNode = DoFindNode(*head, k, c);
if(pNode)
{
node_type* p1 = pNode->mpNext;
for(; p1; p1 = p1->mpNext)
{
if(!compare(k, c, p1))
break;
}
const_iterator first(pNode, head);
const_iterator last(p1, head);
if(!p1)
last.increment_bucket();
return eastl::pair<const_iterator, const_iterator>(first, last);
}
return eastl::pair<const_iterator, const_iterator>(const_iterator(mpBucketArray + mnBucketCount), // iterator(mpBucketArray + mnBucketCount) == end()
const_iterator(mpBucketArray + mnBucketCount));
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type*
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFindNode(node_type* pNode, const key_type& k, hash_code_t c) const
{
for(; pNode; pNode = pNode->mpNext)
{
if(compare(k, c, pNode))
return pNode;
}
return NULL;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
template <typename U, typename BinaryPredicate>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type*
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFindNode(node_type* pNode, const U& other, BinaryPredicate predicate) const
{
for(; pNode; pNode = pNode->mpNext)
{
if(predicate(mExtractKey(pNode->mValue), other)) // Intentionally compare with key as first arg and other as second arg.
return pNode;
}
return NULL;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::node_type*
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFindNode(node_type* pNode, hash_code_t c) const
{
for(; pNode; pNode = pNode->mpNext)
{
if(pNode->mnHashCode == c)
return pNode;
}
return NULL;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool>
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertValue(const value_type& value, true_type) // true_type means bUniqueKeys is true.
{
const key_type& k = mExtractKey(value);
const hash_code_t c = get_hash_code(k);
size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
node_type* const pNode = DoFindNode(mpBucketArray[n], k, c);
if(pNode == NULL)
{
const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1);
// Allocate the new node before doing the rehash so that we don't
// do a rehash if the allocation throws.
node_type* const pNodeNew = DoAllocateNode(value);
set_code(pNodeNew, c); // This is a no-op for most hashtables.
#if EASTL_EXCEPTIONS_ENABLED
try
{
#endif
if(bRehash.first)
{
n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second);
DoRehash(bRehash.second);
}
EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]);
pNodeNew->mpNext = mpBucketArray[n];
mpBucketArray[n] = pNodeNew;
++mnElementCount;
return eastl::pair<iterator, bool>(iterator(pNodeNew, mpBucketArray + n), true);
#if EASTL_EXCEPTIONS_ENABLED
}
catch(...)
{
DoFreeNode(pNodeNew);
throw;
}
#endif
}
return eastl::pair<iterator, bool>(iterator(pNode, mpBucketArray + n), false);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertValue(const value_type& value, false_type) // false_type means bUniqueKeys is false.
{
const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1);
if(bRehash.first)
DoRehash(bRehash.second);
const key_type& k = mExtractKey(value);
const hash_code_t c = get_hash_code(k);
const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
node_type* const pNodeNew = DoAllocateNode(value);
set_code(pNodeNew, c); // This is a no-op for most hashtables.
// To consider: Possibly make this insertion not make equal elements contiguous.
// As it stands now, we insert equal values contiguously in the hashtable.
// The benefit is that equal_range can work in a sensible manner and that
// erase(value) can more quickly find equal values. The downside is that
// this insertion operation taking some extra time. How important is it to
// us that equal_range span all equal items?
node_type* const pNodePrev = DoFindNode(mpBucketArray[n], k, c);
if(pNodePrev == NULL)
{
EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]);
pNodeNew->mpNext = mpBucketArray[n];
mpBucketArray[n] = pNodeNew;
}
else
{
pNodeNew->mpNext = pNodePrev->mpNext;
pNodePrev->mpNext = pNodeNew;
}
++mnElementCount;
return iterator(pNodeNew, mpBucketArray + n);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
eastl::pair<typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator, bool>
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertKey(const key_type& key, true_type) // true_type means bUniqueKeys is true.
{
const hash_code_t c = get_hash_code(key);
size_type n = (size_type)bucket_index(key, c, (uint32_t)mnBucketCount);
node_type* const pNode = DoFindNode(mpBucketArray[n], key, c);
if(pNode == NULL)
{
const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1);
// Allocate the new node before doing the rehash so that we don't
// do a rehash if the allocation throws.
node_type* const pNodeNew = DoAllocateNodeFromKey(key);
set_code(pNodeNew, c); // This is a no-op for most hashtables.
#if EASTL_EXCEPTIONS_ENABLED
try
{
#endif
if(bRehash.first)
{
n = (size_type)bucket_index(key, c, (uint32_t)bRehash.second);
DoRehash(bRehash.second);
}
EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]);
pNodeNew->mpNext = mpBucketArray[n];
mpBucketArray[n] = pNodeNew;
++mnElementCount;
return eastl::pair<iterator, bool>(iterator(pNodeNew, mpBucketArray + n), true);
#if EASTL_EXCEPTIONS_ENABLED
}
catch(...)
{
DoFreeNode(pNodeNew);
throw;
}
#endif
}
return eastl::pair<iterator, bool>(iterator(pNode, mpBucketArray + n), false);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoInsertKey(const key_type& key, false_type) // false_type means bUniqueKeys is false.
{
const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1);
if(bRehash.first)
DoRehash(bRehash.second);
const hash_code_t c = get_hash_code(key);
const size_type n = (size_type)bucket_index(key, c, (uint32_t)mnBucketCount);
node_type* const pNodeNew = DoAllocateNodeFromKey(key);
set_code(pNodeNew, c); // This is a no-op for most hashtables.
// To consider: Possibly make this insertion not make equal elements contiguous.
// As it stands now, we insert equal values contiguously in the hashtable.
// The benefit is that equal_range can work in a sensible manner and that
// erase(value) can more quickly find equal values. The downside is that
// this insertion operation taking some extra time. How important is it to
// us that equal_range span all equal items?
node_type* const pNodePrev = DoFindNode(mpBucketArray[n], key, c);
if(pNodePrev == NULL)
{
EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]);
pNodeNew->mpNext = mpBucketArray[n];
mpBucketArray[n] = pNodeNew;
}
else
{
pNodeNew->mpNext = pNodePrev->mpNext;
pNodePrev->mpNext = pNodeNew;
}
++mnElementCount;
return iterator(pNodeNew, mpBucketArray + n);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert_return_type
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert(const value_type& value)
{
return DoInsertValue(value, integral_constant<bool, bU>());
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert(const_iterator, const value_type& value)
{
// We ignore the first argument (hint iterator). It's not likely to be useful for hashtable containers.
#ifdef __MWERKS__ // The Metrowerks compiler has a bug.
insert_return_type result = insert(value);
return result.first; // Note by Paul Pedriana while perusing this code: This code will fail to compile when bU is false (i.e. for multiset, multimap).
#elif defined(__GNUC__) && (__GNUC__ < 3) // If using old GCC (GCC 2.x has a bug which we work around)
EASTL_ASSERT(empty()); // This function cannot return the correct return value on GCC 2.x. Unless, that is, the container is empty.
DoInsertValue(value, integral_constant<bool, bU>());
return begin(); // This is the wrong answer.
#else
insert_return_type result = DoInsertValue(value, integral_constant<bool, bU>());
return result.first; // Note by Paul Pedriana while perusing this code: This code will fail to compile when bU is false (i.e. for multiset, multimap).
#endif
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
template <typename InputIterator>
void
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert(InputIterator first, InputIterator last)
{
const uint32_t nElementAdd = (uint32_t)eastl::ht_distance(first, last);
const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, nElementAdd);
if(bRehash.first)
DoRehash(bRehash.second);
for(; first != last; ++first)
{
#ifdef __MWERKS__ // The Metrowerks compiler has a bug.
insert(*first);
#else
DoInsertValue(*first, integral_constant<bool, bU>());
#endif
}
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::erase(iterator i)
{
iterator iNext(i);
++iNext;
node_type* pNode = i.mpNode;
node_type* pNodeCurrent = *i.mpBucket;
if(pNodeCurrent == pNode)
*i.mpBucket = pNodeCurrent->mpNext;
else
{
// We have a singly-linked list, so we have no choice but to
// walk down it till we find the node before the node at 'i'.
node_type* pNodeNext = pNodeCurrent->mpNext;
while(pNodeNext != pNode)
{
pNodeCurrent = pNodeNext;
pNodeNext = pNodeCurrent->mpNext;
}
pNodeCurrent->mpNext = pNodeNext->mpNext;
}
DoFreeNode(pNode);
--mnElementCount;
return iNext;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::erase(iterator first, iterator last)
{
while(first != last)
first = erase(first);
return first;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::reverse_iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::erase(reverse_iterator position)
{
return reverse_iterator(erase((++position).base()));
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::reverse_iterator
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::erase(reverse_iterator first, reverse_iterator last)
{
// Version which erases in order from first to last.
// difference_type i(first.base() - last.base());
// while(i--)
// first = erase(first);
// return first;
// Version which erases in order from last to first, but is slightly more efficient:
return reverse_iterator(erase((++last).base(), (++first).base()));
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
typename hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::size_type
hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::erase(const key_type& k)
{
// To do: Reimplement this function to do a single loop and not try to be
// smart about element contiguity. The mechanism here is only a benefit if the
// buckets are heavily overloaded; otherwise this mechanism may be slightly slower.
const hash_code_t c = get_hash_code(k);
const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount);
const size_type nElementCountSaved = mnElementCount;
node_type** pBucketArray = mpBucketArray + n;
while(*pBucketArray && !compare(k, c, *pBucketArray))
pBucketArray = &(*pBucketArray)->mpNext;
while(*pBucketArray && compare(k, c, *pBucketArray))
{
node_type* const pNode = *pBucketArray;
*pBucketArray = pNode->mpNext;
DoFreeNode(pNode);
--mnElementCount;
}
return nElementCountSaved - mnElementCount;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::clear()
{
DoFreeNodes(mpBucketArray, mnBucketCount);
mnElementCount = 0;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::clear(bool clearBuckets)
{
DoFreeNodes(mpBucketArray, mnBucketCount);
if(clearBuckets)
{
DoFreeBuckets(mpBucketArray, mnBucketCount);
reset();
}
mnElementCount = 0;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::reset()
{
// The reset function is a special extension function which unilaterally
// resets the container to an empty state without freeing the memory of
// the contained objects. This is useful for very quickly tearing down a
// container built into scratch memory.
mnBucketCount = 1;
#ifdef _MSC_VER
mpBucketArray = (node_type**)&gpEmptyBucketArray[0];
#else
void* p = &gpEmptyBucketArray[0];
memcpy(&mpBucketArray, &p, sizeof(mpBucketArray)); // Other compilers implement strict aliasing and casting is thus unsafe.
#endif
mnElementCount = 0;
mRehashPolicy.mnNextResize = 0;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::rehash(size_type nBucketCount)
{
// Note that we unilaterally use the passed in bucket count; we do not attempt migrate it
// up to the next prime number. We leave it at the user's discretion to do such a thing.
DoRehash(nBucketCount);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoRehash(size_type nNewBucketCount)
{
node_type** const pBucketArray = DoAllocateBuckets(nNewBucketCount); // nNewBucketCount should always be >= 2.
#if EASTL_EXCEPTIONS_ENABLED
try
{
#endif
node_type* pNode;
for(size_type i = 0; i < mnBucketCount; ++i)
{
while((pNode = mpBucketArray[i]) != NULL) // Using '!=' disables compiler warnings.
{
const size_type nNewBucketIndex = (size_type)bucket_index(pNode, (uint32_t)nNewBucketCount);
mpBucketArray[i] = pNode->mpNext;
pNode->mpNext = pBucketArray[nNewBucketIndex];
pBucketArray[nNewBucketIndex] = pNode;
}
}
DoFreeBuckets(mpBucketArray, mnBucketCount);
mnBucketCount = nNewBucketCount;
mpBucketArray = pBucketArray;
#if EASTL_EXCEPTIONS_ENABLED
}
catch(...)
{
// A failure here means that a hash function threw an exception.
// We can't restore the previous state without calling the hash
// function again, so the only sensible recovery is to delete everything.
DoFreeNodes(pBucketArray, nNewBucketCount);
DoFreeBuckets(pBucketArray, nNewBucketCount);
DoFreeNodes(mpBucketArray, mnBucketCount);
mnElementCount = 0;
throw;
}
#endif
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline bool hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::validate() const
{
// Verify our empty bucket array is unmodified.
if(gpEmptyBucketArray[0] != NULL)
return false;
if(gpEmptyBucketArray[1] != (void*)uintptr_t(~0))
return false;
// Verify that we have at least one bucket. Calculations can
// trigger division by zero exceptions otherwise.
if(mnBucketCount == 0)
return false;
// Verify that gpEmptyBucketArray is used correctly.
// gpEmptyBucketArray is only used when initially empty.
if((void**)mpBucketArray == &gpEmptyBucketArray[0])
{
if(mnElementCount) // gpEmptyBucketArray is used only for empty hash tables.
return false;
if(mnBucketCount != 1) // gpEmptyBucketArray is used exactly an only for mnBucketCount == 1.
return false;
}
else
{
if(mnBucketCount < 2) // Small bucket counts *must* use gpEmptyBucketArray.
return false;
}
// Verify that the element count matches mnElementCount.
size_type nElementCount = 0;
for(const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp)
++nElementCount;
if(nElementCount != mnElementCount)
return false;
// To do: Verify that individual elements are in the expected buckets.
return true;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
int hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::validate_iterator(const_iterator i) const
{
// To do: Come up with a more efficient mechanism of doing this.
for(const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp)
{
if(temp == i)
return (isf_valid | isf_current | isf_can_dereference);
}
if(i == end())
return (isf_valid | isf_current);
return isf_none;
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline bool operator==(const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& a,
const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& b)
{
return (a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin());
}
// Comparing hash tables for less-ness is an odd thing to do. We provide it for
// completeness, though the user is advised to be wary of how they use this.
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline bool operator<(const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& a,
const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& b)
{
// This requires hash table elements to support operator<. Since the hash table
// doesn't compare elements via less (it does so via equals), we must use the
// globally defined operator less for the elements.
return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline bool operator!=(const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& a,
const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& b)
{
return !(a == b);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline bool operator>(const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& a,
const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& b)
{
return b < a;
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline bool operator<=(const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& a,
const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& b)
{
return !(b < a);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline bool operator>=(const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& a,
const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& b)
{
return !(a < b);
}
template <typename K, typename V, typename A, typename EK, typename Eq,
typename H1, typename H2, typename H, typename RP, bool bC, bool bM, bool bU>
inline void swap(const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& a,
const hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>& b)
{
a.swap(b);
}
} // namespace eastl
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // Header include guard