/* ZHashMap.hpp Author: James Russell Created: 12/24/2011 Purpose: Templated hash map implementation. License: This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See http://sam.zoy.org/wtfpl/COPYING for more details. */ #pragma once #ifndef _ZHASHMAP_HPP #define _ZHASHMAP_HPP #include #include #include //Default number of buckets #ifndef ZHASHMAP_DEFAULT_BUCKETS #define ZHASHMAP_DEFAULT_BUCKETS (31) #endif //Default load factor for the HashMap (expressed as an integer percentage LF / 100) #ifndef ZHASHMAP_DEFAULT_LOADFACTOR #define ZHASHMAP_DEFAULT_LOADFACTOR (75) #endif //Factor by which HashMap grows when loadfactor is exceeded #ifndef ZHASHMAP_DEFAULT_GROWFACTOR #define ZHASHMAP_DEFAULT_GROWFACTOR (2.0) #endif //Number of nodes for the lists stored locally (safe since we never splice) #ifndef ZHASHMAP_DEFAULT_LOCAL_NODES #define ZHASHMAP_DEFAULT_LOCAL_NODES (10) #endif //Forward Declaration of ZHashMap template < typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA > class ZHashMap; //Forward Declaration of ZHashMapIterator template class ZHashMapIterator; /* Forward Declaration of ZHashMap Method Implementation Structures Existence of these structures allows for template specialization of individual methods of the ZHashMap class. In order to specialize a single method of ZHashMap, simply specialize the corresponding method implementation structure. */ //Forward Declaration of ZHashMap::Clear struct template struct ZHashMap_ClearImpl { inline static void Call(ZHashMap& _self); }; //Forward Declaration of ZHashMap::ContainsKey struct template struct ZHashMap_ContainsKeyImpl { inline static bool Call(const ZHashMap& _self, const K& _key); }; //Forward Declaration of ZHashMap::ContainsValue struct template struct ZHashMap_ContainsValueImpl { inline static bool Call(const ZHashMap& _self, const V& _value); }; //Forward Declaration of ZHashMap::Empty struct template struct ZHashMap_EmptyImpl { inline static bool Call(const ZHashMap& _self); }; //Forward Declaration of ZHashMap::Erase struct template struct ZHashMap_EraseImpl { inline static void Call(ZHashMap& _self, const K& _key); inline static void Call(ZHashMap& _self, ZHashMapIterator& _itr); }; //Forward Declaration of ZHashMap::Get struct template struct ZHashMap_GetImpl { inline static V& Call(const ZHashMap& _self, const K& _key); }; //Forward Declaration of ZHashMap::Put struct template struct ZHashMap_PutImpl { inline static void Call(ZHashMap& _self, const K& _key); inline static void Call(ZHashMap& _self, const K& _key, const V& _value); }; //Forward Declaration of ZHashMap::Resize struct template struct ZHashMap_ResizeImpl { inline static void Call(ZHashMap& _self, size_t _buckets); }; //Forward Declaration of ZHashMap::Size struct template struct ZHashMap_SizeImpl { inline static size_t Call(const ZHashMap& _self); }; /////////////////////////////////////// /* Hash Comparator, Hasher, HashNode */ /////////////////////////////////////// /* Hash comparator class, which compares elements with the == operator. */ template struct ZHashComparator { ZHashComparator() { } //Silence Sun C++ warnings about uninitialized structs //operator overload which returns true if equivalent (as per ==), false otherwise bool operator () (const T& _a, const T& _b) const { if ((T)_a == (T)_b) return true; else return false; } }; /* Hasher class, which produces hash values of objects for use in ZHashMap. The template parameter T is the type we will be hashing. The template parameter HT is the hash type that is computed. */ template class ZHasher { public: //Comparator for use with equals and less than ZHashComparator Comp; //Default hash function, which attempts to cast the object as a hash value HT Hash(const T& _obj) const { return (HT)(uintptr_t)_obj; //Since 'HT' may be 32-bit, but '_obj' may be a pointer, this can cause compile errors. } //Default comparison function, which compares objects (should return 0 if not equal, !0 otherwise) int Equals(const T& _first, const T& _second) const { return Comp(_first, _second); } }; /* Hash node structure, which contains a key, a value, and the pre-computed hash for the key as well as the hash mod (bucket). This node is stored within a ZListNode for hash chaining. The template parameter K is the key type. The template parameter V is the value type. The template parameter HT is the hash type. */ template struct ZHashNode { ZHashNode() { } //Silence Sun C++ warnings about uninitialized structs K Key; //The contained key V Value; //The contained value HT Hash; //The hash of the key size_t HashMod; //The hash mod number of buckets (gives bucket) }; /////////////////////// /* ZHashMap Iterator */ /////////////////////// /* Iterator for ZHashMap. The template parameter K is the key type. The template parameter V is the value type. The template parameter HT is the hash type. */ template class ZHashMapIterator { private: //The current node we are pointing to ZListNode< ZHashNode > *Node; //The end node of the map ZListNode< ZHashNode > *EndNode; public: /* Default Constructor. */ ZHashMapIterator() : Node(NULL), EndNode(NULL) { } /* Useful constructor. @param _node - the node we are to begin iterating at */ ZHashMapIterator(ZListNode< ZHashNode >* _node, ZListNode< ZHashNode >* _endNode) : Node(_node), EndNode(_endNode) { } /* Copy Constructor. @param _other - the other iterator */ ZHashMapIterator(const ZHashMapIterator& _other) : Node(_other.Node), EndNode(_other.EndNode) { } /* public ZHashMapIterator::CheckNodeCurrent Node check function that does not allow the current node to point to NULL. @return (void) */ void CheckNodeCurrent() const { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(Node != NULL, "ZHashMap Iterator Invalid!"); #endif } /* public ZHashMapIterator::CheckNodeNext Node check function that does not allow Node to be 'End' @return (void) */ void CheckNodeNext() const { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(Node != NULL && Node != EndNode, "ZHashMap Iterator Next Invalid!"); #endif } /* public ZHashMapIterator::CheckNodePrevious Node check function that does not allow Node to be 'Begin' @return (void) */ void CheckNodePrevious() const { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(Node != NULL && Node->Previous != EndNode, "ZHashMap Iterator Previous Invalid!"); #endif } /* public ZHashMapIterator::GetNode Gets the node that this iterator is currently pointed at. @return (ZListNode*) - the node we are currently pointed at */ ZListNode< ZHashNode >* GetNode() const { return Node; } /* public ZHashMapIterator::SetNode Sets the current node for this iterator. Useful for when the underlying list changes state. @param _node - the node to point this iterator at @return (void) */ void SetNode(ZListNode< ZHashNode >* _node) { Node = _node; } /* public ZHashMapIterator::GetKey Gets the key this iterator points to. @return (const K&) - the key pointed to */ const K& GetKey() const { CheckNodeCurrent(); return Node->Element.Key; } /* public ZHashMapIterator::GetValue Gets the value this iterator points to. @return (V&) - the value pointed to */ V& GetValue() const { CheckNodeCurrent(); return Node->Element.Value; } /* public ZHashMapIterator::HasCurrent Determines if this iterator currently points to a valid element. @return (bool) - true if element at current position */ bool HasCurrent() const { return Node != NULL && Node != EndNode; } /* public ZHashMapIterator::HasNext Determines if this iterator has a valid element after the current element. @return (bool) - true if element after current, false otherwise */ bool HasNext() const { return Node != NULL && Node != EndNode && Node->Next != EndNode; } /* public ZHashMapIterator::HasPrev Determines if this iterator has a valid element before the current element. @return (bool) - true if element before current, false otherwise */ bool HasPrev() const { return Node != NULL && Node->Previous != EndNode; } /* public ZHashMapIterator::Next Advances this iterator to the next element. @return (void) @assert - if this would advance past end */ void Next() { ++(*this); } /* public ZHashMapIterator::Prev Returns this iterator to the previous element. @return (void) @assert - if this would advance past begin */ void Prev() { --(*this); } /* public ZHashMapIterator::SetValue Sets the value mapped to the given key. @return (void) @assert - if this is the end node */ void SetValue(const V& _value) { CheckNodeCurrent(); Node->Element.Value = _value; } //Operator overrides ZHashMapIterator& operator ++ () { CheckNodeNext(); Node = Node->Next; return *this; } ZHashMapIterator operator ++ (int) { ZListNode< ZHashNode > *node = Node; CheckNodeNext(); Node = Node->Next; return ZHashMapIterator(node, EndNode); } ZHashMapIterator operator + (int _value) { ZHashMapIterator itr(*this); for (int i = 0; i < _value; i++) ++itr; return itr; } ZHashMapIterator& operator += (int _value) { for (int i = 0; i < _value; i++) ++(*this); return *this; } ZHashMapIterator& operator -- () { CheckNodePrevious(); Node = Node->Previous; return *this; } ZHashMapIterator operator -- (int) { ZListNode< ZHashNode > *node = Node; CheckNodePrevious(); Node = Node->Previous; return ZHashMapIterator(node, EndNode); } ZHashMapIterator operator - (int _value) { ZHashMapIterator itr(*this); for (int i = _value; i > 0; i--) --itr; return itr; } ZHashMapIterator& operator -= (int _value) { for (int i = 0; i < _value; i--) --(*this); return *this; } ZHashMapIterator& operator = (const ZHashMapIterator &_other) { Node = _other.Node; EndNode = _other.EndNode; return *this; } bool operator == (const ZHashMapIterator& _other) const { return (Node == _other.Node) && (EndNode == _other.EndNode); } bool operator != (const ZHashMapIterator& _other) const { return !( (*this)==_other ); } const ZHashNode& operator * () const { CheckNodeCurrent(); return Node->Element; } }; ///////////////////////////// /* ZHashMap Implementation */ ///////////////////////////// /* Templated dynamic hash map implementation. Uses chained hashing and (optional) load factor balancing to ensure it behaves gracefully even under heavy load. This implementation does not maintain order between keys. The template parameter K is the key type, which must be hashable. The template parameter V is the value type, which need not be hashable. The template parameter HT is the hash type. This should generally correspond to something like a 32-bit or 64-bit unsigned integer. It must be compatible with the modulo operator. The template parameter H is the hasher type, which defaults to the ZHasher for K using hash type HT. The hasher must expose the same methods as ZHasher (Hash, Equals). The template parameter LF is the load factor of the hash map given as an integral value between 0 (hashmap will not resize) and 100. Load factor is then computed as (double)(LF / 100). Whenever the current load factor exceeds this value, the number of buckets is increased (map is resized). The template parameter LA is the allocator type to use for list nodes. It must allocate ZListNode instances containing type ZHashNode. The template parameter AA is the allocator type to pass along to the contained ZArray. It must allocate arrays of type ZListNode< ZHashNode< K, V > >*. */ template , size_t LF = ZHASHMAP_DEFAULT_LOADFACTOR, typename LA = ZListPooledAllocator< ZHashNode, ZHASHMAP_DEFAULT_LOCAL_NODES >, typename AA = ZArrayAllocator< ZListNode< ZHashNode >*, ZHASHMAP_DEFAULT_BUCKETS> > class ZHashMap { friend struct ZHashMap_ClearImpl; friend struct ZHashMap_ContainsKeyImpl; friend struct ZHashMap_ContainsValueImpl; friend struct ZHashMap_EmptyImpl; friend struct ZHashMap_EraseImpl; friend struct ZHashMap_GetImpl; friend struct ZHashMap_PutImpl; friend struct ZHashMap_ResizeImpl; friend struct ZHashMap_SizeImpl; private: H Hasher; //The hasher for our keys LA NodeAllocator; //Allocator for chained hashing size_t ElementCount; //The element count ZArray< ZListNode< ZHashNode >*, AA> Map; //The map ZListNode< ZHashNode > EmptyNode; //The end node for the hash map //Resizes and copies the HashMap if we exceed LoadFactor (LF of 0 indicates we should not resize) inline void CheckLoadFactor() { //This will evaluate to false if LF == 0, true if LF > 0 (done to avoid C4127) if ((void)LF,LF) { double currentLoadFactor = LoadFactor(); if (currentLoadFactor > ((double)LF / (double)100.0)) Resize((size_t)((double)Map.Size() * ZHASHMAP_DEFAULT_GROWFACTOR)); } } //Gets a bucket given a hash code inline size_t GetBucket(HT _hash) const { return _hash % Map.Size(); } public: /* Iterator type for ZHashMap. Allows ZHashMap::Iterator notation. */ typedef ZHashMapIterator< K, V, HT > Iterator; /* Default Constructor. */ ZHashMap() : ElementCount(0), Map(ZHASHMAP_DEFAULT_BUCKETS) { EmptyNode.Next = &EmptyNode; EmptyNode.Previous = &EmptyNode; Map.Resize(ZHASHMAP_DEFAULT_BUCKETS, NULL); } /* Parameterized Constructor. @param _buckets - the number of buckets the hashmap should use */ ZHashMap(size_t _buckets) : ElementCount(0), Map(_buckets) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(_buckets > 0, "Cannot make ZHashMap with no buckets!"); #endif EmptyNode.Next = &EmptyNode; EmptyNode.Previous = &EmptyNode; Map.Resize(_buckets, NULL); } /* Copy constructor. @param _other - the other hash map */ ZHashMap(const ZHashMap& _other) : ElementCount(_other.Size()), Map(_other.Size()) { EmptyNode.Next = &EmptyNode; EmptyNode.Previous = &EmptyNode; Map.Resize(_other.Size(), NULL); for (typename ZHashMap::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) Put((*itr).Key, (*itr).Value); } /* Copy constructor that can copy hash maps with other template types. Key and Value type must be the same. @param _other - the other hash map */ template ZHashMap(const ZHashMap& _other) : ElementCount(_other.Size()), Map(_other.Size()) { EmptyNode.Next = &EmptyNode; EmptyNode.Previous = &EmptyNode; Map.Resize(_other.Map.Size(), NULL); for (typename ZHashMap::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) Put((*itr).Key, (*itr).Value); } /* = operator overload. Makes a copy of another hash map. This version is needed to override the default version. @param _other - the other hash map @return (ZHashMap) - this hash map */ ZHashMap& operator = (const ZHashMap& _other) { Clear(); ElementCount = _other.ElementCount; Map.Resize(_other.Map.Size()); EmptyNode.Next = &EmptyNode; EmptyNode.Previous = &EmptyNode; for (typename ZHashMap::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) { Put((*itr).Key, (*itr).Value); } return *this; } /* = operator overload. @param _other - the other hash map @return (ZHashMap) - this hash map */ template ZHashMap& operator = (const ZHashMap& _other) { Clear(); ElementCount = _other.ElementCount; Map.Resize(_other.Map.Size()()); EmptyNode.Next = &EmptyNode; EmptyNode.Previous = &EmptyNode; for (typename ZHashMap::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) { Put((*itr).Key, (*itr).Value); } return *this; } /* public ZHashMap::operator[] Gets a value from the map. Functionally resembles the 'Get' operation but will create a default constructed value if not assigned. @param _key - the key to lookup @return (V&) - the value mapped to _key */ V& operator [] (const K& _key) { if (!ContainsKey(_key)) Put(_key, V()); return Get(_key); } /* public ZHashMap::Begin Gets an iterator to the first element in the hash map. Keep in mind that hash map elements are unordered. @return (ZHashMap::Iterator) - iterator to the first element */ Iterator Begin() const { return ZHashMapIterator< K, V, HT >(const_cast >* >(&EmptyNode)->Next, const_cast >* >(&EmptyNode)); } /* public ZHashMap::Clear Clears the hash map of all keys and values. @return (void) */ void Clear() { ZHashMap_ClearImpl::Call(*this); } /* public ZHashMap::ContainsKey Determines if the hash map contains the given key. @param _key - the key to check @return (bool) - boolean indicating if the hash map contains the key */ bool ContainsKey(const K& _key) const { return ZHashMap_ContainsKeyImpl::Call(*this, _key); } /* public ZHashMap::ContainsValue Determines if the hash map contains the given value. @param _value - the value to check for @return (bool) - boolean indicating if the hash map contains the value */ bool ContainsValue(const V& _value) const { return ZHashMap_ContainsValueImpl::Call(*this, _value); } /* public ZHashMap::Empty Indicates whether or not the hash map is empty. @return (bool) - boolean indicating the hash map is empty (free of keys and values) */ bool Empty() const { return ZHashMap_EmptyImpl::Call(*this); } /* public ZHashMap::End Gets an iterator to the end node in the hash map. Keep in mind that hash map elements are unordered. @return (ZHashMap::Iterator) - iterator to the end node */ Iterator End() const { return ZHashMapIterator< K, V, HT >(const_cast >* >(&EmptyNode), const_cast >* >(&EmptyNode)); } /* public ZHashMap::Erase Removes the associated key and mapped value. @param _key - the key to lookup @return (void) - this hash map @assert - if the key is not mapped */ void Erase(const K& _key) { ZHashMap_EraseImpl::Call(*this, _key); } /* public ZHashMap::Erase Removes the key and mapped value given by the iterator. This version is more efficient than the key-only version because no hash lookup is performed. @param _itr - iterator to the hash node to remove @return (void) @assert - if it is the end iterator */ void Erase(Iterator& _itr) { ZHashMap_EraseImpl::Call(*this, _itr); } /* public ZHashMap::Find Returns an iterator to the element mapped to the given key. Will return the End() iterator if not mapped. @param _key - the key to look up @return (ZHashMap::Iterator) - iterator to the element, End() if not found */ Iterator Find(const K& _key) const { HT hash = Hasher.Hash(_key); size_t bucket = GetBucket(hash); ZListNode< ZHashNode >* node = Map.Data()[bucket]; while (node != NULL && node != &EmptyNode && node->Element.HashMod == bucket) { if (node->Element.Hash == hash && Hasher.Equals(_key, node->Element.Key) != 0) return Iterator(node, const_cast< ZListNode< ZHashNode > *>(&EmptyNode)); node = node->Next; } return End(); } /* public ZHashMap::Get Gets the value mapped to the given key. @param _key - the key to look up @return (V&) - the value mapped to _key @assert - if the key is not present in the map */ V& Get(const K& _key) const { return ZHashMap_GetImpl::Call(*this, _key); } /* public ZHashMap::Keys Gets a list of keys in the map. Uses the method 'PushBack' to add the keys. @param C - the container type to push the keys into, which must contain type K and have a 'PushBack' method @param _container - the container to put the keys into @return - ZList containing all the keys in the map */ template void Keys(C& _container) const { Iterator itr = Begin(); while (itr.HasCurrent()) { _container.PushBack((*itr).Key); itr.Next(); } } /* public ZHashMap::LoadFactor Returns the current load factor of the hash map, as determined by number of elements over number of buckets. @return (double) - the load factor */ double LoadFactor() const { return (double)ElementCount / (double)Map.Size(); } /* public ZHashMap::Mappings Gets a list of all the key value mappings in the hash map. Adds pairs of mappings using 'PushBack'. @param C - the container type to push the keys into, which must contain type ZPair and have a 'PushBack' method @param _container - the container to put the keys into @return (ZList>)- list of key-value pairs */ template void Mappings(C& _container) const { Iterator itr = Begin(); while (itr.HasCurrent()) { _container.PushBack(ZPair((*itr).Key, (*itr).Value)); itr.Next(); } } /* public ZHashMap::Put Puts the given key into the map with a default constructed value of type V. @param _key - the key to place in the map */ void Put(const K& _key) { return ZHashMap_PutImpl::Call(*this, _key); } /* public ZHashMap::Put Puts the given key and value combination into the hash map. @param _key - the key to associate with _value @param _value - the value to lookup using _key */ void Put(const K& _key, const V& _value) { return ZHashMap_PutImpl::Call(*this, _key, _value); } /* public ZHashMap::Resize Sets the number of buckets the hashmap will use. Existing elements will be re-hashed into the map. @param _buckets - number of buckets to use @return (ZHashMap&) - this map */ void Resize(size_t _buckets) { ZHashMap_ResizeImpl::Call(*this, _buckets); } /* public ZHashMap::Size Returns the number of mapped key-value pairs in the map. @return (size_t) - the number of key value pairs */ size_t Size() const { return ZHashMap_SizeImpl::Call(*this); } /* public ZHashMap::Values Gets a list of values in the map. @param C - the container type to push the keys into, which must contain type V and have a 'PushBack' method @param _container - the container to put the keys into @return (ZList)- ZList containing all the values in the map */ template void Values(C& _container) const { Iterator itr = Begin(); while (itr.HasCurrent()) { _container.PushBack((*itr).Value); itr.Next(); } } }; //////////////////////////////////////////// /* Non-Specialized Method Implementations */ //////////////////////////////////////////// template void ZHashMap_ClearImpl::Call(ZHashMap& _self) { ZListNode< ZHashNode >* node = _self.EmptyNode.Next; while (node != &_self.EmptyNode) { node = node->Next; _self.NodeAllocator.Deallocate(node->Previous); } for (size_t i = 0; i < _self.Map.Size(); i++) _self.Map.Data()[i] = NULL; _self.EmptyNode.Next = &_self.EmptyNode; _self.EmptyNode.Previous = &_self.EmptyNode; _self.ElementCount = 0; } /*************************************************************************/ template bool ZHashMap_ContainsKeyImpl::Call(const ZHashMap& _self, const K& _key) { HT hash = _self.Hasher.Hash(_key); size_t bucket = _self.GetBucket(hash); ZListNode< ZHashNode >* node = _self.Map.Data()[bucket]; while (node != NULL && node != &_self.EmptyNode && node->Element.HashMod == bucket) { if (node->Element.Hash == hash && _self.Hasher.Equals(_key, node->Element.Key) != 0) return true; node = node->Next; } return false; } /*************************************************************************/ template bool ZHashMap_ContainsValueImpl::Call(const ZHashMap& _self, const V& _value) { typename ZHashMap::Iterator itr = _self.Begin(); while (itr.HasCurrent()) { if ((*itr).Value == _value) return true; itr.Next(); } return false; } /*************************************************************************/ template bool ZHashMap_EmptyImpl::Call(const ZHashMap& _self) { return _self.ElementCount == 0; } /*************************************************************************/ template void ZHashMap_EraseImpl::Call(ZHashMap& _self, const K& _key) { HT hash = _self.Hasher.Hash(_key); size_t bucket = _self.GetBucket(hash); ZListNode< ZHashNode >* node = _self.Map.Data()[bucket]; while (node != NULL && node != &_self.EmptyNode && node->Element.HashMod == bucket) { if (node->Element.Hash == hash && _self.Hasher.Equals(_key, node->Element.Key) != 0) { ZListNode< ZHashNode >* prevNode = node->Previous; ZListNode< ZHashNode >* nextNode = node->Next; prevNode->Next = nextNode; nextNode->Previous = prevNode; //If the bucket maps to this node, it must change (either Next or NULL) if (_self.Map.Data()[bucket] == node) { if (nextNode->Element.HashMod == bucket) _self.Map.Data()[bucket] = nextNode; else _self.Map.Data()[bucket] = NULL; } _self.NodeAllocator.Deallocate(node); _self.ElementCount--; return; } node = node->Next; } } /*************************************************************************/ template void ZHashMap_EraseImpl::Call(ZHashMap& _self, ZHashMapIterator& _itr) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(_itr != _self.End(), "ZHashMap: Cannot Erase End iterator!"); #endif ZListNode< ZHashNode >* node = _itr.GetNode(); ZListNode< ZHashNode >* prevNode = node->Previous; ZListNode< ZHashNode >* nextNode = node->Next; size_t bucket = node->Element.HashMod; prevNode->Next = nextNode; nextNode->Previous = prevNode; //If the bucket maps to this node, it must change (either Next or NULL) if (_self.Map.Data()[node->Element.HashMod] == node) { if (nextNode->Element.HashMod == bucket) _self.Map.Data()[bucket] = nextNode; else _self.Map.Data()[bucket] = NULL; } _self.NodeAllocator.Deallocate(node); _self.ElementCount--; } /*************************************************************************/ template V& ZHashMap_GetImpl::Call(const ZHashMap& _self, const K& _key) { HT hash = _self.Hasher.Hash(_key); size_t bucket = _self.GetBucket(hash); ZListNode< ZHashNode >* node = _self.Map.Data()[bucket]; while (node != NULL && node != &_self.EmptyNode && node->Element.HashMod == bucket) { if (node->Element.Hash == hash && _self.Hasher.Equals(_key, node->Element.Key) != 0) return node->Element.Value; node = node->Next; } #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ERROR("ZHashMap: Get could not find value!"); #endif //If the runtime assert is ignored and we continue, return ref to a static local variable //This is effectively 'undefined' behavior static V val; return val; } /*************************************************************************/ template void ZHashMap_PutImpl::Call(ZHashMap& _self, const K& _key) { _self.CheckLoadFactor(); HT hash = _self.Hasher.Hash(_key); size_t bucket = _self.GetBucket(hash); ZListNode< ZHashNode >* node = _self.Map.Data()[bucket]; if (node == NULL) { //Empty bucket, so search for the next node for (size_t i = bucket + 1; i < _self.Map.Size(); i++) { node = _self.Map.Data()[i]; if (node != NULL) break; } //No node was found, so use the empty node if (node == NULL) node = &_self.EmptyNode; } else { //Non-empty bucket, so see if we are already mapped while (node->Element.HashMod == bucket) { if (node->Element.Hash == hash && _self.Hasher.Equals(_key, node->Element.Key) != 0) return; node = node->Next; } } //Not mapped, so insert (node is currently one past where we should be) ZListNode< ZHashNode >* newNode = _self.NodeAllocator.Allocate(); #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(newNode != NULL, "ZHashMap: Unable to allocate hash node!"); #endif //If the bucket has NULL linkage, set it if (_self.Map.Data()[bucket] == NULL) _self.Map.Data()[bucket] = newNode; newNode->Next = node; newNode->Previous = node->Previous; newNode->Element.Key = _key; newNode->Element.Value = V(); //Be sure to default construct the value instance newNode->Element.Hash = hash; newNode->Element.HashMod = bucket; newNode->Previous->Next = newNode; newNode->Next->Previous = newNode; _self.ElementCount++; } /*************************************************************************/ template void ZHashMap_PutImpl::Call(ZHashMap& _self, const K& _key, const V& _value) { _self.CheckLoadFactor(); HT hash = _self.Hasher.Hash(_key); size_t bucket = _self.GetBucket(hash); ZListNode< ZHashNode >* node = _self.Map.Data()[bucket]; if (node == NULL) { //Empty bucket, so search for the next node for (size_t i = bucket + 1; i < _self.Map.Size(); i++) { node = _self.Map.Data()[i]; if (node != NULL) break; } //No node was found, so use the empty node if (node == NULL) node = &_self.EmptyNode; } else { //Non-empty bucket, so see if we are already mapped while (node->Element.HashMod == bucket) { if (node->Element.Hash == hash && _self.Hasher.Equals(_key, node->Element.Key) != 0) { node->Element.Value = _value; return; } node = node->Next; } } //Not mapped, so insert (node is currently one past where we should be) ZListNode< ZHashNode >* newNode = _self.NodeAllocator.Allocate(); #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(newNode != NULL, "ZHashMap: Unable to allocate hash node!"); #endif //If the bucket has NULL linkage, set it if (_self.Map.Data()[bucket] == NULL) _self.Map.Data()[bucket] = newNode; newNode->Next = node; newNode->Previous = node->Previous; newNode->Element.Key = _key; newNode->Element.Value = _value; newNode->Element.Hash = hash; newNode->Element.HashMod = bucket; newNode->Previous->Next = newNode; newNode->Next->Previous = newNode; _self.ElementCount++; } /*************************************************************************/ template void ZHashMap_ResizeImpl::Call(ZHashMap& _self, size_t _buckets) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(_buckets > 0, "ZHashMap: Unable to resize to zero buckets!"); #endif //Get a pointer to the first non-empty element (which may itself be the empty element) ZListNode< ZHashNode >* node = _self.EmptyNode.Next; //Clear out the map completely for (size_t i = 0; i < _self.Map.Size(); i++) _self.Map.Data()[i] = NULL; _self.EmptyNode.Next = &_self.EmptyNode; _self.EmptyNode.Previous = &_self.EmptyNode; _self.Map.Resize(_buckets, NULL); //Loop through our previous nodes and place them back in the map in their new spots while (node != &_self.EmptyNode) { //Compute new bucket node->Element.HashMod = node->Element.Hash % _buckets; //Determine where we should be placing this node ZListNode< ZHashNode >* curNode = node; ZListNode< ZHashNode >* nextNode = _self.Map.Data()[node->Element.HashMod]; //Get the next node to place now, before we scribble the pointers node = curNode->Next; //If the bucket has NULL linkage, next node if (nextNode == NULL) { //Empty bucket, so search for the next nextNode for (size_t i = curNode->Element.HashMod + 1; i < _self.Map.Size(); i++) { nextNode = _self.Map.Data()[i]; if (nextNode != NULL) break; } //No nextNode was found, so use the empty nextNode if (nextNode == NULL) nextNode = &_self.EmptyNode; } //Reestablish linkage curNode->Next = nextNode; curNode->Previous = nextNode->Previous; curNode->Previous->Next = curNode; curNode->Next->Previous = curNode; //Since we are pushing front, this always means we set the pointer _self.Map.Data()[curNode->Element.HashMod] = curNode; } } /*************************************************************************/ template size_t ZHashMap_SizeImpl::Call(const ZHashMap& _self) { return _self.ElementCount; } #endif