#pragma once #include "hash_function.h" #include "pair.h" #include "bucket_iterator.h" #include "vector.h" #include "functional.h" namespace stingray_plugin_foundation { // Implements an unordered map using a hash table. // // The hash table allocates its memory in a single block to be as cache // friendly as possible. Hash collisions are resolved by chaining into // a separate region of that memory block. This avoids clustering while // also avoiding the cache inefficiency of linked lists. // // A lookup in the spillover area will likely lead to a cache miss, so // from a cache perspective linear probing might be more efficient. // OTOH linear probing has a tendency to cause clustering which can // heavily degrade performance. It also complicates hash table deletes. // // For a randomly distributed hash function, a lookup in this hash table // on average has too look at 1.3 nodes. The expected memory use is // // $ 1.37 n (K_s D_s + 4) $, // // where `n` is the number of elements and `K_s` is the size of // the key elements and `D_s` is the size of the data elements. The // spillover region is set to 37 % of the hash region size. Experimentally // this has been found to be a good trade off value. // // HashMap is a good choice when you need to insert and lookup continuously // (so you can't use SortMap) and you don't need to do an ordered traversal // of the items. // \tparam K the key type // \tparam D the data type // \tparam HASH hash functor, defaults to default_hash> // \tparam EQUAL equality functor, defaults to std::equal_to template , class EQUAL = equal_to > class HashMap { public: ALLOCATOR_AWARE; typedef K key_type; typedef D data_type; typedef HASH key_hash; typedef EQUAL key_equal; typedef HashMap this_type; typedef Pair value_type; // Special values for marker enum {END_OF_LIST = 0x7fffffff, UNUSED = 0xfffffffe, END_OF_FREELIST = 0xffffffff}; typedef BucketIterator iterator; typedef ConstBucketIterator const_iterator; HashMap(Allocator &a); // Creates an unordered map presized to the specified number of buckets and // spill-over buckets. It is recommended that spill = 0.37 * buckets. HashMap(unsigned buckets, unsigned spill, Allocator &a); HashMap(const HashMap &o); ~HashMap(); void operator=(const HashMap &o); void swap(HashMap &o); iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; template const_iterator find(const KEY_EQ &k) const; template iterator find(const KEY_EQ &k); Allocator & allocator() const; void reserve(unsigned size); unsigned size() const; bool empty() const; // Returns true if the key is in the hash template bool has(const KEY_EQ &k) const; template data_type &operator[](const KEY_EQ &k); template value_type &get_pair(const KEY_EQ &k); template const data_type &operator[](const KEY_EQ &k) const; // Returns the value for `k` if it exists and `def` otherwise. template data_type &get(const KEY_EQ &k, data_type &def); template const data_type &get(const KEY_EQ &k, const data_type &def) const; void clear(); // Inserts the data point (`k`, `v`) in the map. template void insert(const KEY_EQ &k, const DATA_EQ &v); template void erase(const KEY_EQ &k); // Rehashes the data to the specified number of buckets (and a spill // region of 0.37 * buckets). // You can use this to reserve memory in the hash before inserting a lot // of elements to avoid rehashing during insertion. You can also use it // to shrink a hash, but you cannot shrink a hash beyond its current size. void rehash(unsigned new_buckets); // Used by iterator unsigned num_buckets() const; bool bucket_valid(unsigned i) const; value_type &bucket_value(unsigned i); const value_type &bucket_value(unsigned i) const; // Serializes the hash map to the stream. template void serialize(STREAM &stream); private: template unsigned find_or_fail(const KEY_EQ &k) const; template unsigned find_or_make(const KEY_EQ &k); template void find_and_erase(const KEY_EQ &k); bool full() const; void grow(); template unsigned hash(const KEY_EQ &k) const; unsigned allocate_spill(); void free_spill(unsigned i); void clear_freelist(); void allocate_data(unsigned count); void construct(value_type &p) { new (&p) value_type(_allocator); } void destruct(value_type &p) { p.~value_type(); } struct Data { Data() : size(), marker(), value() {} unsigned size; unsigned *marker; value_type *value; }; key_hash _hash; key_equal _equal; Data _data; unsigned _used; unsigned _buckets; unsigned _spill_unused; unsigned _spill_freelist; Allocator &_allocator; }; } // namespace stingray_plugin_foundation #include "hash_map.inl"