From 1849a0383555aa1ddab91f4dcc2af14467611b05 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 8 Feb 2023 15:28:09 +0100 Subject: [PATCH] Introduce HybridMap A tree- and hash-map. Ordered iteration combined with O(1) lookup. --- lib/base/CMakeLists.txt | 1 + lib/base/hybrid-map.hpp | 152 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 lib/base/hybrid-map.hpp diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 18e884de2..98e6a9981 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -37,6 +37,7 @@ set(base_SOURCES fifo.cpp fifo.hpp filelogger.cpp filelogger.hpp filelogger-ti.hpp function.cpp function.hpp function-ti.hpp function-script.cpp functionwrapper.hpp + hybrid-map.hpp initialize.cpp initialize.hpp io-engine.cpp io-engine.hpp journaldlogger.cpp journaldlogger.hpp journaldlogger-ti.hpp diff --git a/lib/base/hybrid-map.hpp b/lib/base/hybrid-map.hpp new file mode 100644 index 000000000..5f85e369f --- /dev/null +++ b/lib/base/hybrid-map.hpp @@ -0,0 +1,152 @@ +/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ + +#pragma once + +#include +#include +#include +#include + +namespace icinga +{ + +/** + * Proxies == and std::hash to *this->Target. Useful not to duplicate hash table keys. + * + * @ingroup base + */ +template +struct HashProxy +{ + T const * Target; + + inline HashProxy(T const * target) : Target(target) + { + } + + inline bool operator==(const HashProxy& rhs) const + { + return *Target == *rhs.Target; + } +}; + +/** + * A tree- and hash-map. Ordered iteration combined with O(1) lookup. + * + * @ingroup base + */ +template +class HybridMap +{ +public: + typedef typename std::map::value_type ValueType; + typedef typename std::map::iterator Iterator; + + auto Contains(const K& k) const + { + return m_Fast.find(&k) != m_Fast.end(); + } + + auto Get(const K& k, V& v) const + { + auto pos (m_Fast.find(&k)); + + if (pos == m_Fast.end()) { + return false; + } + + v = pos->second->second; + return true; + } + + inline auto GetLength() const + { + return m_Fast.size(); + } + + inline auto begin() + { + return m_Ordered.begin(); + } + + inline auto end() + { + return m_Ordered.end(); + } + + inline auto begin() const + { + return m_Ordered.begin(); + } + + inline auto end() const + { + return m_Ordered.end(); + } + + void Set(const K& k, V v) + { + auto pos (m_Fast.find(&k)); + + if (pos == m_Fast.end()) { + auto pos (m_Ordered.emplace(k, std::move(v)).first); + + m_Fast.emplace(&pos->first, pos); + } else { + pos->second->second = std::move(v); + } + } + + void Set(K&& k, V v) + { + auto pos (m_Fast.find(&k)); + + if (pos == m_Fast.end()) { + auto pos (m_Ordered.emplace(std::move(k), std::move(v)).first); + + m_Fast.emplace(&pos->first, pos); + } else { + pos->second->second = std::move(v); + } + } + + void Remove(const K& k) + { + auto pos (m_Fast.find(&k)); + + if (pos != m_Fast.end()) { + m_Ordered.erase(pos->second); + m_Fast.erase(pos); + } + } + + void Remove(Iterator i) + { + m_Fast.erase(&i->first); + m_Ordered.erase(i); + } + + void Clear() + { + m_Ordered.clear(); + m_Fast.clear(); + } + +private: + std::map m_Ordered; + std::unordered_map, Iterator> m_Fast; +}; + +} + +namespace std +{ + template + struct hash> + { + inline std::size_t operator()(const icinga::HashProxy& hp) const noexcept + { + return std::hash{}(*hp.Target); + } + }; +}