ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
flat_map.hpp
Go to the documentation of this file.
1/**
2 * @file flat_map.hpp
3 * @brief Flat (unsorted, contiguous-storage) associative map template.
4 *
5 * Heavily inspired from https://stackoverflow.com/a/30938947, credits to
6 * Yakk - Adam Nevraum @ StackOverflow
7 *
8 * @c flat_map<Key, Value, Storage> is a lightweight associative container
9 * that stores key-value pairs in a contiguous sequence (by default a
10 * @c std::vector, but any compatible random-access container works).
11 *
12 * **Trade-offs vs. @c std::map / @c std::unordered_map**:
13 * - Lookup and insertion are O(n) (linear scan), but for *small* maps (up
14 * to ~20 elements) cache-friendly linear search outperforms pointer-
15 * chasing tree or hash-table traversals.
16 * - The storage type is a template parameter so that
17 * @c boost::container::static_vector can be used, enabling stack
18 * allocation and avoiding heap allocation entirely for small maps with
19 * bounded keys (e.g. keys bounded by the treewidth + 1 of a circuit).
20 * - No iterator invalidation on insertions for @c static_vector.
21 *
22 * ProvSQL uses this as the @c valuation_t type in
23 * @c dDNNFTreeDecompositionBuilder: a mapping from @c gate_t to @c bool
24 * for the current bag's truth-value assignment.
25 *
26 * A @c std::hash specialisation is provided so that @c flat_map can be
27 * used as a key in @c std::unordered_map.
28 */
29#ifndef FLAT_MAP_H
30#define FLAT_MAP_H
31
32// Heavily inspired from https://stackoverflow.com/a/30938947, credits to
33// Yakk - Adam Nevraum @ StackOverflow
34
35#include <vector>
36#include <utility>
37#include <algorithm>
38
39/**
40 * @brief Flat associative map with pluggable storage.
41 *
42 * @tparam Key Key type. Must be equality-comparable.
43 * @tparam Value Mapped value type.
44 * @tparam Storage Container template used for storage (default:
45 * @c std::vector). Must support @c emplace_back,
46 * @c erase, @c begin, @c end, @c size, and @c empty.
47 */
48template<class Key, class Value, template<class...>class Storage=std::vector>
49struct flat_map {
50 using storage_t = Storage<std::pair<Key, Value>>; ///< Underlying container type
51 storage_t storage; ///< Contiguous key-value storage
52
53 using iterator=decltype(begin(std::declval<storage_t&>())); ///< Mutable iterator
54 using const_iterator=decltype(begin(std::declval<const storage_t&>())); ///< Const iterator
55
56 // Constructor
57 flat_map() = default;
58 /** @brief Construct from an initializer list of key-value pairs. @param init Initializer list. */
59 flat_map(std::initializer_list<std::pair<Key,Value>> init) : storage(std::move(init)) { }
60
61 // boilerplate:
62 /** @brief Return iterator to the first element. @return Mutable begin iterator. */
64 using std::begin;
65 return begin(storage);
66 }
67 /** @brief Return const iterator to the first element. @return Const begin iterator. */
69 using std::begin;
70 return begin(storage);
71 }
72 /** @brief Return const iterator to the first element. @return Const begin iterator. */
74 using std::begin;
75 return begin(storage);
76 }
77 /** @brief Return iterator past the last element. @return Mutable end iterator. */
79 using std::end;
80 return end(storage);
81 }
82 /** @brief Return const iterator past the last element. @return Const end iterator. */
84 using std::end;
85 return end(storage);
86 }
87 /** @brief Return const iterator past the last element. @return Const end iterator. */
89 using std::end;
90 return end(storage);
91 }
92 /** @brief Return the number of key-value pairs. @return Number of elements. */
93 size_t size() const {
94 return storage.size();
95 }
96 /** @brief Return @c true if the map contains no elements. @return @c true if empty. */
97 bool empty() const {
98 return storage.empty();
99 }
100 // these only have to be valid if called:
101 /** @brief Reserve storage for at least @p n elements. @param n Minimum capacity. */
102 void reserve(size_t n) {
103 storage.reserve(n);
104 }
105 /** @brief Return the current storage capacity. @return Number of elements that can be stored without reallocation. */
106 size_t capacity() const {
107 return storage.capacity();
108 }
109 // map-like interface:
110 /**
111 * @brief Access or insert the value for key @p k.
112 * @param k Key to look up or insert.
113 * @return Reference to the associated value.
114 */
115 template<class K, class=std::enable_if_t<std::is_convertible<K, Key>{}>>
116 Value& operator[](K&& k){
117 auto it = find(k);
118 if (it != end()) return it->second;
119 storage.emplace_back( std::forward<K>(k), Value{} );
120 return storage.back().second;
121 }
122private:
123 /** @brief Return a predicate matching key @p k. @param k Key to match against. @return Lambda matching pairs whose first element equals @p k. */
124 template<class K, class=std::enable_if_t<std::is_convertible<K, Key>{}>>
125 auto key_match( K& k ) {
126 return [&k](const std::pair<Key, Value>& kv){
127 return kv.first == k;
128 };
129 }
130public:
131 /**
132 * @brief Find the element with key @p k.
133 * @param k Key to search for.
134 * @return Iterator to the element, or @c end() if not found.
135 */
136 template<class K, class=std::enable_if_t<std::is_convertible<K, Key>{}>>
137 iterator find(K&& k) {
138 return std::find_if( begin(), end(), key_match(k) );
139 }
140 /**
141 * @brief Find the element with key @p k (const overload).
142 * @param k Key to search for.
143 * @return Const iterator to the element, or @c end() if not found.
144 */
145 template<class K, class=std::enable_if_t<std::is_convertible<K, Key>{}>>
146 const_iterator find(K&& k) const {
147 return const_cast<flat_map*>(this)->find(k);
148 }
149 /**
150 * @brief Remove the element at @p it.
151 * @param it Iterator to the element to remove.
152 * @return Iterator to the element following the removed one.
153 */
155 return storage.erase(it);
156 }
157 /**
158 * @brief Insert or update a key-value pair.
159 * @param value Key-value pair to insert or update.
160 */
161 template<class P, class=std::enable_if_t<std::is_convertible<P,std::pair<Key, Value>>{}>>
162 void insert( P&& value )
163 {
164 auto it = find(value.first);
165 if (it == end())
166 storage.emplace_back( std::forward<P>(value) );
167 else
168 it->second = value.second;
169 }
170
171 /**
172 * @brief Compare two flat_maps for equality.
173 * @param rhs Right-hand side map.
174 * @return @c true if both maps contain the same key-value pairs.
175 */
176 bool operator==(const flat_map &rhs) const
177 {
178 if(size()!=rhs.size())
179 return false;
180 for(const auto &[k, v]: rhs) {
181 auto it = find(k);
182 if(it==end())
183 return false;
184 else if(it->second != v)
185 return false;
186 }
187
188 return true;
189 }
190
191 /**
192 * @brief Lexicographic less-than comparison (order-independent on keys).
193 * @param rhs Right-hand side map.
194 * @return @c true if this map is less than @p rhs.
195 */
196 bool operator<(const flat_map &rhs) const
197 {
198 if(size()<rhs.size())
199 return true;
200 else if(size()>rhs.size())
201 return false;
202 storage_t x1{storage}, x2{rhs.storage};
203 std::sort(x1.begin(), x1.end());
204 std::sort(x2.begin(), x2.end());
205
206 for(auto it1 = x1.begin(), it2 = x2.begin();
207 it1!=x1.end();
208 ++it1, ++it2) {
209 if(*it1<*it2)
210 return true;
211 else if(*it1>*it2)
212 return false;
213 }
214
215 return false;
216 }
217};
218
219namespace std {
220 /**
221 * @brief @c std::hash specialisation for @c flat_map, enabling use as an unordered container key.
222 */
223 template<class K, class V, template<class...> class Storage>
224 struct hash<flat_map<K, V, Storage>> {
225 /**
226 * @brief Compute the hash of a @c flat_map by XOR-folding element hashes.
227 * @param key The map to hash.
228 * @return Combined hash value.
229 */
230 size_t operator()(const flat_map<K, V, Storage> &key) const
231 {
232 size_t result = 0xDEADBEEF;
233 for(const auto &[k,v]: key)
234 result ^= std::hash<K>()(k) ^ std::hash<V>()(v);
235 return result;
236 }
237 };
238}
239#endif /* FLAT_MAP_H */
Flat associative map with pluggable storage.
Definition flat_map.hpp:49
Storage< std::pair< Key, Value > > storage_t
Underlying container type.
Definition flat_map.hpp:50
flat_map()=default
const_iterator cend() const
Return const iterator past the last element.
Definition flat_map.hpp:88
iterator find(K &&k)
Find the element with key k.
Definition flat_map.hpp:137
bool empty() const
Return true if the map contains no elements.
Definition flat_map.hpp:97
size_t size() const
Return the number of key-value pairs.
Definition flat_map.hpp:93
const_iterator end() const
Return const iterator past the last element.
Definition flat_map.hpp:83
iterator begin()
Return iterator to the first element.
Definition flat_map.hpp:63
flat_map(std::initializer_list< std::pair< Key, Value > > init)
Construct from an initializer list of key-value pairs.
Definition flat_map.hpp:59
const_iterator cbegin() const
Return const iterator to the first element.
Definition flat_map.hpp:73
void insert(P &&value)
Insert or update a key-value pair.
Definition flat_map.hpp:162
auto key_match(K &k)
Return a predicate matching key k.
Definition flat_map.hpp:125
decltype(begin(std::declval< const storage_t & >())) const_iterator
Const iterator.
Definition flat_map.hpp:54
const_iterator begin() const
Return const iterator to the first element.
Definition flat_map.hpp:68
size_t capacity() const
Return the current storage capacity.
Definition flat_map.hpp:106
iterator end()
Return iterator past the last element.
Definition flat_map.hpp:78
iterator erase(const_iterator it)
Remove the element at it.
Definition flat_map.hpp:154
storage_t storage
Contiguous key-value storage.
Definition flat_map.hpp:51
Value & operator[](K &&k)
Access or insert the value for key k.
Definition flat_map.hpp:116
const_iterator find(K &&k) const
Find the element with key k (const overload).
Definition flat_map.hpp:146
bool operator==(const flat_map &rhs) const
Compare two flat_maps for equality.
Definition flat_map.hpp:176
bool operator<(const flat_map &rhs) const
Lexicographic less-than comparison (order-independent on keys).
Definition flat_map.hpp:196
decltype(begin(std::declval< storage_t & >())) iterator
Mutable iterator.
Definition flat_map.hpp:53
void reserve(size_t n)
Reserve storage for at least n elements.
Definition flat_map.hpp:102
size_t operator()(const flat_map< K, V, Storage > &key) const
Compute the hash of a flat_map by XOR-folding element hashes.
Definition flat_map.hpp:230