diff options
Diffstat (limited to 'include/EASTL/internal/hashtable.h')
-rw-r--r-- | include/EASTL/internal/hashtable.h | 3125 |
1 files changed, 0 insertions, 3125 deletions
diff --git a/include/EASTL/internal/hashtable.h b/include/EASTL/internal/hashtable.h deleted file mode 100644 index 077f5b4..0000000 --- a/include/EASTL/internal/hashtable.h +++ /dev/null @@ -1,3125 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Copyright (c) Electronic Arts Inc. All rights reserved. -///////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// This file implements a hashtable, much like the C++11 unordered_set/unordered_map. -// proposed classes. -// The primary distinctions between this hashtable and C++11 unordered containers 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. -// - hashtable provides a lower level insert function which lets the caller -// specify the hash code and optionally the node instance. -/////////////////////////////////////////////////////////////////////////////// - - -#ifndef EASTL_INTERNAL_HASHTABLE_H -#define EASTL_INTERNAL_HASHTABLE_H - - -#include <EABase/eabase.h> -#if defined(EA_PRAGMA_ONCE_SUPPORTED) - #pragma once -#endif - -#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 <EASTL/initializer_list.h> -#include <EASTL/tuple.h> -#include <string.h> - -EA_DISABLE_ALL_VC_WARNINGS() - #include <new> - #include <stddef.h> -EA_RESTORE_ALL_VC_WARNINGS() - -// 4512 - 'class' : assignment operator could not be generated. -// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc -// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. -EA_DISABLE_VC_WARNING(4512 4530 4571); - - -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 - - - /// kHashtableAllocFlagBuckets - /// Flag to allocator which indicates that we are allocating buckets and not nodes. - enum { kHashtableAllocFlagBuckets = 0x00400000 }; - - - /// 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]; - - - /// EASTL_MACRO_SWAP - /// - /// Use EASTL_MACRO_SWAP because GCC (at least v4.6-4.8) has a bug where it fails to compile eastl::swap(mpBucketArray, x.mpBucketArray). - /// - #define EASTL_MACRO_SWAP(Type, a, b) \ - { Type temp = a; a = b; b = temp; } - - - /// 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; - - EA_DISABLE_VC_WARNING(4625 4626) // "copy constructor / assignment operator could not be generated because a base class copy constructor is inaccessible or deleted" - #ifdef EA_COMPILER_MSVC_2015 - EA_DISABLE_VC_WARNING(5026) // disable warning: "move constructor was implicitly defined as deleted" - #endif - template <typename Value> - struct hash_node<Value, true> - { - hash_node() = default; - hash_node(const hash_node&) = default; - hash_node(hash_node&&) = default; - - Value mValue; - hash_node* mpNext; - eastl_size_t mnHashCode; // See config.h for the definition of eastl_size_t, which defaults to size_t. - } EASTL_MAY_ALIAS; - - template <typename Value> - struct hash_node<Value, false> - { - hash_node() = default; - hash_node(const hash_node&) = default; - hash_node(hash_node&&) = default; - - Value mValue; - hash_node* mpNext; - } EASTL_MAY_ALIAS; - - #ifdef EA_COMPILER_MSVC_2015 - EA_RESTORE_VC_WARNING() - #endif - EA_RESTORE_VC_WARNING() - - - // has_hashcode_member - // - // Custom type-trait that checks for the existence of a class data member 'mnHashCode'. - // - // In order to explicitly instantiate the hashtable without error we need to SFINAE away the functions that will - // fail to compile based on if the 'hash_node' contains a 'mnHashCode' member dictated by the hashtable template - // parameters. The hashtable support this level of configuration to allow users to choose which between the space vs. - // time optimization. - // - namespace Internal - { - template <class T> - struct has_hashcode_member - { - private: - template <class U> static eastl::no_type test(...); - template <class U> static eastl::yes_type test(decltype(U::mnHashCode)* = 0); - public: - static const bool value = sizeof(test<T>(0)) == sizeof(eastl::yes_type); - }; - } - - static_assert(Internal::has_hashcode_member<hash_node<int, true>>::value, "contains a mnHashCode member"); - static_assert(!Internal::has_hashcode_member<hash_node<int, false>>::value, "doesn't contain a mnHashCode member"); - - // convenience macros to increase the readability of the code paths that must SFINAE on if the 'hash_node' - // contains the cached hashed value or not. - #define ENABLE_IF_HAS_HASHCODE(T, RT) typename eastl::enable_if<Internal::has_hashcode_member<T>::value, RT>::type* - #define ENABLE_IF_HASHCODE_EASTLSIZET(T, RT) typename eastl::enable_if<eastl::is_convertible<T, eastl_size_t>::value, RT>::type - #define ENABLE_IF_TRUETYPE(T) typename eastl::enable_if<T::value>::type* - #define DISABLE_IF_TRUETYPE(T) typename eastl::enable_if<!T::value>::type* - - - /// 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 - { - typedef hash_node<Value, bCacheHashCode> node_type; - - node_type* mpNode; - - 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 hashtable_iterator_base<Value, bCacheHashCode> this_type; - typedef hash_node<Value, bCacheHashCode> node_type; - - protected: - template <typename, typename, typename, typename, typename, typename, typename, typename, typename, bool, bool, bool> - friend class hashtable; - - template <typename, bool, bool> - friend struct hashtable_iterator; - - template <typename V, bool b> - friend bool operator==(const hashtable_iterator_base<V, b>&, const hashtable_iterator_base<V, b>&); - - template <typename V, bool b> - friend bool operator!=(const hashtable_iterator_base<V, b>&, const hashtable_iterator_base<V, b>&); - - 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, SIZE_T_MAX] to the range of [0, BucketCount). - /// - struct mod_range_hashing - { - uint32_t operator()(size_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 - { - EA_UNUSED(key); - 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 - { - EA_UNUSED(pDest); - EA_UNUSED(c); - } - - 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 size_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 size_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 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; - typedef integral_constant<bool, bUniqueKeys> has_unique_keys_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; - using hash_code_base_type::copy_code; - - static const bool kCacheHashCode = bCacheHashCode; - - enum - { - // This enumeration is deprecated in favor of eastl::kHashtableAllocFlagBuckets. - kAllocFlagBuckets = eastl::kHashtableAllocFlagBuckets // 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. - - struct NodeFindKeyData { - node_type* node; - hash_code_t code; - size_type bucket_index; - }; - - 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); - - hashtable(const hashtable& x); - - // initializer_list ctor support is implemented in subclasses (e.g. hash_set). - // hashtable(initializer_list<value_type>, size_type nBucketCount, const H1&, const H2&, const H&, - // const Equal&, const ExtractKey&, const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); - - hashtable(this_type&& x); - hashtable(this_type&& x, const allocator_type& allocator); - ~hashtable(); - - const allocator_type& get_allocator() const EA_NOEXCEPT; - allocator_type& get_allocator() EA_NOEXCEPT; - void set_allocator(const allocator_type& allocator); - - this_type& operator=(const this_type& x); - this_type& operator=(std::initializer_list<value_type> ilist); - this_type& operator=(this_type&& x); - - void swap(this_type& x); - - iterator begin() EA_NOEXCEPT - { - iterator i(mpBucketArray); - if(!i.mpNode) - i.increment_bucket(); - return i; - } - - const_iterator begin() const EA_NOEXCEPT - { - const_iterator i(mpBucketArray); - if(!i.mpNode) - i.increment_bucket(); - return i; - } - - const_iterator cbegin() const EA_NOEXCEPT - { return begin(); } - - iterator end() EA_NOEXCEPT - { return iterator(mpBucketArray + mnBucketCount); } - - const_iterator end() const EA_NOEXCEPT - { return const_iterator(mpBucketArray + mnBucketCount); } - - const_iterator cend() const EA_NOEXCEPT - { return const_iterator(mpBucketArray + mnBucketCount); } - - // Returns an iterator to the first item in bucket n. - local_iterator begin(size_type n) EA_NOEXCEPT - { return local_iterator(mpBucketArray[n]); } - - const_local_iterator begin(size_type n) const EA_NOEXCEPT - { return const_local_iterator(mpBucketArray[n]); } - - const_local_iterator cbegin(size_type n) const EA_NOEXCEPT - { return const_local_iterator(mpBucketArray[n]); } - - // Returns an iterator to the last item in a bucket returned by begin(n). - local_iterator end(size_type) EA_NOEXCEPT - { return local_iterator(NULL); } - - const_local_iterator end(size_type) const EA_NOEXCEPT - { return const_local_iterator(NULL); } - - const_local_iterator cend(size_type) const EA_NOEXCEPT - { return const_local_iterator(NULL); } - - bool empty() const EA_NOEXCEPT - { return mnElementCount == 0; } - - size_type size() const EA_NOEXCEPT - { return mnElementCount; } - - size_type bucket_count() const EA_NOEXCEPT - { return mnBucketCount; } - - size_type bucket_size(size_type n) const EA_NOEXCEPT - { return (size_type)eastl::distance(begin(n), end(n)); } - - //size_type bucket(const key_type& k) const EA_NOEXCEPT - // { return bucket_index(k, (hash code here), (uint32_t)mnBucketCount); } - - // Returns the ratio of element count to bucket count. A return value of 1 means - // there's an optimal 1 bucket for each element. - float load_factor() const EA_NOEXCEPT - { 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 C++ hash tables (unordered containers). - const rehash_policy_type& rehash_policy() const EA_NOEXCEPT - { return mRehashPolicy; } - - /// Generalization of set_max_load_factor. This is an extension that's - /// not present in C++ hash tables (unordered containers). - void rehash_policy(const rehash_policy_type& rehashPolicy); - - template <class... Args> - insert_return_type emplace(Args&&... args); - - template <class... Args> - iterator emplace_hint(const_iterator position, Args&&... args); - - insert_return_type insert(const value_type& value); - insert_return_type insert(value_type&& otherValue); - iterator insert(const_iterator hint, const value_type& value); - iterator insert(const_iterator hint, value_type&& value); - void insert(std::initializer_list<value_type> ilist); - template <typename InputIterator> void insert(InputIterator first, InputIterator last); - //insert_return_type insert(node_type&& nh); - //iterator insert(const_iterator hint, node_type&& nh); - - // This overload attempts to mitigate the overhead associated with mismatched cv-quality elements of - // the hashtable pair. It can avoid copy overhead because it will perfect forward the user provided pair types - // until it can constructed in-place in the allocated hashtable node. - // - // Ideally we would remove this overload as it deprecated and removed in C++17 but it currently causes - // performance regressions for hashtables with complex keys (keys that allocate resources). - template <class P, - class = typename eastl::enable_if_t< - #if EASTL_ENABLE_PAIR_FIRST_ELEMENT_CONSTRUCTOR - !eastl::is_same_v<eastl::decay_t<P>, key_type> && - #endif - !eastl::is_literal_type_v<P> && - eastl::is_constructible_v<value_type, P&&>>> - insert_return_type insert(P&& otherValue); - - // Non-standard extension - template <class P> // See comments below for the const value_type& equivalent to this function. - insert_return_type insert(hash_code_t c, node_type* pNodeNew, P&& otherValue); - - // We provide a version of insert which lets the caller directly specify the hash value and - // a potential node to insert if needed. This allows for less thread contention in the case - // of a thread-shared hash table that's accessed during a mutex lock, because the hash calculation - // and node creation is done outside of the lock. If pNodeNew is supplied by the user (i.e. non-NULL) - // then it must be freeable via the hash table's allocator. If the return value is true then this function - // took over ownership of pNodeNew, else pNodeNew is still owned by the caller to free or to pass - // to another call to insert. pNodeNew need not be assigned the value by the caller, as the insert - // function will assign value to pNodeNew upon insertion into the hash table. pNodeNew may be - // created by the user with the allocate_uninitialized_node function, and freed by the free_uninitialized_node function. - insert_return_type insert(hash_code_t c, node_type* pNodeNew, const value_type& value); - - template <class M> eastl::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); - template <class M> eastl::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); - template <class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); - template <class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); - - // Used to allocate and free memory used by insert(const value_type& value, hash_code_t c, node_type* pNodeNew). - node_type* allocate_uninitialized_node(); - void free_uninitialized_node(node_type* pNode); - - iterator erase(const_iterator position); - iterator erase(const_iterator first, const_iterator last); - size_type erase(const key_type& k); - - void clear(); - void clear(bool clearBuckets); // If clearBuckets is true, we free the bucket memory and set the bucket count back to the newly constructed count. - void reset_lose_memory() EA_NOEXCEPT; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. - void rehash(size_type nBucketCount); - void reserve(size_type nElementCount); - - 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; - - // Note: find_by_hash and find_range_by_hash both perform a search based on a hash value. - // It is important to note that multiple hash values may map to the same hash bucket, so - // it would be incorrect to assume all items returned match the hash value that - // was searched for. - - /// Implements a find whereby the user supplies the node's hash code. - /// It returns an iterator to the first element that matches the given hash. However, there may be multiple elements that match the given hash. - - template<typename HashCodeT> - ENABLE_IF_HASHCODE_EASTLSIZET(HashCodeT, iterator) find_by_hash(HashCodeT c) - { - EASTL_CT_ASSERT_MSG(bCacheHashCode, - "find_by_hash(hash_code_t c) is designed to avoid recomputing hashes, " - "so it requires cached hash codes. Consider setting template parameter " - "bCacheHashCode to true or using find_by_hash(const key_type& k, hash_code_t c) instead."); - - 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 HashCodeT> - ENABLE_IF_HASHCODE_EASTLSIZET(HashCodeT, const_iterator) find_by_hash(HashCodeT c) const - { - EASTL_CT_ASSERT_MSG(bCacheHashCode, - "find_by_hash(hash_code_t c) is designed to avoid recomputing hashes, " - "so it requires cached hash codes. Consider setting template parameter " - "bCacheHashCode to true or using find_by_hash(const key_type& k, hash_code_t c) instead."); - - 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() - } - - iterator find_by_hash(const key_type& k, hash_code_t c) - { - const size_type n = (size_type)bucket_index(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() - } - - const_iterator find_by_hash(const key_type& k, hash_code_t c) const - { - const size_type n = (size_type)bucket_index(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() - } - - // Returns a pair that allows iterating over all nodes in a hash bucket - // first in the pair returned holds the iterator for the beginning of the bucket, - // second in the pair returned holds the iterator for the end of the bucket, - // If no bucket is found, both values in the pair are set to end(). - // - // See also the note above. - eastl::pair<iterator, iterator> find_range_by_hash(hash_code_t c); - eastl::pair<const_iterator, const_iterator> find_range_by_hash(hash_code_t c) const; - - size_type count(const key_type& k) const EA_NOEXCEPT; - - eastl::pair<iterator, iterator> equal_range(const key_type& k); - eastl::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; - - bool validate() const; - int validate_iterator(const_iterator i) const; - - protected: - // We must remove one of the 'DoGetResultIterator' overloads from the overload-set (via SFINAE) because both can - // not compile successfully at the same time. The 'bUniqueKeys' template parameter chooses at compile-time the - // type of 'insert_return_type' between a pair<iterator,bool> and a raw iterator. We must pick between the two - // overloads that unpacks the iterator from the pair or simply passes the provided iterator to the caller based - // on the class template parameter. - template <typename BoolConstantT> - iterator DoGetResultIterator(BoolConstantT, - const insert_return_type& irt, - ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr) const EA_NOEXCEPT - { - return irt.first; - } - - template <typename BoolConstantT> - iterator DoGetResultIterator(BoolConstantT, - const insert_return_type& irt, - DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr) const EA_NOEXCEPT - { - return irt; - } - - node_type* DoAllocateNodeFromKey(const key_type& key); - node_type* DoAllocateNodeFromKey(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); - - template <bool bDeleteOnException, typename Enabled = bool_constant<bUniqueKeys>, ENABLE_IF_TRUETYPE(Enabled) = nullptr> // only enabled when keys are unique - eastl::pair<iterator, bool> DoInsertUniqueNode(const key_type& k, hash_code_t c, size_type n, node_type* pNodeNew); - - template <typename BoolConstantT, class... Args, ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr> - eastl::pair<iterator, bool> DoInsertValue(BoolConstantT, Args&&... args); - - template <typename BoolConstantT, class... Args, DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr> - iterator DoInsertValue(BoolConstantT, Args&&... args); - - - template <typename BoolConstantT> - eastl::pair<iterator, bool> DoInsertValueExtra(BoolConstantT, - const key_type& k, - hash_code_t c, - node_type* pNodeNew, - value_type&& value, - ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - template <typename BoolConstantT> - eastl::pair<iterator, bool> DoInsertValue(BoolConstantT, - value_type&& value, - ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - template <typename BoolConstantT> - iterator DoInsertValueExtra(BoolConstantT, - const key_type& k, - hash_code_t c, - node_type* pNodeNew, - value_type&& value, - DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - template <typename BoolConstantT> - iterator DoInsertValue(BoolConstantT, value_type&& value, DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - - template <typename BoolConstantT> - eastl::pair<iterator, bool> DoInsertValueExtra(BoolConstantT, - const key_type& k, - hash_code_t c, - node_type* pNodeNew, - const value_type& value, - ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - template <typename BoolConstantT> - eastl::pair<iterator, bool> DoInsertValue(BoolConstantT, - const value_type& value, - ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - template <typename BoolConstantT> - iterator DoInsertValueExtra(BoolConstantT, - const key_type& k, - hash_code_t c, - node_type* pNodeNew, - const value_type& value, - DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - template <typename BoolConstantT> - iterator DoInsertValue(BoolConstantT, const value_type& value, DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); - - template <class... Args> - node_type* DoAllocateNode(Args&&... args); - node_type* DoAllocateNode(value_type&& value); - node_type* DoAllocateNode(const value_type& value); - - // DoInsertKey is supposed to get hash_code_t c = get_hash_code(key). - // it is done in case application has it's own hashset/hashmap-like containter, where hash code is for some reason known prior the insert - // this allows to save some performance, especially with heavy hash functions - eastl::pair<iterator, bool> DoInsertKey(true_type, const key_type& key, hash_code_t c); - iterator DoInsertKey(false_type, const key_type& key, hash_code_t c); - eastl::pair<iterator, bool> DoInsertKey(true_type, key_type&& key, hash_code_t c); - iterator DoInsertKey(false_type, key_type&& key, hash_code_t c); - - // We keep DoInsertKey overload without third parameter, for compatibility with older revisions of EASTL (3.12.07 and earlier) - // It used to call get_hash_code as a first call inside the DoInsertKey. - eastl::pair<iterator, bool> DoInsertKey(true_type, const key_type& key) { return DoInsertKey(true_type(), key, get_hash_code(key)); } - iterator DoInsertKey(false_type, const key_type& key) { return DoInsertKey(false_type(), key, get_hash_code(key)); } - eastl::pair<iterator, bool> DoInsertKey(true_type, key_type&& key) { return DoInsertKey(true_type(), eastl::move(key), get_hash_code(key)); } - iterator DoInsertKey(false_type, key_type&& key) { return DoInsertKey(false_type(), eastl::move(key), get_hash_code(key)); } - - void DoRehash(size_type nBucketCount); - node_type* DoFindNode(node_type* pNode, const key_type& k, hash_code_t c) const; - NodeFindKeyData DoFindKeyData(const key_type& k) const; - - template <typename T> - ENABLE_IF_HAS_HASHCODE(T, node_type) DoFindNode(T* pNode, hash_code_t c) const - { - for (; pNode; pNode = pNode->mpNext) - { - if (pNode->mnHashCode == c) - return pNode; - } - return NULL; - } - - template <typename U, typename BinaryPredicate> - node_type* DoFindNodeT(node_type* pNode, const U& u, BinaryPredicate predicate) const; - - private: - template <typename V, typename Enabled = bool_constant<bUniqueKeys>, ENABLE_IF_TRUETYPE(Enabled) = nullptr> - eastl::pair<iterator, bool> DoInsertValueExtraForwarding(const key_type& k, - hash_code_t c, - node_type* pNodeNew, - V&& value); - - - }; // 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_lose_memory(); - 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); - 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_lose_memory(); - } - } - - - 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(this_type&& x) - : rehash_base<RP, hashtable>(x), - hash_code_base<K, V, EK, Eq, H1, H2, H, bC>(x), - mnBucketCount(0), - mnElementCount(0), - mRehashPolicy(x.mRehashPolicy), - mAllocator(x.mAllocator) - { - reset_lose_memory(); // We do this here the same as we do it in the default ctor because it puts the container in a proper initial empty state. This code would be cleaner if we could rely on being able to use C++11 delegating constructors and just call the default ctor here. - swap(x); - } - - - 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(this_type&& x, const allocator_type& allocator) - : rehash_base<RP, hashtable>(x), - hash_code_base<K, V, EK, Eq, H1, H2, H, bC>(x), - mnBucketCount(0), - mnElementCount(0), - mRehashPolicy(x.mRehashPolicy), - mAllocator(allocator) - { - reset_lose_memory(); // We do this here the same as we do it in the default ctor because it puts the container in a proper initial empty state. This code would be cleaner if we could rely on being able to use C++11 delegating constructors and just call the default ctor here. - swap(x); // swap will directly or indirectly handle the possibility that mAllocator != x.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 const 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() const EA_NOEXCEPT - { - 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 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() EA_NOEXCEPT - { - 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 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=(this_type&& x) - { - if(this != &x) - { - clear(); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. - swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. - } - 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 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=(std::initializer_list<value_type> ilist) - { - // The simplest means of doing this is to clear and insert. There probably isn't a generic - // solution that's any more efficient without having prior knowledge of the ilist contents. - clear(); - insert(ilist.begin(), ilist.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>::DoAllocateNodeFromKey(const key_type& key) - { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); - EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new(eastl::addressof(pNode->mValue)) value_type(pair_first_construct, 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> - 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(key_type&& key) - { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); - EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new(eastl::addressof(pNode->mValue)) value_type(pair_first_construct, eastl::move(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(kHashtableAllocFlagBuckets == 0x00400000); // Currently we expect this to be so, because the allocator has a copy of this enum. - node_type** const pBucketArray = (node_type**)EASTLAllocAlignedFlags(mAllocator, (n + 1) * sizeof(node_type*), EASTL_ALIGN_OF(node_type*), 0, kHashtableAllocFlagBuckets); - //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) - { - 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_MACRO_SWAP(node_type**, mpBucketArray, x.mpBucketArray); - eastl::swap(mnBucketCount, x.mnBucketCount); - eastl::swap(mnElementCount, x.mnElementCount); - - if (mAllocator != x.mAllocator) // If allocators are not equivalent... - { - eastl::swap(mAllocator, x.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>::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 = DoFindNodeT(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 = DoFindNodeT(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> - 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>::find_range_by_hash(hash_code_t c) const - { - const size_type start = (size_type)bucket_index(c, (uint32_t)mnBucketCount); - node_type* const pNodeStart = mpBucketArray[start]; - - if (pNodeStart) - { - eastl::pair<const_iterator, const_iterator> pair(const_iterator(pNodeStart, mpBucketArray + start), - const_iterator(pNodeStart, mpBucketArray + start)); - pair.second.increment_bucket(); - return pair; - } - - return eastl::pair<const_iterator, const_iterator>(const_iterator(mpBucketArray + mnBucketCount), - 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> - 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>::find_range_by_hash(hash_code_t c) - { - const size_type start = (size_type)bucket_index(c, (uint32_t)mnBucketCount); - node_type* const pNodeStart = mpBucketArray[start]; - - if (pNodeStart) - { - eastl::pair<iterator, iterator> pair(iterator(pNodeStart, mpBucketArray + start), - iterator(pNodeStart, mpBucketArray + start)); - pair.second.increment_bucket(); - return pair; - - } - - return eastl::pair<iterator, iterator>(iterator(mpBucketArray + mnBucketCount), - 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> - 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 EA_NOEXCEPT - { - 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>::NodeFindKeyData - hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::DoFindKeyData(const key_type& k) const { - NodeFindKeyData d; - d.code = get_hash_code(k); - d.bucket_index = (size_type)bucket_index(k, d.code, (uint32_t)mnBucketCount); - d.node = DoFindNode(mpBucketArray[d.bucket_index], k, d.code); - return d; - } - - 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>::DoFindNodeT(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> - template <bool bDeleteOnException, typename Enabled, ENABLE_IF_TRUETYPE(Enabled)> // only enabled when keys are unique - 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>::DoInsertUniqueNode(const key_type& k, hash_code_t c, size_type n, node_type* pNodeNew) - { - const eastl::pair<bool, uint32_t> bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); - - 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((uintptr_t)mpBucketArray != (uintptr_t)&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(...) - { - EA_CONSTEXPR_IF(bDeleteOnException) { DoFreeNode(pNodeNew); } - 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> - template <typename BoolConstantT, class... Args, ENABLE_IF_TRUETYPE(BoolConstantT)> - 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(BoolConstantT, Args&&... args) // true_type means bUniqueKeys is true. - { - // Adds the value to the hash table if not already present. - // If already present then the existing value is returned via an iterator/bool pair. - - // We have a chicken-and-egg problem here. In order to know if and where to insert the value, we need to get the - // hashtable key for the value. But we don't explicitly have a value argument, we have a templated Args&&... argument. - // We need the value_type in order to proceed, but that entails getting an instance of a value_type from the args. - // And it may turn out that the value is already present in the hashtable and we need to cancel the insertion, - // despite having obtained a value_type to put into the hashtable. We have mitigated this problem somewhat by providing - // specializations of the insert function for const value_type& and value_type&&, and so the only time this function - // should get called is when args refers to arguments to construct a value_type. - - node_type* const pNodeNew = DoAllocateNode(eastl::forward<Args>(args)...); - const key_type& k = mExtractKey(pNodeNew->mValue); - 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) // If value is not present... add it. - { - return DoInsertUniqueNode<true>(k, c, n, pNodeNew); - } - else - { - // To do: We have an inefficiency to deal with here. We allocated a node above but we are freeing it here because - // it turned out it wasn't needed. But we needed to create the node in order to get the hashtable key for - // the node. One possible resolution is to create specializations: DoInsertValue(true_type, value_type&&) and - // DoInsertValue(true_type, const value_type&) which don't need to create a node up front in order to get the - // hashtable key. Probably most users would end up using these pathways instead of this Args... pathway. - // While we should considering handling this to-do item, a lot of the performance limitations of maps and sets - // in practice is with finding elements rather than adding (potentially redundant) new elements. - DoFreeNode(pNodeNew); - } - - 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> - template <typename BoolConstantT, class... Args, DISABLE_IF_TRUETYPE(BoolConstantT)> - 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(BoolConstantT, Args&&... args) // 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); - - node_type* pNodeNew = DoAllocateNode(eastl::forward<Args>(args)...); - const key_type& k = mExtractKey(pNodeNew->mValue); - const hash_code_t c = get_hash_code(k); - const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); - - 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> - template <class... Args> - 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(Args&&... args) - { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); - EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new(eastl::addressof(pNode->mValue)) value_type(eastl::forward<Args>(args)...); - pNode->mpNext = NULL; - return pNode; - #if EASTL_EXCEPTIONS_ENABLED - } - catch(...) - { - EASTLFree(mAllocator, pNode, sizeof(node_type)); - throw; - } - #endif - } - - - //////////////////////////////////////////////////////////////////////////////////////////////////// - // Note: The following insertion-related functions are nearly copies of the above three functions, - // but are for value_type&& and const value_type& arguments. It's useful for us to have the functions - // below, even when using a fully compliant C++11 compiler that supports the above functions. - // The reason is because the specializations below are slightly more efficient because they can delay - // the creation of a node until it's known that it will be needed. - //////////////////////////////////////////////////////////////////////////////////////////////////// - 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 BoolConstantT> - inline 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>::DoInsertValueExtra(BoolConstantT, const key_type& k, - hash_code_t c, node_type* pNodeNew, value_type&& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. - { - return DoInsertValueExtraForwarding(k, c, pNodeNew, eastl::move(value)); - } - - 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 BoolConstantT> - inline 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>::DoInsertValueExtra(BoolConstantT, const key_type& k, - hash_code_t c, node_type* pNodeNew, const value_type& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. - { - return DoInsertValueExtraForwarding(k, c, pNodeNew, value); - } - - 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 VFwd, typename Enabled, ENABLE_IF_TRUETYPE(Enabled)> // true_type means bUniqueKeys is true. - 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>::DoInsertValueExtraForwarding(const key_type& k, - hash_code_t c, node_type* pNodeNew, VFwd&& value) - { - // Adds the value to the hash table if not already present. - // If already present then the existing value is returned via an iterator/bool pair. - size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); - node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); - - if(pNode == NULL) // If value is not present... add it. - { - // Allocate the new node before doing the rehash so that we don't - // do a rehash if the allocation throws. - if(pNodeNew) - { - ::new(eastl::addressof(pNodeNew->mValue)) value_type(eastl::forward<VFwd>(value)); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. - return DoInsertUniqueNode<false>(k, c, n, pNodeNew); - } - else - { - pNodeNew = DoAllocateNode(eastl::move(value)); - return DoInsertUniqueNode<true>(k, c, n, pNodeNew); - } - } - // Else the value is already present, so don't add a new node. And don't free pNodeNew. - - 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> - template <typename BoolConstantT> - 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(BoolConstantT, value_type&& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. - { - const key_type& k = mExtractKey(value); - const hash_code_t c = get_hash_code(k); - - return DoInsertValueExtra(true_type(), k, c, NULL, eastl::move(value)); - } - - - 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 BoolConstantT> - 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>::DoInsertValueExtra(BoolConstantT, const key_type& k, hash_code_t c, node_type* pNodeNew, value_type&& value, - DISABLE_IF_TRUETYPE(BoolConstantT)) // 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); // Note: We don't need to wrap this call with try/catch because there's nothing we would need to do in the catch. - - const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); - - if(pNodeNew) - ::new(eastl::addressof(pNodeNew->mValue)) value_type(eastl::move(value)); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. - else - pNodeNew = DoAllocateNode(eastl::move(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> - template<typename BoolConstantT> - 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(BoolConstantT, value_type&& value, DISABLE_IF_TRUETYPE(BoolConstantT)) // false_type means bUniqueKeys is false. - { - const key_type& k = mExtractKey(value); - const hash_code_t c = get_hash_code(k); - - return DoInsertValueExtra(false_type(), k, c, NULL, eastl::move(value)); - } - - - 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(value_type&& value) - { - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); - EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new(eastl::addressof(pNode->mValue)) value_type(eastl::move(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> - template<typename BoolConstantT> - 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(BoolConstantT, const value_type& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. - { - const key_type& k = mExtractKey(value); - const hash_code_t c = get_hash_code(k); - - return DoInsertValueExtra(true_type(), k, c, NULL, value); - } - - - 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 BoolConstantT> - 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>::DoInsertValueExtra(BoolConstantT, const key_type& k, hash_code_t c, node_type* pNodeNew, const value_type& value, - DISABLE_IF_TRUETYPE(BoolConstantT)) // 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); // Note: We don't need to wrap this call with try/catch because there's nothing we would need to do in the catch. - - const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); - - if(pNodeNew) - ::new(eastl::addressof(pNodeNew->mValue)) value_type(value); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. - else - 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> - template<typename BoolConstantT> - 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(BoolConstantT, const value_type& value, DISABLE_IF_TRUETYPE(BoolConstantT)) // false_type means bUniqueKeys is false. - { - const key_type& k = mExtractKey(value); - const hash_code_t c = get_hash_code(k); - - return DoInsertValueExtra(false_type(), k, c, NULL, value); - } - - - 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), EASTL_ALIGN_OF(node_type), 0); - EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); - - #if EASTL_EXCEPTIONS_ENABLED - try - { - #endif - ::new(eastl::addressof(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>::allocate_uninitialized_node() - { - // We don't wrap this in try/catch because users of this function are expected to do that themselves as needed. - node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); - EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); - // Leave pNode->mValue uninitialized. - pNode->mpNext = NULL; - return pNode; - } - - - 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>::free_uninitialized_node(node_type* pNode) - { - // pNode->mValue is expected to be uninitialized. - 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> - 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(true_type, const key_type& key, const hash_code_t c) // true_type means bUniqueKeys is true. - { - 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(false_type, const key_type& key, const hash_code_t c) // 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 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> - 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(true_type, key_type&& key, const hash_code_t c) // true_type means bUniqueKeys is true. - { - 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(eastl::move(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(false_type, key_type&& key, const hash_code_t c) // 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 size_type n = (size_type)bucket_index(key, c, (uint32_t)mnBucketCount); - - node_type* const pNodeNew = DoAllocateNodeFromKey(eastl::move(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> - template <class... Args> - 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>::emplace(Args&&... args) - { - return DoInsertValue(has_unique_keys_type(), eastl::forward<Args>(args)...); // Need to use forward instead of move because Args&& is a "universal reference" instead of an rvalue reference. - } - - 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 <class... Args> - 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>::emplace_hint(const_iterator, Args&&... args) - { - // We currently ignore the iterator argument as a hint. - insert_return_type result = DoInsertValue(has_unique_keys_type(), eastl::forward<Args>(args)...); - return DoGetResultIterator(has_unique_keys_type(), 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> - 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(value_type&& otherValue) - { - return DoInsertValue(has_unique_keys_type(), eastl::move(otherValue)); - } - - - 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 <class P> - 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(hash_code_t c, node_type* pNodeNew, P&& otherValue) - { - // pNodeNew->mValue is expected to be uninitialized. - value_type value(eastl::forward<P>(otherValue)); // Need to use forward instead of move because P&& is a "universal reference" instead of an rvalue reference. - const key_type& k = mExtractKey(value); - return DoInsertValueExtra(has_unique_keys_type(), k, c, pNodeNew, eastl::move(value)); - } - - - 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, value_type&& value) - { - // We currently ignore the iterator argument as a hint. - insert_return_type result = DoInsertValue(has_unique_keys_type(), value_type(eastl::move(value))); - return DoGetResultIterator(has_unique_keys_type(), 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> - 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(has_unique_keys_type(), value); - } - - - 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(hash_code_t c, node_type* pNodeNew, const value_type& value) - { - // pNodeNew->mValue is expected to be uninitialized. - const key_type& k = mExtractKey(value); - return DoInsertValueExtra(has_unique_keys_type(), k, c, pNodeNew, value); - } - - - 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 P, class> - 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(P&& otherValue) - { - return emplace(eastl::forward<P>(otherValue)); - } - - - 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. - insert_return_type result = DoInsertValue(has_unique_keys_type(), value); - return DoGetResultIterator(has_unique_keys_type(), 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> - void hashtable<K, V, A, EK, Eq, H1, H2, H, RP, bC, bM, bU>::insert(std::initializer_list<value_type> ilist) - { - insert(ilist.begin(), ilist.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 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) - DoInsertValue(has_unique_keys_type(), *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> - template <class M> - 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>::insert_or_assign(const key_type& k, M&& obj) - { - auto iter = find(k); - if(iter == end()) - { - return insert(value_type(piecewise_construct, eastl::forward_as_tuple(k), eastl::forward_as_tuple(eastl::forward<M>(obj)))); - } - else - { - iter->second = eastl::forward<M>(obj); - return {iter, 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> - template <class M> - 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>::insert_or_assign(key_type&& k, M&& obj) - { - auto iter = find(k); - if(iter == end()) - { - return insert(value_type(piecewise_construct, eastl::forward_as_tuple(eastl::move(k)), eastl::forward_as_tuple(eastl::forward<M>(obj)))); - } - else - { - iter->second = eastl::forward<M>(obj); - return {iter, 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> - template <class M> - 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_or_assign(const_iterator, const key_type& k, M&& obj) - { - return insert_or_assign(k, eastl::forward<M>(obj)).first; // we ignore the iterator hint - } - - 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 <class M> - 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_or_assign(const_iterator, key_type&& k, M&& obj) - { - return insert_or_assign(eastl::move(k), eastl::forward<M>(obj)).first; // we ignore the iterator hint - } - - - 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(const_iterator i) - { - iterator iNext(i.mpNode, i.mpBucket); // Convert from const_iterator to iterator while constructing. - ++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(const_iterator first, const_iterator last) - { - while(first != last) - first = erase(first); - return iterator(first.mpNode, first.mpBucket); - } - - - - 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; - - node_type* pDeleteList = nullptr; - while(*pBucketArray && compare(k, c, *pBucketArray)) - { - node_type* const pNode = *pBucketArray; - *pBucketArray = pNode->mpNext; - // Don't free the node here, k might be a reference to the key inside this node, - // and we're re-using it when we compare to the following nodes. - // Instead, add it to the list of things to be deleted. - pNode->mpNext = pDeleteList; - pDeleteList = pNode; - --mnElementCount; - } - - while (pDeleteList) { - node_type* const pToDelete = pDeleteList; - pDeleteList = pDeleteList->mpNext; - DoFreeNode(pToDelete); - } - - 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_lose_memory(); - } - 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_lose_memory() EA_NOEXCEPT - { - // 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>::reserve(size_type nElementCount) - { - rehash(mRehashPolicy.GetBucketCount(uint32_t(nElementCount))); - } - - - - 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 - /////////////////////////////////////////////////////////////////////// - - // operator==, != have been moved to the specific container subclasses (e.g. hash_map). - - // The following comparison operators are deprecated and will likely be removed in a - // future version of this package. - // - // 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 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 - - -EA_RESTORE_VC_WARNING(); - - -#endif // Header include guard |