#ifndef CORE_HASHMAP_H #define CORE_HASHMAP_H #include "data/LinkedList.h" #include "data/List.h" #include "utils/ArrayString.h" namespace Core { template struct HashMap final { class Node final { friend HashMap; friend List; friend LinkedList; K key; public: V value; const K& getKey() const { return key; } private: template Node(const K& key_, Args&&... args) : key(key_), value(Core::forward(args)...) { } }; using NodePointer = LinkedList::Node*; using NodePointerList = List; using NodeIterator = LinkedList::Iterator; using ConstNodeIterator = LinkedList::ConstIterator; template class Iterator final { N iterator; public: Iterator(N iterator_) : iterator(iterator_) { } Iterator& operator++() { ++iterator; return *this; } bool operator!=(const Iterator& other) const { return iterator != other.iterator; } R& operator*() const { return A(*iterator); } }; template static R& access(R& node) { return node; } template static R& accessValue(I& node) { return node.value; } static const K& accessKey(const Node& node) { return node.getKey(); } template using BaseEntryIterator = Iterator>; 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 I(map.nodes.begin()); } I end() const { return I(map.nodes.end()); } }; using EntryIteratorAdapter = IteratorAdapter; using ConstEntryIteratorAdapter = IteratorAdapter; using ValueIteratorAdapter = IteratorAdapter; using ConstValueIteratorAdapter = IteratorAdapter; using ConstKeyIteratorAdapter = IteratorAdapter; private: LinkedList nodes; List nodePointers; public: // returns true on error check_return bool copyFrom(const HashMap& other) { HashMap copy; for(const auto& e : other) { if(copy.add(e.getKey(), e.value)) { return true; } } swap(copy.nodes, nodes); swap(copy.nodePointers, nodePointers); return false; } // returns true on error check_return bool rehash(int minCapacity) { if(minCapacity <= nodePointers.getLength()) { return false; } HashMap map; int l = 1 << Math::roundUpLog2(Core::Math::max(minCapacity, 8)); if(map.nodePointers.resize(l)) { return true; } for(NodePointerList& list : nodePointers) { for(NodePointer& n : list) { int h = map.hashIndex(n->data.key); if(map.nodePointers[h].add(n)) { return true; } } } Core::swap(map.nodePointers, nodePointers); return false; } // returns true on error template check_return bool tryEmplace(const K& key, Args&&... args) { if(rehash(nodes.getLength() + 1)) { return true; } int h = hashIndex(key); V* v = searchList(key, h); if(v != nullptr) { return true; } NodePointer np = nodes.add(key, Core::forward(args)...); if(np == nullptr) { return true; } else if(nodePointers[h].add(np)) { nodes.remove(np); return true; } return false; } // returns true on error template check_return bool add(const K& key, VA&& value) { if(rehash(nodes.getLength() + 1)) { return true; } int h = hashIndex(key); V* v = searchList(key, h); if(v == nullptr) { NodePointer np = nodes.add(key, Core::forward(value)); if(np == nullptr) { return true; } else if(nodePointers[h].add(np)) { nodes.remove(np); return true; } } else { *v = Core::forward(value); } return false; } // returns true when a value was removed check_return bool remove(const K& key) { NodePointerList& list = nodePointers[hashIndex(key)]; for(int i = 0; i < list.getLength(); i++) { if(list[i]->data.key == key) { NodePointer np = list[i]; bool r = list.removeBySwap(i); nodes.remove(np); return !r; } } return false; } const V* search(const K& key) const { return searchList(key, hashIndex(key)); } V* search(const K& key) { return searchList(key, hashIndex(key)); } bool contains(const K& key) const { return search(key) != nullptr; } HashMap& clear() { nodes.clear(); for(NodePointerList& n : nodePointers) { n.clear(); } return *this; } EntryIteratorAdapter entries() { return {*this}; } ConstEntryIteratorAdapter entries() const { return {*this}; } ConstKeyIteratorAdapter keys() const { return {*this}; } ValueIteratorAdapter values() { return {*this}; } ConstValueIteratorAdapter values() const { return {*this}; } EntryIterator begin() { return EntryIterator(nodes.begin()); } EntryIterator end() { return EntryIterator(nodes.end()); } ConstEntryIterator begin() const { return ConstEntryIterator(nodes.begin()); } ConstEntryIterator end() const { return ConstEntryIterator(nodes.end()); } // returns true on error template check_return bool toString(ArrayString& s) const { if(s.append("[")) { return true; } bool c = false; for(const NodePointerList& list : nodePointers) { for(const NodePointer& n : list) { if(c && s.append(", ")) { return true; } else if(s.append(n->data.key) || s.append(" = ") || s.append(n->data.value)) { return true; } c = true; } } return s.append("]"); } private: template int hashIndex(const H& key) const { return static_cast(fullHash(key)) & (nodePointers.getLength() - 1); } template Hash fullHash(const H& key) const { return key.hashCode(); } Hash fullHash(int key) const { static_assert(sizeof(key) == sizeof(Hash), "unwanted loose of precision in hash"); return static_cast(key); } Hash fullHash(unsigned int key) const { static_assert(sizeof(key) == sizeof(Hash), "unwanted loose of precision in hash"); return key; } const V* searchList(const K& key, int h) const { if(nodePointers.getLength() == 0) { return nullptr; } for(const NodePointer& n : nodePointers[h]) { if(n->data.key == key) { return &n->data.value; } } return nullptr; } V* searchList(const K& key, int h) { return const_cast( static_cast(this)->searchList(key, h)); } }; } #endif