// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "HashSetBase.h"
///
/// Describes single portion of space for the key and value pair in a hash map.
///
template
struct DictionaryBucket
{
friend Memory;
friend HashSetBase;
friend Dictionary;
/// The key.
KeyType Key;
/// The value.
ValueType Value;
private:
HashSetBucketState _state;
DictionaryBucket()
: _state(HashSetBucketState::Empty)
{
}
DictionaryBucket(DictionaryBucket&& other) noexcept
{
_state = other._state;
if (other._state == HashSetBucketState::Occupied)
{
Memory::MoveItems(&Key, &other.Key, 1);
Memory::MoveItems(&Value, &other.Value, 1);
other._state = HashSetBucketState::Empty;
}
}
DictionaryBucket& operator=(DictionaryBucket&& other) noexcept
{
if (this != &other)
{
if (_state == HashSetBucketState::Occupied)
{
Memory::DestructItem(&Key);
Memory::DestructItem(&Value);
}
_state = other._state;
if (other._state == HashSetBucketState::Occupied)
{
Memory::MoveItems(&Key, &other.Key, 1);
Memory::MoveItems(&Value, &other.Value, 1);
other._state = HashSetBucketState::Empty;
}
}
return *this;
}
/// Copying a bucket is useless, because a key must be unique in the dictionary.
DictionaryBucket(const DictionaryBucket&) = delete;
/// Copying a bucket is useless, because a key must be unique in the dictionary.
DictionaryBucket& operator=(const DictionaryBucket&) = delete;
~DictionaryBucket()
{
if (_state == HashSetBucketState::Occupied)
{
Memory::DestructItem(&Key);
Memory::DestructItem(&Value);
}
}
FORCE_INLINE void Free()
{
if (_state == HashSetBucketState::Occupied)
{
Memory::DestructItem(&Key);
Memory::DestructItem(&Value);
}
_state = HashSetBucketState::Empty;
}
FORCE_INLINE void Delete()
{
ASSERT(IsOccupied());
_state = HashSetBucketState::Deleted;
Memory::DestructItem(&Key);
Memory::DestructItem(&Value);
}
template
FORCE_INLINE void Occupy(const KeyComparableType& key)
{
Memory::ConstructItems(&Key, &key, 1);
Memory::ConstructItem(&Value);
_state = HashSetBucketState::Occupied;
}
template
FORCE_INLINE void Occupy(const KeyComparableType& key, const ValueType& value)
{
Memory::ConstructItems(&Key, &key, 1);
Memory::ConstructItems(&Value, &value, 1);
_state = HashSetBucketState::Occupied;
}
template
FORCE_INLINE void Occupy(const KeyComparableType& key, ValueType&& value)
{
Memory::ConstructItems(&Key, &key, 1);
Memory::MoveItems(&Value, &value, 1);
_state = HashSetBucketState::Occupied;
}
FORCE_INLINE bool IsEmpty() const
{
return _state == HashSetBucketState::Empty;
}
FORCE_INLINE bool IsDeleted() const
{
return _state == HashSetBucketState::Deleted;
}
FORCE_INLINE bool IsOccupied() const
{
return _state == HashSetBucketState::Occupied;
}
FORCE_INLINE bool IsNotOccupied() const
{
return _state != HashSetBucketState::Occupied;
}
FORCE_INLINE const KeyType& GetKey() const
{
return Key;
}
};
///
/// Template for unordered dictionary with mapped key with value pairs.
///
/// The type of the keys in the dictionary.
/// The type of the values in the dictionary.
/// The type of memory allocator.
template
API_CLASS(InBuild) class Dictionary : public HashSetBase>
{
friend Dictionary;
public:
typedef DictionaryBucket Bucket;
typedef HashSetBase Base;
public:
///
/// Initializes an empty without reserving any space.
///
Dictionary()
{
}
///
/// Initializes by reserving space.
///
/// The number of elements that can be added without a need to allocate more memory.
FORCE_INLINE explicit Dictionary(const int32 capacity)
{
Base::SetCapacity(capacity);
}
///
/// Initializes a new instance of the class.
///
/// The other collection to move.
Dictionary(Dictionary&& other) noexcept
{
Base::MoveToEmpty(MoveTemp(other));
}
///
/// Initializes by copying the elements from the other collection.
///
/// Other collection to copy
Dictionary(const Dictionary& other)
{
Clone(other);
}
///
/// Clones the data from the other collection.
///
/// The other collection to copy.
/// The reference to this.
Dictionary& operator=(const Dictionary& other)
{
if (this != &other)
Clone(other);
return *this;
}
///
/// Moves the data from the other collection.
///
/// The other collection to move.
/// The reference to this.
Dictionary& operator=(Dictionary&& other) noexcept
{
if (this != &other)
{
Base::Clear();
Base::_allocation.Free();
Base::MoveToEmpty(MoveTemp(other));
}
return *this;
}
///
/// Finalizes an instance of the class.
///
~Dictionary()
{
}
public:
///
/// The read-only dictionary collection iterator.
///
struct ConstIterator : Base::IteratorBase
{
friend Dictionary;
public:
ConstIterator(const Dictionary* collection, const int32 index)
: Base::IteratorBase(collection, index)
{
}
ConstIterator()
: Base::IteratorBase(nullptr, -1)
{
}
ConstIterator(const ConstIterator& i)
: Base::IteratorBase(i._collection, i._index)
{
}
ConstIterator(ConstIterator&& i) noexcept
: Base::IteratorBase(i._collection, i._index)
{
}
public:
FORCE_INLINE bool operator!() const
{
return !(bool)*this;
}
FORCE_INLINE bool operator==(const ConstIterator& v) const
{
return this->_index == v._index && this->_collection == v._collection;
}
FORCE_INLINE bool operator!=(const ConstIterator& v) const
{
return this->_index != v._index || this->_collection != v._collection;
}
ConstIterator& operator=(const ConstIterator& v)
{
this->_collection = v._collection;
this->_index = v._index;
return *this;
}
ConstIterator& operator=(ConstIterator&& v) noexcept
{
this->_collection = v._collection;
this->_index = v._index;
return *this;
}
ConstIterator& operator++()
{
this->Next();
return *this;
}
ConstIterator operator++(int) const
{
ConstIterator i = *this;
i.Next();
return i;
}
ConstIterator& operator--()
{
this->Prev();
return *this;
}
ConstIterator operator--(int) const
{
ConstIterator i = *this;
i.Prev();
return i;
}
};
///
/// The dictionary collection iterator.
///
struct Iterator : Base::IteratorBase
{
friend Dictionary;
public:
Iterator(Dictionary* collection, const int32 index)
: Base::IteratorBase(collection, index)
{
}
Iterator()
: Base::IteratorBase(nullptr, -1)
{
}
Iterator(const Iterator& i)
: Base::IteratorBase(i._collection, i._index)
{
}
Iterator(Iterator&& i) noexcept
: Base::IteratorBase(i._collection, i._index)
{
}
public:
FORCE_INLINE Bucket& operator*() const
{
return ((Dictionary*)this->_collection)->_allocation.Get()[this->_index];
}
FORCE_INLINE Bucket* operator->() const
{
return &((Dictionary*)this->_collection)->_allocation.Get()[this->_index];
}
FORCE_INLINE bool operator!() const
{
return !(bool)*this;
}
FORCE_INLINE bool operator==(const Iterator& v) const
{
return this->_index == v._index && this->_collection == v._collection;
}
FORCE_INLINE bool operator!=(const Iterator& v) const
{
return this->_index != v._index || this->_collection != v._collection;
}
Iterator& operator=(const Iterator& v)
{
this->_collection = v._collection;
this->_index = v._index;
return *this;
}
Iterator& operator=(Iterator&& v) noexcept
{
this->_collection = v._collection;
this->_index = v._index;
return *this;
}
Iterator& operator++()
{
this->Next();
return *this;
}
Iterator operator++(int) const
{
Iterator i = *this;
i.Next();
return i;
}
Iterator& operator--()
{
this->Prev();
return *this;
}
Iterator operator--(int) const
{
Iterator i = *this;
i.Prev();
return i;
}
};
public:
///
/// Gets element by the key (will add default ValueType element if key not found).
///
/// The key of the element.
/// The value that is at given index.
template
ValueType& At(const KeyComparableType& key)
{
Bucket* bucket = Base::OnAdd(key, false);
if (bucket->_state != HashSetBucketState::Occupied)
bucket->Occupy(key);
return bucket->Value;
}
///
/// Gets the element by the key.
///
/// The ky of the element.
/// The value that is at given index.
template
const ValueType& At(const KeyComparableType& key) const
{
typename Base::FindPositionResult pos;
Base::FindPosition(key, pos);
ASSERT(pos.ObjectIndex != -1);
return Base::_allocation.Get()[pos.ObjectIndex].Value;
}
///
/// Gets or sets the element by the key.
///
/// The key of the element.
/// The value that is at given index.
template
FORCE_INLINE ValueType& operator[](const KeyComparableType& key)
{
return At(key);
}
///
/// Gets or sets the element by the key.
///
/// The ky of the element.
/// The value that is at given index.
template
FORCE_INLINE const ValueType& operator[](const KeyComparableType& key) const
{
return At(key);
}
///
/// Tries to get element with given key.
///
/// The key of the element.
/// The result value.
/// True if element of given key has been found, otherwise false.
template
bool TryGet(const KeyComparableType& key, ValueType& result) const
{
typename Base::FindPositionResult pos;
Base::FindPosition(key, pos);
if (pos.ObjectIndex == -1)
return false;
result = Base::_allocation.Get()[pos.ObjectIndex].Value;
return true;
}
///
/// Tries to get pointer to the element with given key.
///
/// The ky of the element.
/// Pointer to the element value or null if cannot find it.
template
ValueType* TryGet(const KeyComparableType& key) const
{
typename Base::FindPositionResult pos;
Base::FindPosition(key, pos);
if (pos.ObjectIndex == -1)
return nullptr;
return const_cast(&Base::_allocation.Get()[pos.ObjectIndex].Value);
}
public:
///
/// Clears the collection and delete value objects.
/// Note: collection must contain pointers to the objects that have public destructor and be allocated using New method.
///
#if defined(_MSC_VER)
template::Value>::Type>
#endif
void ClearDelete()
{
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
{
if (i->Value)
::Delete(i->Value);
}
Base::Clear();
}
public:
///
/// Add pair element to the collection.
///
/// The key.
/// The value.
/// Weak reference to the stored bucket.
template
FORCE_INLINE Bucket* Add(const KeyComparableType& key, const ValueType& value)
{
Bucket* bucket = Base::OnAdd(key);
bucket->Occupy(key, value);
return bucket;
}
///
/// Add pair element to the collection.
///
/// The key.
/// The value.
/// Weak reference to the stored bucket.
template
FORCE_INLINE Bucket* Add(const KeyComparableType& key, ValueType&& value)
{
Bucket* bucket = Base::OnAdd(key);
bucket->Occupy(key, MoveTemp(value));
return bucket;
}
///
/// Add pair element to the collection.
///
/// Iterator with key and value.
DEPRECATED("Use Add with separate Key and Value from iterator.") void Add(const Iterator& i)
{
ASSERT(i._collection != this && i);
const Bucket& bucket = *i;
Add(bucket.Key, bucket.Value);
}
///
/// Removes element with a specified key.
///
/// The element key to remove.
/// True if cannot remove item from the collection because cannot find it, otherwise false.
template
bool Remove(const KeyComparableType& key)
{
typename Base::FindPositionResult pos;
Base::FindPosition(key, pos);
if (pos.ObjectIndex != -1)
{
Base::_allocation.Get()[pos.ObjectIndex].Delete();
--Base::_elementsCount;
++Base::_deletedCount;
return true;
}
return false;
}
///
/// Removes element at specified iterator.
///
/// The element iterator to remove.
/// True if cannot remove item from the collection because cannot find it, otherwise false.
bool Remove(const Iterator& i)
{
ASSERT(i._collection == this);
if (i)
{
Base::_allocation.Get()[i._index].Delete();
--Base::_elementsCount;
++Base::_deletedCount;
return true;
}
return false;
}
///
/// Removes elements with a specified value
///
/// Element value to remove
/// The amount of removed items. Zero if nothing changed.
int32 RemoveValue(const ValueType& value)
{
int32 result = 0;
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
{
if (i->Value == value)
{
Remove(i);
++result;
}
}
return result;
}
public:
///
/// Finds the element with given key in the collection.
///
/// The key to find.
/// The iterator for the found element or End if cannot find it.
template
Iterator Find(const KeyComparableType& key)
{
if (Base::IsEmpty())
return End();
typename Base::FindPositionResult pos;
Base::FindPosition(key, pos);
return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End();
}
///
/// Finds the element with given key in the collection.
///
/// The key to find.
/// The iterator for the found element or End if cannot find it.
template
ConstIterator Find(const KeyComparableType& key) const
{
typename Base::FindPositionResult pos;
Base::FindPosition(key, pos);
return pos.ObjectIndex != -1 ? ConstIterator(this, pos.ObjectIndex) : End();
}
///
/// Checks if given key is in a collection.
///
/// The key to find.
/// True if key has been found in a collection, otherwise false.
template
bool ContainsKey(const KeyComparableType& key) const
{
typename Base::FindPositionResult pos;
Base::FindPosition(key, pos);
return pos.ObjectIndex != -1;
}
///
/// Checks if given value is in a collection.
///
/// The value to find.
/// True if value has been found in a collection, otherwise false.
bool ContainsValue(const ValueType& value) const
{
if (Base::HasItems())
{
const Bucket* data = Base::_allocation.Get();
for (int32 i = 0; i < Base::_size; ++i)
{
if (data[i].IsOccupied() && data[i].Value == value)
return true;
}
}
return false;
}
///
/// Searches for the specified object and returns the zero-based index of the first occurrence within the entire dictionary.
///
/// The value of the key to find.
/// The output key.
/// True if value has been found, otherwise false.
bool KeyOf(const ValueType& value, KeyType* key) const
{
if (Base::HasItems())
{
const Bucket* data = Base::_allocation.Get();
for (int32 i = 0; i < Base::_size; ++i)
{
if (data[i].IsOccupied() && data[i].Value == value)
{
if (key)
*key = data[i].Key;
return true;
}
}
}
return false;
}
public:
///
/// Clones other collection into this.
///
/// The other collection to clone.
void Clone(const Dictionary& other)
{
// TODO: if both key and value are POD types then use raw memory copy for buckets
Base::Clear();
Base::SetCapacity(other.Capacity(), false);
for (ConstIterator i = other.Begin(); i != other.End(); ++i)
{
const Bucket& bucket = *i;
Add(bucket.Key, bucket.Value);
}
ASSERT(Base::Count() == other.Count());
ASSERT(Base::Capacity() == other.Capacity());
}
///
/// Gets the keys collection to the output array (will contain unique items).
///
/// The result.
template
void GetKeys(Array& result) const
{
for (ConstIterator i = Begin(); i.IsNotEnd(); ++i)
result.Add(i->Key);
}
///
/// Gets the values collection to the output array (may contain duplicates).
///
/// The result.
template
void GetValues(Array& result) const
{
for (ConstIterator i = Begin(); i.IsNotEnd(); ++i)
result.Add(i->Value);
}
public:
Iterator Begin()
{
Iterator i(this, -1);
++i;
return i;
}
Iterator End()
{
return Iterator(this, Base::_size);
}
ConstIterator Begin() const
{
ConstIterator i(this, -1);
++i;
return i;
}
ConstIterator End() const
{
return ConstIterator(this, Base::_size);
}
Iterator begin()
{
Iterator i(this, -1);
++i;
return i;
}
FORCE_INLINE Iterator end()
{
return Iterator(this, Base::_size);
}
ConstIterator begin() const
{
ConstIterator i(this, -1);
++i;
return i;
}
FORCE_INLINE ConstIterator end() const
{
return ConstIterator(this, Base::_size);
}
};