#ifndef CORE_PROBING_HPPASHMAP_HPP #define CORE_PROBING_HPPASHMAP_HPP #include "data/List.hpp" #include "utils/AlignedData.hpp" #include "utils/ArrayString.hpp" #include "utils/HashCode.hpp" #include "utils/Logger.hpp" namespace Core { template struct ProbingHashMap final { template class Node final { friend ProbingHashMap; friend List; K key; public: Value& value; const K& getKey() const { return key; } template check_return Error toString(String& s) const { CORE_RETURN_ERROR(s.append(key)); CORE_RETURN_ERROR(s.append(" = ")); return s.append(value); } private: Node(const K& key_, Value& value_) : key(key_), value(value_) { } }; private: template class Iterator final { const K* currentKey; const K* endKey; Value* currentValue; public: Iterator(const K* key, const K* endKey_, Value* value) : currentKey(key), endKey(endKey_), currentValue(value) { skip(); } Iterator& operator++() { ++currentKey; ++currentValue; skip(); return *this; } bool operator!=(const Iterator& other) const { return currentKey != other.currentKey; } R operator*() const { return A(*currentKey, *currentValue); } private: void skip() { while(currentKey != endKey && *currentKey == emptyValue()) { ++currentKey; ++currentValue; } } }; template static Node access(const K& key, Value& value) { return Node(key, value); } template static Value& accessValue(const K&, Value& value) { return value; } static const K& accessKey(const K& key, const V&) { return key; } template using BaseEntryIterator = Iterator, access>; using EntryIterator = BaseEntryIterator; using ConstEntryIterator = BaseEntryIterator; template using BaseValueIterator = Iterator>; using ValueIterator = BaseValueIterator; using ConstValueIterator = BaseValueIterator; using ConstKeyIterator = Iterator; template struct IteratorAdapter final { M& map; I begin() const { return {map.keys.begin(), map.keys.end(), map.values}; } I end() const { return {map.keys.end(), map.keys.end(), nullptr}; } }; using ValueIteratorAdapter = IteratorAdapter; using ConstValueIteratorAdapter = IteratorAdapter; using ConstKeyIteratorAdapter = IteratorAdapter; private: List keys; V* values; int entries; public: ProbingHashMap() : values(nullptr), entries(0) { } ProbingHashMap(const ProbingHashMap& other) = delete; ProbingHashMap(ProbingHashMap&& other) : ProbingHashMap() { swap(other); } ~ProbingHashMap() { for(int i = 0; i < keys.getLength(); i++) { if(keys[i] != emptyValue()) { values[i].~V(); } } delete[] reinterpret_cast*>(values); } ProbingHashMap& operator=(const ProbingHashMap& other) = delete; ProbingHashMap& operator=(ProbingHashMap&& other) { swap(other); return *this; } check_return Error copyFrom(const ProbingHashMap& other) { ProbingHashMap copy; for(const auto& e : other) { CORE_RETURN_ERROR(copy.add(e.getKey(), e.value)); } swap(copy); return Error::NONE; } check_return Error rehash(int minCapacity) { if(minCapacity <= keys.getLength()) { return Error::NONE; } ProbingHashMap map; int l = Core::Math::max(1 << Math::roundUpLog2(minCapacity), 8); CORE_RETURN_ERROR(map.keys.resize(l, emptyValue())); map.values = reinterpret_cast(new AlignedType[l]); if(map.values == nullptr) { return Error::OUT_OF_MEMORY; } for(int i = 0; i < keys.getLength(); i++) { if(keys[i] != emptyValue()) { CORE_RETURN_ERROR(map.add(keys[i], values[i])); } } swap(map); return Error::NONE; } template check_return Error tryEmplace(V*& v, const K& key, Args&&... args) { if(key == emptyValue()) { return Error::INVALID_ARGUMENT; } CORE_RETURN_ERROR(rehash(entries * 2 + 1)); int index = searchSlot(key); if(index < 0) { return Error::CAPACITY_REACHED; } else if(keys[index] == key) { return Error::EXISTING_KEY; } keys[index] = key; v = new(values + index) V(Core::forward(args)...); entries++; return Error::NONE; } template check_return Error put(V*& v, const K& key, VA&& value) { if(key == emptyValue()) { return Error::INVALID_ARGUMENT; } CORE_RETURN_ERROR(rehash(entries * 2 + 1)); int index = searchSlot(key); if(index < 0) { return Error::CAPACITY_REACHED; } if(keys[index] == key) { values[index] = Core::forward(value); } else { new(values + index) V(Core::forward(value)); entries++; } keys[index] = key; v = reinterpret_cast(values + index); return Error::NONE; } template check_return Error add(const K& key, VA&& value) { V* v = nullptr; return put(v, key, Core::forward(value)); } const V* search(const K& key) const { return searchValue(key); } V* search(const K& key) { return searchValue(key); } bool contains(const K& key) const { return search(key) != nullptr; } ProbingHashMap& clear() { ProbingHashMap map; swap(map); return *this; } ConstKeyIteratorAdapter getKeys() const { return {*this}; } ValueIteratorAdapter getValues() { return {*this}; } ConstValueIteratorAdapter getValues() const { return {*this}; } EntryIterator begin() { return {keys.begin(), keys.end(), values}; } EntryIterator end() { return {keys.end(), keys.end(), nullptr}; } ConstEntryIterator begin() const { return {keys.begin(), keys.end(), values}; } ConstEntryIterator end() const { return {keys.end(), keys.end(), nullptr}; } template check_return Error toString(String& s) const { return Core::toString(s, *this); } void swap(ProbingHashMap& o) { Core::swap(o.keys, keys); Core::swap(o.values, values); Core::swap(o.entries, entries); } private: int searchSlot(const K& key) const { int baseHash = static_cast(hashCode(key) * 514685581u); int end = keys.getLength() - 1; for(int i = 0; i <= end; i++) { int hash = (baseHash + i) & end; if(keys[hash] == emptyValue() || keys[hash] == key) { return hash; } } return -1; } template Value* searchValue(const K& key) const { int baseHash = static_cast(hashCode(key) * 514685581u); int end = keys.getLength() - 1; for(int i = 0; i <= end; i++) [[unlikely]] { int hash = (baseHash + i) & end; if(keys[hash] == key) [[likely]] { return values + hash; } else if(keys[hash] == emptyValue()) { return nullptr; } } return nullptr; } }; } #endif