// Copyright (c) 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); } };