Files
libsst/Lib/Include/ZSTL/ZHashMap.hpp
2026-04-03 00:22:39 -05:00

1231 lines
36 KiB
C++

/*
ZHashMap.hpp
Author: James Russell <jcrussell@762studios.com>
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 <ZSTL/ZPair.hpp>
#include <ZSTL/ZArray.hpp>
#include <ZSTL/ZList.hpp>
//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 <typename K, typename V, typename HT>
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<K, V, HT, H, LF, LA, AA>::Clear struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_ClearImpl {
inline static void Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::ContainsKey struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_ContainsKeyImpl {
inline static bool Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::ContainsValue struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_ContainsValueImpl {
inline static bool Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const V& _value);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::Empty struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_EmptyImpl {
inline static bool Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::Erase struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_EraseImpl {
inline static void Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key);
inline static void Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, ZHashMapIterator<K, V, HT>& _itr);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::Get struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_GetImpl {
inline static V& Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::Put struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_PutImpl {
inline static void Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key);
inline static void Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key, const V& _value);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::Resize struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_ResizeImpl {
inline static void Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, size_t _buckets);
};
//Forward Declaration of ZHashMap<K, V, HT, H, LF, LA, AA>::Size struct
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
struct ZHashMap_SizeImpl {
inline static size_t Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self);
};
///////////////////////////////////////
/* Hash Comparator, Hasher, HashNode */
///////////////////////////////////////
/*
Hash comparator class, which compares elements with the == operator.
*/
template <typename T>
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 <typename T, typename HT>
class ZHasher
{
public:
//Comparator for use with equals and less than
ZHashComparator<T> 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 <typename K, typename V, typename HT>
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 <typename K, typename V, typename HT>
class ZHashMapIterator
{
private:
//The current node we are pointing to
ZListNode< ZHashNode<K, V, HT> > *Node;
//The end node of the map
ZListNode< ZHashNode<K, V, HT> > *EndNode;
public:
/*
Default Constructor.
*/
ZHashMapIterator()
: Node(NULL), EndNode(NULL) { }
/*
Useful constructor.
@param _node - the node we are to begin iterating at
*/
ZHashMapIterator(ZListNode< ZHashNode<K, V, HT> >* _node, ZListNode< ZHashNode<K, V, HT> >* _endNode)
: Node(_node), EndNode(_endNode) { }
/*
Copy Constructor.
@param _other - the other iterator
*/
ZHashMapIterator(const ZHashMapIterator& _other)
: Node(_other.Node), EndNode(_other.EndNode) { }
/*
public ZHashMapIterator<K, V, HT>::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<K, V, HT>::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<K, V, HT>::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<K, V, HT>::GetNode
Gets the node that this iterator is currently pointed at.
@return (ZListNode<T>*) - the node we are currently pointed at
*/
ZListNode< ZHashNode<K, V, HT> >* GetNode() const
{ return Node; }
/*
public ZHashMapIterator<K, V, HT>::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<K, V, HT> >* _node)
{ Node = _node; }
/*
public ZHashMapIterator<K, V, HT>::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<K, V, HT>::GetValue
Gets the value this iterator points to.
@return (V&) - the value pointed to
*/
V& GetValue() const
{ CheckNodeCurrent(); return Node->Element.Value; }
/*
public ZHashMapIterator<K, V, HT>::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<K, V, HT>::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<K, V, HT>::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<K, V, HT>::Next
Advances this iterator to the next element.
@return (void)
@assert - if this would advance past end
*/
void Next()
{ ++(*this); }
/*
public ZHashMapIterator<K, V, HT>::Prev
Returns this iterator to the previous element.
@return (void)
@assert - if this would advance past begin
*/
void Prev()
{ --(*this); }
/*
public ZHashMapIterator<K, V, HT>::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<K, V, HT> > *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<K, V, HT> > *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<K, V, HT>& 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<K, V, HT>.
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 <typename K, typename V,
typename HT = ZHashValue, typename H = ZHasher<K, HT>, size_t LF = ZHASHMAP_DEFAULT_LOADFACTOR,
typename LA = ZListPooledAllocator< ZHashNode<K, V, HT>, ZHASHMAP_DEFAULT_LOCAL_NODES >,
typename AA = ZArrayAllocator< ZListNode< ZHashNode<K, V, HT> >*, ZHASHMAP_DEFAULT_BUCKETS> >
class ZHashMap
{
friend struct ZHashMap_ClearImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_ContainsKeyImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_ContainsValueImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_EmptyImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_EraseImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_GetImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_PutImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_ResizeImpl<K, V, HT, H, LF, LA, AA>;
friend struct ZHashMap_SizeImpl<K, V, HT, H, LF, LA, AA>;
private:
H Hasher; //The hasher for our keys
LA NodeAllocator; //Allocator for chained hashing
size_t ElementCount; //The element count
ZArray< ZListNode< ZHashNode<K, V, HT> >*, AA> Map; //The map
ZListNode< ZHashNode<K, V, HT> > 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<K, V, HT, H, LF, LA, AA>& _other)
: ElementCount(_other.Size()), Map(_other.Size())
{
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
Map.Resize(_other.Size(), NULL);
for (typename ZHashMap<K, V, HT, H, LF, LA, AA>::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 <typename OHT, typename OH, size_t OLF, typename OLA, typename OAA>
ZHashMap(const ZHashMap<K, V, OHT, OH, OLF, OLA, OAA>& _other)
: ElementCount(_other.Size()), Map(_other.Size())
{
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
Map.Resize(_other.Map.Size(), NULL);
for (typename ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>) - this hash map
*/
ZHashMap<K, V, HT, H, LF, LA, AA>& operator = (const ZHashMap<K, V, HT, H, LF, LA, AA>& _other)
{
Clear();
ElementCount = _other.ElementCount;
Map.Resize(_other.Map.Size());
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
for (typename ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>) - this hash map
*/
template <typename OHT, typename OH, size_t OLF, typename OLA, typename OAA>
ZHashMap<K, V, HT, H, LF, LA, AA>& operator = (const ZHashMap<K, V, OHT, OH, OLF, OLA, OAA>& _other)
{
Clear();
ElementCount = _other.ElementCount;
Map.Resize(_other.Map.Size()());
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
for (typename ZHashMap<K, V, HT, H, LF, LA, AA>::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) {
Put((*itr).Key, (*itr).Value);
}
return *this;
}
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, H, LF, lA, AA>::Begin
Gets an iterator to the first element in the hash map. Keep in mind
that hash map elements are unordered.
@return (ZHashMap<K, V, HT, H, LF, LA, AA>::Iterator) - iterator to the first element
*/
Iterator Begin() const
{ return ZHashMapIterator< K, V, HT >(const_cast<ZListNode< ZHashNode<K, V, HT> >* >(&EmptyNode)->Next,
const_cast<ZListNode< ZHashNode<K, V, HT> >* >(&EmptyNode)); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::Clear
Clears the hash map of all keys and values.
@return (void)
*/
void Clear()
{ ZHashMap_ClearImpl<K, V, HT, H, LF, LA, AA>::Call(*this); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this, _key); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this, _value); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this); }
/*
public ZHashMap<K, V, H, LF, lA, AA>::End
Gets an iterator to the end node in the hash map. Keep in mind
that hash map elements are unordered.
@return (ZHashMap<K, V, HT, H, LF, LA, AA>::Iterator) - iterator to the end node
*/
Iterator End() const
{ return ZHashMapIterator< K, V, HT >(const_cast<ZListNode< ZHashNode<K, V, HT> >* >(&EmptyNode),
const_cast<ZListNode< ZHashNode<K, V, HT> >* >(&EmptyNode)); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this, _key); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this, _itr); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::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<K, V, HT> >* 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<K, V, HT> > *>(&EmptyNode));
node = node->Next;
}
return End();
}
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this, _key); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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 <typename C>
void Keys(C& _container) const
{
Iterator itr = Begin();
while (itr.HasCurrent())
{
_container.PushBack((*itr).Key);
itr.Next();
}
}
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::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<K, V> and have a 'PushBack' method
@param _container - the container to put the keys into
@return (ZList<ZPair<K, V>>)- list of key-value pairs
*/
template <typename C>
void Mappings(C& _container) const
{
Iterator itr = Begin();
while (itr.HasCurrent())
{
_container.PushBack(ZPair<K, V>((*itr).Key, (*itr).Value));
itr.Next();
}
}
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this, _key); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this, _key, _value); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>&) - this map
*/
void Resize(size_t _buckets)
{ ZHashMap_ResizeImpl<K, V, HT, H, LF, LA, AA>::Call(*this, _buckets); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<K, V, HT, H, LF, LA, AA>::Call(*this); }
/*
public ZHashMap<K, V, HT, H, LF, LA, AA>::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<V>)- ZList containing all the values in the map
*/
template <typename C>
void Values(C& _container) const
{
Iterator itr = Begin();
while (itr.HasCurrent())
{
_container.PushBack((*itr).Value);
itr.Next();
}
}
};
////////////////////////////////////////////
/* Non-Specialized Method Implementations */
////////////////////////////////////////////
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
void ZHashMap_ClearImpl<K, V, HT, H, LF, LA, AA>::Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self)
{
ZListNode< ZHashNode<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
bool ZHashMap_ContainsKeyImpl<K, V, HT, H, LF, LA, AA>::Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key)
{
HT hash = _self.Hasher.Hash(_key);
size_t bucket = _self.GetBucket(hash);
ZListNode< ZHashNode<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
bool ZHashMap_ContainsValueImpl<K, V, HT, H, LF, LA, AA>::Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const V& _value)
{
typename ZHashMap<K, V, HT, H, LF, LA, AA>::Iterator itr = _self.Begin();
while (itr.HasCurrent())
{
if ((*itr).Value == _value)
return true;
itr.Next();
}
return false;
}
/*************************************************************************/
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
bool ZHashMap_EmptyImpl<K, V, HT, H, LF, LA, AA>::Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self)
{
return _self.ElementCount == 0;
}
/*************************************************************************/
template <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
void ZHashMap_EraseImpl<K, V, HT, H, LF, LA, AA>::Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key)
{
HT hash = _self.Hasher.Hash(_key);
size_t bucket = _self.GetBucket(hash);
ZListNode< ZHashNode<K, V, HT> >* 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<K, V, HT> >* prevNode = node->Previous;
ZListNode< ZHashNode<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
void ZHashMap_EraseImpl<K, V, HT, H, LF, LA, AA>::Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, ZHashMapIterator<K, V, HT>& _itr)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_itr != _self.End(), "ZHashMap: Cannot Erase End iterator!");
#endif
ZListNode< ZHashNode<K, V, HT> >* node = _itr.GetNode();
ZListNode< ZHashNode<K, V, HT> >* prevNode = node->Previous;
ZListNode< ZHashNode<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
V& ZHashMap_GetImpl<K, V, HT, H, LF, LA, AA>::Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key)
{
HT hash = _self.Hasher.Hash(_key);
size_t bucket = _self.GetBucket(hash);
ZListNode< ZHashNode<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
void ZHashMap_PutImpl<K, V, HT, H, LF, LA, AA>::Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key)
{
_self.CheckLoadFactor();
HT hash = _self.Hasher.Hash(_key);
size_t bucket = _self.GetBucket(hash);
ZListNode< ZHashNode<K, V, HT> >* 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<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
void ZHashMap_PutImpl<K, V, HT, H, LF, LA, AA>::Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _self, const K& _key, const V& _value)
{
_self.CheckLoadFactor();
HT hash = _self.Hasher.Hash(_key);
size_t bucket = _self.GetBucket(hash);
ZListNode< ZHashNode<K, V, HT> >* 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<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
void ZHashMap_ResizeImpl<K, V, HT, H, LF, LA, AA>::Call(ZHashMap<K, V, HT, H, LF, LA, AA>& _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<K, V, HT> >* 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<K, V, HT> >* curNode = node;
ZListNode< ZHashNode<K, V, HT> >* 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 <typename K, typename V, typename HT, typename H, size_t LF, typename LA, typename AA>
size_t ZHashMap_SizeImpl<K, V, HT, H, LF, LA, AA>::Call(const ZHashMap<K, V, HT, H, LF, LA, AA>& _self)
{
return _self.ElementCount;
}
#endif