#pragma once #include #include #include #include #include "../../core/Defines.h" // Searchtree based class that finds all fitting nodes for a certain value. // A function needs to be given that hashes a value into a set of hashes (all values should give a set with the same length!) // For each hash in this set of hashes, a value of 0 means that anything can be put there. The other hashes have to match. template class NodeReplacementFinder { private: struct SearchTreeNode { private: std::unordered_map children; union WildCardOrValue { SearchTreeNode* wildcard; V value; }; WildCardOrValue wildCardOrValue; bool isLeaf; V GetValue() const { assert(isLeaf); return wildCardOrValue.value; } void SetValue(V value) { assert(isLeaf); wildCardOrValue.value = value; } SearchTreeNode* GetWildCard() const { assert(!isLeaf); return wildCardOrValue.wildcard; } void SetWildCard(SearchTreeNode* wildcard) { assert(!isLeaf); wildCardOrValue.wildcard = wildcard; } SearchTreeNode* GetChild(unsigned32 key) const { // If the key is a wildcard, return the wildcardnode if (key == 0) return GetWildCard(); // Otherwise, find the correct child auto it = children.find(key); if (it == children.end()) return NULL; else return (*it).second; } SearchTreeNode* AddChild(unsigned32 key, bool isLeaf) { SearchTreeNode* child = GetChild(key); if (child != NULL) return child; SearchTreeNode* newChild = new SearchTreeNode(isLeaf); if (key == 0) SetWildCard(newChild); else children.insert(std::make_pair(key, newChild)); return newChild; } // Recursively call add, updating the index to make sure the correct key is used void Add(const std::vector& keys, const V& value, size_t index) { if (isLeaf) SetValue(value); else { unsigned32 curKey = keys[index]; SearchTreeNode* child = AddChild(curKey, index == keys.size() - 1); child->Add(keys, value, index + 1); } } void Find(const std::vector& keys, size_t index, std::vector& out) const { if (isLeaf) out.push_back(GetValue()); else { unsigned32 curKey = keys[index]; if (curKey == 0) { // if the current key is a wildcard, explore all possible paths: for (auto child : children) child.second->Find(keys, index + 1, out); } else { // If the current key isn't a wildcard, only explore the current key and the wildcard key auto curKeyChild = GetChild(curKey); if (curKeyChild != NULL) curKeyChild->Find(keys, index + 1, out); } SearchTreeNode* wildcard = GetWildCard(); if (wildcard != NULL) wildcard->Find(keys, index + 1, out); } } // Returns true if the node can be removed safely bool Remove(const std::vector& keys, size_t index) { if (isLeaf) return true; else { unsigned32 curKey = keys[index]; auto child = GetChild(curKey); if (child != NULL) { bool canDelete = child->Remove(keys, index + 1); if (canDelete) { if (curKey == 0) SetWildCard(NULL); else children.erase(children.find(curKey)); delete child; } } } return children.empty(); } public: SearchTreeNode(bool isLeaf) : isLeaf(isLeaf) { if (!isLeaf) { children = std::unordered_map(); SetWildCard(NULL); } else { SetValue(V()); } } ~SearchTreeNode() { if (!isLeaf) { for (auto child : children) delete child.second; } } void Add(const std::vector& keys, V value) { Add(keys, value, 0); } void Remove(const std::vector& keys) { Remove(keys, 0); } std::vector Find(std::vector keys) const { std::vector res; Find(keys, 0, res); return res; } }; std::function(const V)> GetKeys; SearchTreeNode* root; public: NodeReplacementFinder(std::function(const V)> keyGetter) { GetKeys = keyGetter; root = new SearchTreeNode(false); } ~NodeReplacementFinder() { delete root; } void Add(const V value) { std::vector keys = GetKeys(value); root->Add(keys, value); } void Remove(const V value) { std::vector keys = GetKeys(value); root->Remove(keys); } std::vector Find(const V value) const { std::vector keys = GetKeys(value); return root->Find(keys); } };