aboutsummaryrefslogtreecommitdiff
path: root/EASTL/include/EASTL/internal/intrusive_hashtable.h
diff options
context:
space:
mode:
Diffstat (limited to 'EASTL/include/EASTL/internal/intrusive_hashtable.h')
-rw-r--r--EASTL/include/EASTL/internal/intrusive_hashtable.h989
1 files changed, 989 insertions, 0 deletions
diff --git a/EASTL/include/EASTL/internal/intrusive_hashtable.h b/EASTL/include/EASTL/internal/intrusive_hashtable.h
new file mode 100644
index 0000000..dccca5b
--- /dev/null
+++ b/EASTL/include/EASTL/internal/intrusive_hashtable.h
@@ -0,0 +1,989 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This file implements an intrusive hash table, which is a hash table whereby
+// the container nodes are the hash table objects themselves. This has benefits
+// primarily in terms of memory management. There are some minor limitations
+// that result from this.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef EASTL_INTERNAL_INTRUSIVE_HASHTABLE_H
+#define EASTL_INTERNAL_INTRUSIVE_HASHTABLE_H
+
+
+#include <EABase/eabase.h>
+#if defined(EA_PRAGMA_ONCE_SUPPORTED)
+ #pragma once
+#endif
+
+#include <EASTL/internal/config.h>
+#include <EASTL/internal/hashtable.h>
+#include <EASTL/type_traits.h>
+#include <EASTL/iterator.h>
+#include <EASTL/functional.h>
+#include <EASTL/utility.h>
+#include <EASTL/algorithm.h>
+
+EA_DISABLE_ALL_VC_WARNINGS();
+#include <new>
+#include <stddef.h>
+#include <string.h>
+EA_RESTORE_ALL_VC_WARNINGS();
+
+
+namespace eastl
+{
+
+ /// intrusive_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.
+ /// An intrusive_hash_node additionally can, via template parameter,
+ /// store a hash code in the node to speed up hash calculations
+ /// and comparisons in some cases.
+ ///
+ /// To consider: Make a version of intrusive_hash_node which is
+ /// templated on the container type. This would allow for the
+ /// mpNext pointer to be the container itself and thus allow
+ /// for easier debugging.
+ ///
+ /// Example usage:
+ /// struct Widget : public intrusive_hash_node{ ... };
+ ///
+ /// struct Dagget : public intrusive_hash_node_key<int>{ ... };
+ ///
+ struct intrusive_hash_node
+ {
+ intrusive_hash_node* mpNext;
+ };
+
+
+ template <typename Key>
+ struct intrusive_hash_node_key : public intrusive_hash_node
+ {
+ typedef Key key_type;
+ Key mKey;
+ };
+
+
+
+ /// intrusive_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>
+ struct intrusive_node_iterator
+ {
+ public:
+ typedef intrusive_node_iterator<Value, bConst> this_type;
+ typedef Value value_type;
+ typedef Value node_type;
+ typedef ptrdiff_t difference_type;
+ typedef typename type_select<bConst, const Value*, Value*>::type pointer;
+ typedef typename type_select<bConst, const Value&, Value&>::type reference;
+ typedef EASTL_ITC_NS::forward_iterator_tag iterator_category;
+
+ public:
+ node_type* mpNode;
+
+ public:
+ intrusive_node_iterator()
+ : mpNode(NULL) { }
+
+ explicit intrusive_node_iterator(value_type* pNode)
+ : mpNode(pNode) { }
+
+ intrusive_node_iterator(const intrusive_node_iterator<Value, true>& x)
+ : mpNode(x.mpNode) { }
+
+ reference operator*() const
+ { return *mpNode; }
+
+ pointer operator->() const
+ { return mpNode; }
+
+ this_type& operator++()
+ { mpNode = static_cast<node_type*>(mpNode->mpNext); return *this; }
+
+ this_type operator++(int)
+ { this_type temp(*this); mpNode = static_cast<node_type*>(mpNode->mpNext); return temp; }
+
+ }; // intrusive_node_iterator
+
+
+
+
+ /// intrusive_hashtable_iterator_base
+ ///
+ /// An intrusive_hashtable_iterator_base 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>
+ struct intrusive_hashtable_iterator_base
+ {
+ public:
+ typedef Value value_type;
+
+ protected:
+ template <typename, typename, typename, typename, size_t, bool, bool>
+ friend class intrusive_hashtable;
+
+ template <typename, bool>
+ friend struct intrusive_hashtable_iterator;
+
+ template <typename V>
+ friend bool operator==(const intrusive_hashtable_iterator_base<V>&, const intrusive_hashtable_iterator_base<V>&);
+
+ template <typename V>
+ friend bool operator!=(const intrusive_hashtable_iterator_base<V>&, const intrusive_hashtable_iterator_base<V>&);
+
+ value_type* mpNode; // Current node within current bucket.
+ value_type** mpBucket; // Current bucket.
+
+ public:
+ intrusive_hashtable_iterator_base(value_type* pNode, value_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 = static_cast<value_type*>(mpNode->mpNext);
+
+ while(mpNode == NULL)
+ mpNode = *++mpBucket;
+ }
+
+ }; // intrusive_hashtable_iterator_base
+
+
+
+
+ /// intrusive_hashtable_iterator
+ ///
+ /// An intrusive_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>
+ struct intrusive_hashtable_iterator : public intrusive_hashtable_iterator_base<Value>
+ {
+ public:
+ typedef intrusive_hashtable_iterator_base<Value> base_type;
+ typedef intrusive_hashtable_iterator<Value, bConst> this_type;
+ typedef intrusive_hashtable_iterator<Value, false> this_type_non_const;
+ typedef typename base_type::value_type 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:
+ intrusive_hashtable_iterator()
+ : base_type(NULL, NULL) { }
+
+ explicit intrusive_hashtable_iterator(value_type* pNode, value_type** pBucket)
+ : base_type(pNode, pBucket) { }
+
+ explicit intrusive_hashtable_iterator(value_type** pBucket)
+ : base_type(*pBucket, pBucket) { }
+
+ intrusive_hashtable_iterator(const this_type_non_const& x)
+ : base_type(x.mpNode, x.mpBucket) { }
+
+ reference operator*() const
+ { return *base_type::mpNode; }
+
+ pointer operator->() const
+ { return base_type::mpNode; }
+
+ this_type& operator++()
+ { base_type::increment(); return *this; }
+
+ this_type operator++(int)
+ { this_type temp(*this); base_type::increment(); return temp; }
+
+ }; // intrusive_hashtable_iterator
+
+
+
+ /// use_intrusive_key
+ ///
+ /// operator()(x) returns x.mKey. Used in maps, as opposed to sets.
+ /// This is a template policy implementation; it is an alternative to
+ /// the use_self template implementation, which is used for sets.
+ ///
+ template <typename Node, typename Key>
+ struct use_intrusive_key // : public unary_function<T, T> // Perhaps we want to make it a subclass of unary_function.
+ {
+ typedef Key result_type;
+
+ const result_type& operator()(const Node& x) const
+ { return x.mKey; }
+ };
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ /// intrusive_hashtable
+ ///
+ template <typename Key, typename Value, typename Hash, typename Equal,
+ size_t bucketCount, bool bConstIterators, bool bUniqueKeys>
+ class intrusive_hashtable
+ {
+ public:
+ typedef intrusive_hashtable<Key, Value, Hash, Equal,
+ bucketCount, bConstIterators, bUniqueKeys> this_type;
+ typedef Key key_type;
+ typedef Value value_type;
+ typedef Value mapped_type;
+ typedef Value node_type;
+ typedef uint32_t hash_code_t;
+ 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 intrusive_node_iterator<value_type, bConstIterators> local_iterator;
+ typedef intrusive_node_iterator<value_type, true> const_local_iterator;
+ typedef intrusive_hashtable_iterator<value_type, bConstIterators> iterator;
+ typedef intrusive_hashtable_iterator<value_type, true> const_iterator;
+ typedef typename type_select<bUniqueKeys, pair<iterator, bool>, iterator>::type insert_return_type;
+ typedef typename type_select<bConstIterators, eastl::use_self<Value>,
+ eastl::use_intrusive_key<Value, key_type> >::type extract_key;
+
+ enum
+ {
+ kBucketCount = bucketCount
+ };
+
+ protected:
+ node_type* mBucketArray[kBucketCount + 1]; // '+1' because we have an end bucket which is non-NULL so iterators always stop on it.
+ size_type mnElementCount;
+ Hash mHash; // To do: Use base class optimization to make this go away when it is of zero size.
+ Equal mEqual; // To do: Use base class optimization to make this go away when it is of zero size.
+
+ public:
+ intrusive_hashtable(const Hash&, const Equal&);
+
+ void swap(this_type& x);
+
+ iterator begin() EA_NOEXCEPT
+ {
+ iterator i(mBucketArray);
+ if(!i.mpNode)
+ i.increment_bucket();
+ return i;
+ }
+
+ const_iterator begin() const EA_NOEXCEPT
+ {
+ const_iterator i(const_cast<node_type**>(mBucketArray));
+ if(!i.mpNode)
+ i.increment_bucket();
+ return i;
+ }
+
+ const_iterator cbegin() const EA_NOEXCEPT
+ {
+ return begin();
+ }
+
+ iterator end() EA_NOEXCEPT
+ { return iterator(mBucketArray + kBucketCount); }
+
+ const_iterator end() const EA_NOEXCEPT
+ { return const_iterator(const_cast<node_type**>(mBucketArray) + kBucketCount); }
+
+ const_iterator cend() const EA_NOEXCEPT
+ { return const_iterator(const_cast<node_type**>(mBucketArray) + kBucketCount); }
+
+ local_iterator begin(size_type n) EA_NOEXCEPT
+ { return local_iterator(mBucketArray[n]); }
+
+ const_local_iterator begin(size_type n) const EA_NOEXCEPT
+ { return const_local_iterator(mBucketArray[n]); }
+
+ const_local_iterator cbegin(size_type n) const EA_NOEXCEPT
+ { return const_local_iterator(mBucketArray[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); }
+
+ size_type size() const EA_NOEXCEPT
+ { return mnElementCount; }
+
+ bool empty() const EA_NOEXCEPT
+ { return mnElementCount == 0; }
+
+ size_type bucket_count() const EA_NOEXCEPT // This function is unnecessary, as the user can directly reference
+ { return kBucketCount; } // intrusive_hashtable::kBucketCount as a constant.
+
+ 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 (size_type)(mHash(k) % kBucketCount); }
+
+ public:
+ float load_factor() const EA_NOEXCEPT
+ { return (float)mnElementCount / (float)kBucketCount; }
+
+ public:
+ insert_return_type insert(value_type& value)
+ { return DoInsertValue(value, integral_constant<bool, bUniqueKeys>()); }
+
+ insert_return_type insert(const_iterator, value_type& value)
+ { return insert(value); } // To consider: We might be able to use the iterator argument to specify a specific insertion location.
+
+ template <typename InputIterator>
+ void insert(InputIterator first, InputIterator last);
+
+ public:
+ iterator erase(const_iterator position);
+ iterator erase(const_iterator first, const_iterator last);
+ size_type erase(const key_type& k);
+ iterator remove(value_type& value); // Removes by value instead of by iterator. This is an O(1) operation, due to this hashtable being 'intrusive'.
+
+ void clear();
+
+ public:
+ iterator find(const key_type& k);
+ const_iterator find(const key_type& k) 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:
+ /// hash_set<string> hashSet;
+ /// hashSet.find_as("hello"); // Use default hash and compare.
+ ///
+ /// Example usage (namespaces omitted for brevity):
+ /// 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;
+
+ size_type count(const key_type& k) const;
+
+ // The use for equal_range in a hash_table seems somewhat questionable.
+ // The primary reason for its existence is to replicate the interface of set/map.
+ eastl::pair<iterator, iterator> equal_range(const key_type& k);
+ eastl::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
+
+ public:
+ bool validate() const;
+ int validate_iterator(const_iterator i) const;
+
+ public:
+ Hash hash_function() const
+ { return mHash; }
+
+ 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 key_equal& key_eq() const
+ { return mEqual; }
+
+ key_equal& key_eq()
+ { return mEqual; }
+
+ protected:
+ eastl::pair<iterator, bool> DoInsertValue(value_type&, true_type); // true_type means bUniqueKeys is true.
+ iterator DoInsertValue(value_type&, false_type); // false_type means bUniqueKeys is false.
+
+ node_type* DoFindNode(node_type* pNode, const key_type& k) const;
+
+ template <typename U, typename BinaryPredicate>
+ node_type* DoFindNode(node_type* pNode, const U& u, BinaryPredicate predicate) const;
+
+ }; // class intrusive_hashtable
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // node_iterator_base
+ ///////////////////////////////////////////////////////////////////////
+
+ template <typename Value, bool bConst>
+ inline bool operator==(const intrusive_node_iterator<Value, bConst>& a,
+ const intrusive_node_iterator<Value, bConst>& b)
+ { return a.mpNode == b.mpNode; }
+
+ template <typename Value, bool bConst>
+ inline bool operator!=(const intrusive_node_iterator<Value, bConst>& a,
+ const intrusive_node_iterator<Value, bConst>& b)
+ { return a.mpNode != b.mpNode; }
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // hashtable_iterator_base
+ ///////////////////////////////////////////////////////////////////////
+
+ template <typename Value>
+ inline bool operator==(const intrusive_hashtable_iterator_base<Value>& a,
+ const intrusive_hashtable_iterator_base<Value>& b)
+ { return a.mpNode == b.mpNode; }
+
+
+ template <typename Value>
+ inline bool operator!=(const intrusive_hashtable_iterator_base<Value>& a,
+ const intrusive_hashtable_iterator_base<Value>& b)
+ { return a.mpNode != b.mpNode; }
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // intrusive_hashtable
+ ///////////////////////////////////////////////////////////////////////
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::intrusive_hashtable(const H& h, const Eq& eq)
+ : mnElementCount(0),
+ mHash(h),
+ mEqual(eq)
+ {
+ memset(mBucketArray, 0, kBucketCount * sizeof(mBucketArray[0]));
+ mBucketArray[kBucketCount] = reinterpret_cast<node_type*>((uintptr_t)~0);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ void intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::swap(this_type& x)
+ {
+ for(size_t i = 0; i < kBucketCount; i++)
+ eastl::swap(mBucketArray[i], x.mBucketArray[i]);
+
+ eastl::swap(mnElementCount, x.mnElementCount);
+ eastl::swap(mHash, x.mHash);
+ eastl::swap(mEqual, x.mEqual);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::find(const key_type& k)
+ {
+ const size_type n = (size_type)(mHash(k) % kBucketCount);
+ node_type* const pNode = DoFindNode(mBucketArray[n], k);
+ return pNode ? iterator(pNode, mBucketArray + n) : iterator(mBucketArray + kBucketCount);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::const_iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::find(const key_type& k) const
+ {
+ const size_type n = (size_type)(mHash(k) % kBucketCount);
+ node_type* const pNode = DoFindNode(mBucketArray[n], k);
+ return pNode ? const_iterator(pNode, const_cast<node_type**>(mBucketArray) + n) : const_iterator(const_cast<node_type**>(mBucketArray) + kBucketCount);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ template <typename U, typename UHash, typename BinaryPredicate>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::find_as(const U& other, UHash uhash, BinaryPredicate predicate)
+ {
+ const size_type n = (size_type)(uhash(other) % kBucketCount);
+ node_type* const pNode = DoFindNode(mBucketArray[n], other, predicate);
+ return pNode ? iterator(pNode, mBucketArray + n) : iterator(mBucketArray + kBucketCount);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ template <typename U, typename UHash, typename BinaryPredicate>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::const_iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::find_as(const U& other, UHash uhash, BinaryPredicate predicate) const
+ {
+ const size_type n = (size_type)(uhash(other) % kBucketCount);
+ node_type* const pNode = DoFindNode(mBucketArray[n], other, predicate);
+ return pNode ? const_iterator(pNode, const_cast<node_type**>(mBucketArray) + n) : const_iterator(const_cast<node_type**>(mBucketArray) + kBucketCount);
+ }
+
+
+ /// intrusive_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 intrusive_hashtable_find(H& hashTable, const 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 intrusive_hashtable_find(const H& hashTable, const 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 H, typename Eq, size_t bC, bool bM, bool bU>
+ template <typename U>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::find_as(const U& other)
+ { return eastl::intrusive_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 H, typename Eq, size_t bC, bool bM, bool bU>
+ template <typename U>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::const_iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::find_as(const U& other) const
+ { return eastl::intrusive_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 H, typename Eq, size_t bC, bool bM, bool bU>
+ typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::size_type
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::count(const key_type& k) const
+ {
+ const size_type n = (size_type)(mHash(k) % kBucketCount);
+ size_type result = 0;
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+
+ // 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 = mBucketArray[n]; pNode; pNode = static_cast<node_type*>(pNode->mpNext))
+ {
+ if(mEqual(k, extractKey(*pNode)))
+ ++result;
+ }
+ return result;
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ eastl::pair<typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator,
+ typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator>
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::equal_range(const key_type& k)
+ {
+ const size_type n = (size_type)(mHash(k) % kBucketCount);
+ node_type** head = mBucketArray + n;
+ node_type* pNode = DoFindNode(*head, k);
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+
+ if(pNode)
+ {
+ node_type* p1 = static_cast<node_type*>(pNode->mpNext);
+
+ for(; p1; p1 = static_cast<node_type*>(p1->mpNext))
+ {
+ if(!mEqual(k, extractKey(*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(mBucketArray + kBucketCount),
+ iterator(mBucketArray + kBucketCount));
+ }
+
+
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ eastl::pair<typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::const_iterator,
+ typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::const_iterator>
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::equal_range(const key_type& k) const
+ {
+ const size_type n = (size_type)(mHash(k) % kBucketCount);
+ node_type** head = const_cast<node_type**>(mBucketArray + n);
+ node_type* pNode = DoFindNode(*head, k);
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+
+ if(pNode)
+ {
+ node_type* p1 = static_cast<node_type*>(pNode->mpNext);
+
+ for(; p1; p1 = static_cast<node_type*>(p1->mpNext))
+ {
+ if(!mEqual(k, extractKey(*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(const_cast<node_type**>(mBucketArray) + kBucketCount),
+ const_iterator(const_cast<node_type**>(mBucketArray) + kBucketCount));
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::node_type*
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::DoFindNode(node_type* pNode, const key_type& k) const
+ {
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+
+ for(; pNode; pNode = static_cast<node_type*>(pNode->mpNext))
+ {
+ if(mEqual(k, extractKey(*pNode)))
+ return pNode;
+ }
+ return NULL;
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ template <typename U, typename BinaryPredicate>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::node_type*
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::DoFindNode(node_type* pNode, const U& other, BinaryPredicate predicate) const
+ {
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+
+ for(; pNode; pNode = static_cast<node_type*>(pNode->mpNext))
+ {
+ if(predicate(extractKey(*pNode), other)) // Intentionally compare with key as first arg and other as second arg.
+ return pNode;
+ }
+ return NULL;
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ eastl::pair<typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator, bool>
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::DoInsertValue(value_type& value, true_type) // true_type means bUniqueKeys is true.
+ {
+ // For sets (as opposed to maps), one could argue that all insertions are successful,
+ // as all elements are unique. However, the equal function might not think so.
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+ const size_type n = (size_type)(mHash(extractKey(value)) % kBucketCount);
+ node_type* const pNode = DoFindNode(mBucketArray[n], extractKey(value));
+
+ if(pNode == NULL)
+ {
+ value.mpNext = mBucketArray[n];
+ mBucketArray[n] = &value;
+ ++mnElementCount;
+
+ return eastl::pair<iterator, bool>(iterator(&value, mBucketArray + n), true);
+ }
+
+ return eastl::pair<iterator, bool>(iterator(pNode, mBucketArray + n), false);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::DoInsertValue(value_type& value, false_type) // false_type means bUniqueKeys is false.
+ {
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+ const size_type n = (size_type)(mHash(extractKey(value)) % kBucketCount);
+ node_type* const pNodePrev = DoFindNode(mBucketArray[n], extractKey(value));
+
+ if(pNodePrev == NULL)
+ {
+ value.mpNext = mBucketArray[n];
+ mBucketArray[n] = &value;
+ }
+ else
+ {
+ value.mpNext = pNodePrev->mpNext;
+ pNodePrev->mpNext = &value;
+ }
+
+ ++mnElementCount;
+
+ return iterator(&value, mBucketArray + n);
+ }
+
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ template <typename InputIterator>
+ inline void intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::insert(InputIterator first, InputIterator last)
+ {
+ for(; first != last; ++first)
+ insert(*first);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::erase(const_iterator i)
+ {
+ iterator iNext(i.mpNode, i.mpBucket);
+ ++iNext;
+
+ node_type* pNode = i.mpNode;
+ node_type* pNodeCurrent = *i.mpBucket;
+
+ if(pNodeCurrent == pNode)
+ *i.mpBucket = static_cast<node_type*>(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 = static_cast<node_type*>(pNodeCurrent->mpNext);
+
+ while(pNodeNext != pNode)
+ {
+ pNodeCurrent = pNodeNext;
+ pNodeNext = static_cast<node_type*>(pNodeCurrent->mpNext);
+ }
+
+ pNodeCurrent->mpNext = static_cast<node_type*>(pNodeNext->mpNext);
+ }
+
+ // To consider: In debug builds set the node mpNext to NULL.
+ --mnElementCount;
+
+ return iNext;
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator
+ intrusive_hashtable<K, V, H, Eq, 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 H, typename Eq, size_t bC, bool bM, bool bU>
+ typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::size_type
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::erase(const key_type& k)
+ {
+ const size_type n = (size_type)(mHash(k) % kBucketCount);
+ const size_type nElementCountSaved = mnElementCount;
+ node_type*& pNodeBase = mBucketArray[n];
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+
+ // Note by Paul Pedriana:
+ // We have two loops here, and I'm not finding any easy way to having just one
+ // loop without changing the requirements of the hashtable node definition.
+ // It's a problem of taking an address of a variable and converting it to the
+ // address of another type without knowing what that type is. Perhaps I'm a
+ // little overly tired, so if there is a simple solution I am probably missing it.
+
+ while(pNodeBase && mEqual(k, extractKey(*pNodeBase)))
+ {
+ pNodeBase = static_cast<node_type*>(pNodeBase->mpNext);
+ --mnElementCount;
+ }
+
+ node_type* pNodePrev = pNodeBase;
+
+ if(pNodePrev)
+ {
+ node_type* pNodeCur;
+
+ while((pNodeCur = static_cast<node_type*>(pNodePrev->mpNext)) != NULL)
+ {
+ if(mEqual(k, extractKey(*pNodeCur)))
+ {
+ pNodePrev->mpNext = static_cast<node_type*>(pNodeCur->mpNext);
+ --mnElementCount; // To consider: In debug builds set the node mpNext to NULL.
+ }
+ else
+ pNodePrev = static_cast<node_type*>(pNodePrev->mpNext);
+ }
+ }
+
+ return nElementCountSaved - mnElementCount;
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline typename intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::iterator
+ intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::remove(value_type& value)
+ {
+ extract_key extractKey; // extract_key is empty and thus this ctor is a no-op.
+ const size_type n = (size_type)(mHash(extractKey(value)) % kBucketCount);
+
+ return erase(iterator(&value, &mBucketArray[n]));
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline void intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::clear()
+ {
+ // To consider: In debug builds set the node mpNext to NULL.
+ memset(mBucketArray, 0, kBucketCount * sizeof(mBucketArray[0]));
+ mnElementCount = 0;
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline bool intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::validate() const
+ {
+ // 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 H, typename Eq, size_t bC, bool bM, bool bU>
+ int intrusive_hashtable<K, V, H, Eq, bC, bM, bU>::validate_iterator(const_iterator i) const
+ {
+ // To do: Come up with a more efficient mechanism of doing this.
+
+ for(const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp)
+ {
+ if(temp == i)
+ return (isf_valid | isf_current | isf_can_dereference);
+ }
+
+ if(i == end())
+ return (isf_valid | isf_current);
+
+ return isf_none;
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // global operators
+ ///////////////////////////////////////////////////////////////////////
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline bool operator==(const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& a,
+ const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& b)
+ {
+ return (a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin());
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline bool operator!=(const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& a,
+ const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& b)
+ {
+ return !(a == b);
+ }
+
+
+ // 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 H, typename Eq, size_t bC, bool bM, bool bU>
+ inline bool operator<(const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& a,
+ const intrusive_hashtable<K, V, H, Eq, 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 H, typename Eq, size_t bC, bool bM, bool bU>
+ inline bool operator>(const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& a,
+ const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& b)
+ {
+ return b < a;
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline bool operator<=(const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& a,
+ const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& b)
+ {
+ return !(b < a);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline bool operator>=(const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& a,
+ const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& b)
+ {
+ return !(a < b);
+ }
+
+
+ template <typename K, typename V, typename H, typename Eq, size_t bC, bool bM, bool bU>
+ inline void swap(const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& a,
+ const intrusive_hashtable<K, V, H, Eq, bC, bM, bU>& b)
+ {
+ a.swap(b);
+ }
+
+
+} // namespace eastl
+
+
+
+#endif // Header include guard