#pragma once
#include "Hash.hpp"
#include "../iterator/MapIterator.hpp"
#include "../accessor/Accessors.hpp"
#include "../accessor/AccessorGroup_Windows.hpp"
#include "../../memory/PoolAllocator.hpp"
#include "../../common/specialized.hpp"
#include <xtr1common>
using std::_Nil;
namespace sprawl
{
namespace collections
{
namespace detail
{
template<typename ValueType, typename mapped_type, size_t Idx, typename ThisAccessor = _Nil, _MAX_CLASS_LIST>
class HashMap_Impl;
template< typename ValueType, typename mapped_type, size_t Idx>
class HashMap_Impl<ValueType, mapped_type, Idx, _Nil, _MAX_NIL_LIST >
{
public:
typedef ValueType value_type;
typedef MapIterator<ValueType, mapped_type> iterator;
typedef MapIterator<ValueType, mapped_type> const const_iterator;
typedef sprawl::memory::DynamicPoolAllocator<sizeof(mapped_type)> allocator;
template<typename RequestedKeyType>
inline void get(RequestedKeyType const&, Specialized<Idx>)
{
RequestedKeyType::error_invalid_key_index_combination();
}
template<typename RequestedKeyType>
inline iterator find(RequestedKeyType const&, Specialized<Idx>)
{
RequestedKeyType::error_invalid_key_index_combination();
}
template<typename RequestedKeyType>
inline const_iterator find(RequestedKeyType const&, Specialized<Idx>) const
{
RequestedKeyType::error_invalid_key_index_combination();
}
template<typename RequestedKeyType>
inline const_iterator cfind(RequestedKeyType const&, Specialized<Idx>) const
{
RequestedKeyType::error_invalid_key_index_combination();
}
template<typename RequestedKeyType>
inline void erase(RequestedKeyType const&, Specialized<Idx>)
{
RequestedKeyType::error_invalid_key_index_combination();
}
inline size_t bucketSize(int, Specialized<Idx>) const
{
return ValueType::error_invalid_index();
}
inline iterator begin()
{
return iterator(m_first);
}
inline const_iterator begin() const
{
return cbegin();
}
inline const_iterator cbegin() const
{
return const_iterator(m_first);
}
inline iterator end()
{
return iterator(nullptr);
}
inline const_iterator end() const
{
return cend();
}
inline const_iterator cend() const
{
return const_iterator(nullptr);
}
template<typename RequestedKeyType>
inline void has(RequestedKeyType const&, Specialized<Idx>)
{
RequestedKeyType::error_invalid_key_index_combination();
}
inline size_t size()
{
return m_size;
}
inline size_t bucketCount() const
{
return m_bucketCount;
}
inline bool empty()
{
return m_size == 0;
}
void clear()
{
mapped_type* ptr_next = nullptr;
for (mapped_type* ptr = m_first; ptr != nullptr; ptr = ptr_next)
{
ptr_next = ptr->next;
ptr->~mapped_type();
allocator::free(ptr);
}
m_first = nullptr;
m_last = nullptr;
}
inline void rehash()
{
for (mapped_type* ptr = m_first; ptr != nullptr; ptr = ptr->next)
{
reinsert_(ptr);
}
}
virtual ~HashMap_Impl()
{
clear();
}
protected:
inline bool check_and_insert_(mapped_type* newItem)
{
insert_(newItem);
return true;
}
inline void reinsert_(mapped_type*)
{
//
}
inline void reserve_(size_t newBucketCount)
{
m_bucketCount = newBucketCount;
}
inline void nullout_(mapped_type*)
{
//
}
inline void insert_(mapped_type* newItem)
{
newItem->prev = m_last;
if (m_last != nullptr)
{
m_last->next = newItem;
}
if (m_first == nullptr)
{
m_first = newItem;
}
m_last = newItem;
++m_size;
}
void erase_(mapped_type* item)
{
if(item == m_first)
{
m_first = item->next;
}
if(item == m_last)
{
m_last = item->prev;
}
if(item->prev)
{
item->prev->next = item->next;
}
if(item->next)
{
item->next->prev = item->prev;
}
}
HashMap_Impl(size_t startingBucketCount)
: m_first(nullptr)
, m_last(nullptr)
, m_size(0)
, m_bucketCount(startingBucketCount)
{
//
}
HashMap_Impl(HashMap_Impl const& other)
: m_first(nullptr)
, m_last(nullptr)
, m_size(other.m_size)
, m_bucketCount(other.m_bucketCount)
{
//
}
HashMap_Impl(HashMap_Impl&& other)
: m_first(other.m_first)
, m_last(other.m_last)
, m_size(other.m_size)
, m_bucketCount(other.m_bucketCount)
{
other.m_first = nullptr;
other.m_last = nullptr;
other.m_size = 0;
}
mapped_type* m_first;
mapped_type* m_last;
size_t m_size;
size_t m_bucketCount;
};
#define _CLASS_HASHMAP_IMPL( \
TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \
template< typename ValueType, typename mapped_type, size_t Idx, typename Accessor COMMA LIST(_CLASS_TYPEX)> \
class HashMap_Impl<ValueType, mapped_type, Idx, Accessor, LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD)> \
: public HashMap_Impl< ValueType, mapped_type, Idx + 1, LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD) > \
{ \
public: \
typedef HashMap_Impl<ValueType, mapped_type, Idx + 1, LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD)> Base; \
\
typedef ValueType value_type; \
\
typedef MapIterator<ValueType, mapped_type> iterator; \
typedef MapIterator<ValueType, mapped_type> const const_iterator; \
typedef sprawl::memory::DynamicPoolAllocator<sizeof(mapped_type)> allocator; \
\
using Base::get; \
inline ValueType& get(typename Accessor::key_type const& key, Specialized<Idx> = Specialized<Idx>()) \
{ \
return get_(key)->m_value; \
} \
inline ValueType const& get(typename Accessor::key_type const& key, Specialized<Idx> = Specialized<Idx>()) const \
{ \
return get_(key)->m_value; \
} \
\
using Base::find; \
using Base::cfind; \
inline iterator find(typename Accessor::key_type const& key, Specialized<Idx> = Specialized<Idx>()) \
{ \
mapped_type* ret = get_(key); \
return iterator(ret); \
} \
\
inline const_iterator find(typename Accessor::key_type const& key, Specialized<Idx> = Specialized<Idx>()) const \
{ \
return cfind(key); \
} \
\
inline const_iterator cfind(typename Accessor::key_type const& key, Specialized<Idx> = Specialized<Idx>()) const \
{ \
mapped_type* ret = const_cast<mapped_type*>(get_(key)); \
return const_iterator(ret); \
} \
\
using Base::has; \
inline bool has(typename Accessor::key_type const& key, Specialized<Idx> = Specialized<Idx>()) \
{ \
return get_(key) != nullptr; \
} \
\
template<int index> \
inline int bucketSize(int i, Specialized<Idx> spec = Specialized<Idx>()) const \
{ \
int ret = 0; \
mapped_type *item = m_thisKeyTable[i]; \
while(item) \
{ \
++ret; \
item = item->Next(spec); \
} \
return ret; \
} \
\
inline void clear() \
{ \
for (size_t i = 0; i < this->m_bucketCount; ++i) \
{ \
m_thisKeyTable[i] = nullptr; \
} \
Base::clear(); \
} \
\
inline iterator insert(ValueType const& val) \
{ \
mapped_type* newItem = (mapped_type*)allocator::alloc(); \
::new((void*)newItem) mapped_type(val); \
\
if(!check_and_insert_(newItem)) \
{ \
newItem->~mapped_type(); \
allocator::free(newItem); \
return iterator(nullptr); \
} \
\
if(this->m_size > (this->m_bucketCount*0.5)) \
{ \
reserve(this->m_bucketCount * 2 + 1); \
} \
\
return iterator(newItem); \
} \
\
_HASHMAP_VARIADICS( TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \
\
void reserve(size_t newBucketCount) \
{ \
reserve_(newBucketCount); \
\
for (mapped_type* ptr = this->m_first; ptr != nullptr; ptr = ptr->next) \
{ \
nullout_(ptr); \
reinsert_(ptr); \
} \
} \
\
inline void rehash() \
{ \
rehash_(); \
Base::rehash(); \
} \
\
using Base::erase; \
inline void erase(typename Accessor::key_type const& key, Specialized<Idx> = Specialized<Idx>()) \
{ \
erase_(get_(key)); \
} \
\
virtual ~HashMap_Impl() \
{ \
if(m_thisKeyTable) \
{ \
allocator::free(m_thisKeyTable); \
} \
} \
\
protected: \
HashMap_Impl(size_t startingBucketCount) \
: Base(startingBucketCount) \
, m_thisKeyTable(nullptr) \
{ \
\
} \
\
HashMap_Impl(HashMap_Impl const& other) \
: Base(other) \
, m_thisKeyTable(nullptr) \
{ \
\
} \
\
HashMap_Impl(HashMap_Impl&& other) \
: Base(std::move(other)) \
, m_thisKeyTable(other.m_thisKeyTable) \
{ \
other.m_thisKeyTable = nullptr; \
} \
\
void reserve_(size_t newBucketCount) \
{ \
if(m_thisKeyTable) \
{ \
allocator::free(m_thisKeyTable); \
} \
m_thisKeyTable = (mapped_type**)allocator::alloc(newBucketCount); \
for (size_t i = 0; i < newBucketCount; ++i) \
{ \
m_thisKeyTable[i] = nullptr; \
} \
Base::reserve_(newBucketCount); \
} \
\
inline void nullout_(mapped_type* item) \
{ \
Specialized<Idx> spec; \
item->SetNext(spec, nullptr); \
item->SetPrev(spec, nullptr); \
Base::nullout_(item); \
} \
\
inline void reinsert_(mapped_type* newItem) \
{ \
insert_here_(newItem); \
Base::reinsert_(newItem); \
} \
\
inline void insert_(mapped_type* newItem) \
{ \
insert_here_(newItem); \
Base::insert_(newItem); \
} \
\
bool check_and_insert_(mapped_type* newItem) \
{ \
Specialized<Idx> spec; \
typename Accessor::key_type const& key = newItem->Accessor(spec).GetKey(); \
\
size_t hash = hash_(key); \
newItem->SetHash(spec, hash); \
size_t index = hash % this->m_bucketCount; \
\
mapped_type* hashMatch = m_thisKeyTable[index]; \
while (hashMatch) \
{ \
if(hashMatch->Accessor(spec).GetKey() == key) \
return hashMatch; \
hashMatch = hashMatch->Next(spec); \
} \
\
if(hashMatch) \
{ \
return false; \
} \
\
if(!Base::check_and_insert_(newItem)) \
{ \
return false; \
} \
\
insert_here_(newItem, index); \
return true; \
} \
\
void erase_(mapped_type* item) \
{ \
Specialized<Idx> spec; \
mapped_type* prev = item->Prev(spec); \
mapped_type* next = item->Next(spec); \
\
if(prev) \
{ \
prev->SetNext(spec, next); \
} \
else \
{ \
m_thisKeyTable[item->Idx(spec)] = next; \
} \
\
if (next) \
{ \
next->SetPrev(spec, prev); \
} \
Base::erase_(item); \
} \
\
\
private: \
inline void insert_here_(mapped_type* newItem) \
{ \
Specialized<Idx> spec; \
size_t idx = newItem->GetHash(spec) % this->m_bucketCount; \
\
mapped_type* next = m_thisKeyTable[idx]; \
newItem->SetNext(spec, next); \
if(next != nullptr) \
{ \
next->SetPrev(spec, newItem); \
} \
m_thisKeyTable[idx] = newItem; \
newItem->SetIndex(spec, idx); \
} \
\
inline void insert_here_(mapped_type* newItem, size_t idx) \
{ \
Specialized<Idx> spec; \
\
mapped_type* next = m_thisKeyTable[idx]; \
newItem->SetNext(spec, next); \
if(next != nullptr) \
{ \
next->SetPrev(spec, newItem); \
} \
m_thisKeyTable[idx] = newItem; \
newItem->SetIndex(spec, idx); \
} \
\
inline mapped_type const* get_(typename Accessor::key_type const& key) const \
{ \
Specialized<Idx> spec; \
size_t hash = hash_(key); \
size_t idx = hash % this->m_bucketCount; \
mapped_type* hashMatch = m_thisKeyTable[idx]; \
while(hashMatch) \
{ \
if(hashMatch->GetHash(spec) == hash && hashMatch->Accessor(spec).GetKey() == key) \
return hashMatch; \
hashMatch = hashMatch->Next(spec); \
} \
return nullptr; \
} \
\
inline mapped_type* get_(typename Accessor::key_type const& key) \
{ \
Specialized<Idx> spec; \
size_t hash = hash_(key); \
size_t idx = hash % this->m_bucketCount; \
mapped_type* hashMatch = m_thisKeyTable[idx]; \
while(hashMatch) \
{ \
if(hashMatch->GetHash(spec) == hash && hashMatch->Accessor(spec).GetKey() == key) \
return hashMatch; \
hashMatch = hashMatch->Next(spec); \
} \
return nullptr; \
} \
\
inline size_t hash_(typename Accessor::key_type const& val) \
{ \
return sprawl::Hash<typename Accessor::key_type>::Compute(val); \
} \
\
void rehash_() \
{ \
Specialized<Idx> spec; \
for (size_t i = 0; i < this->m_bucketCount; ++i) \
{ \
mapped_type* item = m_thisKeyTable[i]; \
while (item) \
{ \
mapped_type* item_next = item->Next(spec); \
item->SetNext(spec, nullptr); \
item->SetPrev(spec, nullptr); \
item = item_next; \
} \
m_thisKeyTable[i] = nullptr; \
} \
} \
\
struct EnsureKeyUnique{}; \
mapped_type** m_thisKeyTable; \
};
#define _HASHMAP_VARIADIC_VARIADICS(TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, CLASS_TEMPLATE_LIST, CLASS_PADDING_LIST, CLASS_LIST, CLASS_COMMA) \
template<typename FirstArg COMMA LIST(_CLASS_TYPE)> \
bool insert(ValueType const& val, FirstArg const& arg COMMA LIST(_TYPE_REFREF_ARG)) \
{ \
mapped_type* newItem = (mapped_type*)allocator::alloc(); \
::new((void*)newItem) mapped_type(val, arg COMMA LIST(_FORWARD_ARG)); \
\
if(!check_and_insert_(newItem)) \
{ \
newItem->~mapped_type(); \
allocator::free(newItem); \
return false; \
} \
\
if(this->m_size > (this->m_bucketCount*0.5)) \
{ \
reserve(this->m_bucketCount * 2 + 1); \
} \
\
return true; \
}
#define _HASHMAP_VARIADICS(TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \
_HASHMAP_VARIADIC_VARIADICS(_TEM_LIST0, _PAD_LIST0, _RAW_LIST0, , TEMPLATE_LIST, PADDING_LIST, LIST, COMMA) \
_HASHMAP_VARIADIC_VARIADICS(_TEM_LIST1, _PAD_LIST1, _RAW_LIST1, _COMMA, TEMPLATE_LIST, PADDING_LIST, LIST, COMMA) \
_HASHMAP_VARIADIC_VARIADICS(_TEM_LIST2, _PAD_LIST2, _RAW_LIST2, _COMMA, TEMPLATE_LIST, PADDING_LIST, LIST, COMMA) \
_HASHMAP_VARIADIC_VARIADICS(_TEM_LIST3, _PAD_LIST3, _RAW_LIST3, _COMMA, TEMPLATE_LIST, PADDING_LIST, LIST, COMMA) \
_HASHMAP_VARIADIC_VARIADICS(_TEM_LIST4, _PAD_LIST4, _RAW_LIST4, _COMMA, TEMPLATE_LIST, PADDING_LIST, LIST, COMMA) \
_HASHMAP_VARIADIC_VARIADICS(_TEM_LIST5, _PAD_LIST5, _RAW_LIST5, _COMMA, TEMPLATE_LIST, PADDING_LIST, LIST, COMMA)
_VARIADIC_EXPAND_0X(_CLASS_HASHMAP_IMPL, , , , )
}
template<typename ValueType, typename ThisAccessor = _Nil, _MAX_CLASS_LIST>
class HashMap;
#define _CLASS_HASHMAP( \
TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \
template< typename ValueType COMMA LIST(_CLASS_TYPEX)> \
class HashMap<ValueType, LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD)> \
: public detail::HashMap_Impl< \
ValueType, \
detail::AccessorGroup<ValueType, LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD)>, \
0, \
LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD) \
> \
{ \
public: \
typedef detail::HashMap_Impl< \
ValueType, \
detail::AccessorGroup<ValueType, LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD)>, \
0, \
LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD) \
> Base; \
typedef detail::AccessorGroup<ValueType, LIST(_TYPEX) COMMA PADDING_LIST(_NIL_PAD)> mapped_type; \
\
using Base::get; \
template<int i, typename T2> \
inline ValueType& get(T2 const& val) \
{ \
return Base::get(val, Specialized<i>()); \
} \
template<int i, typename T2> \
inline ValueType const& get(T2 const& val) const\
{ \
return Base::get(val, Specialized<i>()); \
} \
\
using Base::find; \
template<int i, typename T2> \
inline typename Base::iterator find(T2 const& val) \
{ \
return Base::find(val, Specialized<i>()); \
} \
\
template<int i, typename T2> \
inline typename Base::iterator find(T2 const& val) const \
{ \
return Base::find(val, Specialized<i>()); \
} \
\
using Base::cfind; \
template<int i, typename T2> \
inline typename Base::iterator cfind(T2 const& val) const \
{ \
return Base::cfind(val, Specialized<i>()); \
} \
\
using Base::erase; \
template<int i, typename T2> \
inline void erase(T2 const& val) \
{ \
return Base::erase(val, Specialized<i>()); \
} \
\
using Base::has; \
template<int i, typename T2> \
inline bool has(T2 const& val) \
{ \
return Base::has(val, Specialized<i>()); \
} \
\
HashMap(size_t startingBucketCount = 256) \
: Base(startingBucketCount) \
{ \
Base::reserve(startingBucketCount); \
} \
\
HashMap(HashMap const& other) \
: Base(other) \
{ \
Base::reserve(this->m_bucketCount); \
for (mapped_type* ptr = other.m_first; ptr; ptr = ptr->next) \
{ \
mapped_type* newPtr = (mapped_type*)Base::allocator::alloc(); \
::new((void*)newPtr) mapped_type(*ptr); \
Base::insert_(newPtr); \
} \
} \
\
HashMap(HashMap&& other) \
: Base(std::move(other)) \
{ \
other.reserve(other.m_bucketCount); \
} \
\
HashMap& operator=(HashMap const& other) \
{ \
Base::clear(); \
this->m_bucketCount = other.m_bucketCount; \
this->m_size = other.m_size; \
Base::reserve(this->m_bucketCount); \
for (mapped_type* ptr = other.m_first; ptr; ptr = ptr->next) \
{ \
mapped_type* newPtr = (mapped_type*)Base::allocator::alloc(); \
::new((void*)newPtr) mapped_type(*ptr); \
Base::insert_(newPtr); \
} \
return *this; \
} \
\
};
_VARIADIC_EXPAND_0X(_CLASS_HASHMAP, , , , )
}
}
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #4 | 14220 | ShadauxCat |
-Added binary tree implementation (red/black tree) with same multi-key interface as hashmap -Renamed AccessorGroup to MapAccessorGroup to make room for TreeAccessorGroup, which is a lot of duplicated code but had to meet some specific requirements that couldn't be easily fit into the existing AccessorGroup -Fixed HashMap::Clear() not resetting size to 0 -Fixed HashMap::Erase() neither decrementing size nor freeing memory -Changed HashMap to grow before insert instead of after (delaying needed growth until the next insert instead of growing when it detects the next insert will need it) -Fixed a style issue for private function HashMap_Impl::insertHere_() -Fully removed support for Visual Studio 2012 as I have neither the need nor the desire to continue supporting it. The doubled maintenance cost is too high. -Included array unit test that got missed before #review-14221 |
||
| #3 | 14066 | ShadauxCat |
-Improved iterating in hashmap - range-based for now gives the ability to access both key and value instead of just value -Slightly improved some of the template aliases -Mega-deprecated VC11 support. Probably doesn't compile anymore. Maintaining it is too much of a headache. #review-14067 |
||
| #2 | 12508 | ShadauxCat |
-Added threading library. Currently only functional for Linux; Windows will fail to link. (I will fix this soon.) -Fixed missing move and copy constructors in List and ForwardList -Fixed broken move constructor in HashMap -Fixed missing const get() in HashMap -Fixed broken operator-> in ListIterator -Added sprawl::noncopyable -Added sketch headers for filesystem library -Made StringLiteral hashable, added special hashes for pointers and integers in murmur3 -Fixed compiler warning in async_network -Updated memory allocators to use new threading library for mutexes -Added accessibility to sprawl::StringLiteral to be able toa ccess its pointer and length and perform pointer comparisons #review-12504 |
||
| #1 | 11496 | ShadauxCat | Initial checkin: Current states for csbuild and libSprawl |