JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
FlatMap.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPLSpatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright 2024 Jaroslav Pevno, JPLSpatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
22#include "JPLSpatial/Core.h"
24
25#if __cplusplus >= 202302L
26#include <flat_map>
27#else
28#include <vector>
29#include <utility>
30#include <functional>
31#include <stdexcept>
32#include <initializer_list>
33#endif
34
35namespace JPL
36{
37#if __cplusplus >= 202302L
38 template<class T, class ...Args>
39 using FlatMap = std::flat_map<T, Args...>;
40#else
41
42 // std::flat_map is available since C++23
43 // This is a simple implementation of the same concept.
44 // This should be faster than a regular hash map for small number of elements.
45 template<class Key,
46 class T,
47 class Equals = std::equal_to<Key>,
48 class KeyContainer = std::vector<Key>,
49 class MappedContainer = std::vector<T>>
50 class FlatMap
51 {
52 public:
53 using allocator_type = KeyContainer::allocator_type;
54
55 using key_type = Key;
56 using mapped_type = T;
57 using value_type = std::pair<key_type, mapped_type>;
59
62
63 using size_type = std::size_t;
64 using difference_type = std::ptrdiff_t;
65
66 using reference = std::pair<const key_type&, mapped_type&>;
67 using const_reference = std::pair<const key_type&, const mapped_type&>;
68
69 // iterator forbids modifying keys (like std::map / std::flat_map)
72
73 //using reverse_iterator = std::reverse_iterator<iterator>;
74 //using const_reverse_iterator = std::reverse_iterator<const_iterator>;
75 public:
76
77 FlatMap(const FlatMap&) = default;
82
84
86 FlatMap(Equals eq, const allocator_type& allocator = {}) : eq(std::move(eq)), cont(allocator) {}
87
88 template<class InputIter>
90 : eq(std::move(eq))
91 , cont(allocator)
92 {
93 const auto n = static_cast<size_type>(std::distance(first, last));
94 cont.keys.reserve(n);
95 cont.values.reserve(n);
96 for (; first != last; ++first)
97 {
98 cont.keys.push_back(first->first);
99 cont.values.push_back(first->second);
100 }
101 }
102
103 explicit FlatMap(std::initializer_list<value_type> init, const allocator_type& allocator = {})
104 : FlatMap(init.begin(), init.end(), {}, allocator)
105 {
106 }
107
108 // Storage
109 [[nodiscard]] JPL_INLINE const key_container_type& keys() const noexcept { return cont.keys; }
110 [[nodiscard]] JPL_INLINE const mapped_container_type& values() const noexcept { return cont.values; }
111
112 // Capacity
113 JPL_INLINE void reserve(std::size_t n) { cont.keys.reserve(n); cont.values.reserve(n); }
114 [[nodiscard]] JPL_INLINE size_type size() const { return cont.keys.size(); }
115 [[nodiscard]] JPL_INLINE bool empty() const { return size() == 0; }
116
117 // iteration
118 [[nodiscard]] JPL_INLINE iterator begin() noexcept { return { cont.keys.data(), cont.values.data() }; }
119 [[nodiscard]] JPL_INLINE iterator end() noexcept { return { cont.keys.data() + cont.keys.size(), cont.values.data() + cont.values.size() }; }
120 [[nodiscard]] JPL_INLINE const_iterator begin() const noexcept { return { cont.keys.data(), cont.values.data() }; }
121 [[nodiscard]] JPL_INLINE const_iterator end() const noexcept { return { cont.keys.data() + cont.keys.size(), cont.values.data() + cont.values.size() }; }
124
125 // (keeps relative order; no uniqueness check)
126 JPL_INLINE void push_back(const Key& k, const T& v) { cont.keys.push_back(k); cont.values.push_back(v); }
127
128 private:
129 // Heterogeneous support bits
130 template<class L, class R>
131 using EqResult = std::invoke_result_t<const Equals&, const L&, const R&>;
132
133 template<class L, class R>
134 static constexpr bool EqComparable =
135 requires (const Equals & e, const L & l, const R & r)
136 {
137 { std::invoke(e, l, r) } -> std::convertible_to<bool>;
138 };
139
140 public:
141
142 // ----- lookup -----
144 {
145 auto it_key = std::ranges::find_if(cont.keys, [&](const Key& k) { return eq(k, key); });
146 if (it_key == cont.keys.end())
147 return end();
148 return begin() + (it_key - cont.keys.begin());
149 }
150
152 {
153 auto it_key = std::ranges::find_if(cont.keys, [&](const Key& k) { return eq(k, key); });
154 if (it_key == cont.keys.end())
155 return end();
156 return begin() + (it_key - cont.keys.begin());
157 }
158
159 // ----- heterogeneous find
160 template<class K2> requires (EqComparable<Key, K2> || EqComparable<K2, Key>)
162 {
163 auto it_key = std::ranges::find_if(cont.keys, [&](const Key& k)
164 {
165 if constexpr (EqComparable<Key, K2>)
166 {
167 return eq(k, key_like);
168 }
169 else
170 {
171 return eq(key_like, k);
172 }
173 });
174 if (it_key == cont.keys.end())
175 return end();
176 return begin() + (it_key - cont.keys.begin());
177 }
178
179 template<class K2> requires (EqComparable<Key, K2> || EqComparable<K2, Key>)
181 {
182 auto it_key = std::ranges::find_if(cont.keys, [&](const Key& k)
183 {
184 if constexpr (EqComparable<Key, K2>)
185 {
186 return eq(k, key_like);
187 }
188 else
189 {
190 return eq(key_like, k);
191 }
192 });
193 if (it_key == cont.keys.end())
194 return end();
195 return begin() + (it_key - cont.keys.begin());
196 }
197
198 // ----- bounds-checked access -----
199 [[nodiscard]] JPL_INLINE mapped_type& at(const Key& key)
200 {
201 auto it = find(key);
202 if (it == end())
203 throw std::out_of_range("FlatMap::at: key not found");
204 return it->second;
205 }
206
207 [[nodiscard]] JPL_INLINE const mapped_type& at(const Key& key) const
208 {
209 auto it = find(key);
210 if (it == end())
211 throw std::out_of_range("FlatMap::at: key not found");
212 return it->second;
213 }
214
215
216 // ----- operator[] inserts default if absent (unsorted; appends) -----
217 [[nodiscard]] JPL_INLINE mapped_type& operator[](const Key& key)
218 {
219 if (auto it = find(key); it != end())
220 return it->second;
221 cont.keys.push_back(key);
222 cont.values.emplace_back();
223 return cont.values.back();
224 }
225
226 [[nodiscard]] JPL_INLINE mapped_type& operator[](Key&& key)
227 {
228 if (auto it = find(key); it != end())
229 return it->second;
230 cont.keys.push_back(std::move(key));
231 cont.values.emplace_back();
232 return cont.values.back();
233 }
234
235
236 // ----- insertion (no sorting; enforces uniqueness) -----
237 template<class... Args>
238 JPL_INLINE std::pair<iterator, bool> emplace(Args&&... args)
239 {
240 std::pair<Key, T> t(std::forward<Args>(args)...);
241
242 if (auto it = find(t.first); it != end())
243 return { it, false }; // already exists
244
245 // Append (unsorted policy)
246 cont.keys.emplace_back(std::move(t.first));
247 cont.values.emplace_back(std::move(t.second));
248
249 return { end() - 1, true };
250 }
251
252 // ----- erase by iterator -----
253 JPL_INLINE size_t erase(const_iterator iter)
254 {
255 if (iter == end())
256 return 0;
257 const auto idx = static_cast<size_type>(iter - cbegin());
258 cont.keys.erase(cont.keys.begin() + static_cast<difference_type>(idx));
259 cont.values.erase(cont.values.begin() + static_cast<difference_type>(idx));
260 return 1;
261 }
262
263 JPL_INLINE size_t erase(iterator it)
264 {
265 if (it == end())
266 return 0;
267 const auto idx = static_cast<size_type>(it - begin());
268 cont.keys.erase(cont.keys.begin() + static_cast<difference_type>(idx));
269 cont.values.erase(cont.values.begin() + static_cast<difference_type>(idx));
270 return 1;
271 }
272
273 // ----- erase by key -----
274 JPL_INLINE size_t erase(const Key& key)
275 {
276 if (auto it = find(key); it != end())
277 return erase(it);
278 return 0;
279 }
280
281 // heterogeneous erase by key-like
282 template<class K2> requires (EqComparable<Key, K2> || EqComparable<K2, Key>)
283 JPL_INLINE size_t erase(const K2& key_like)
284 {
285 if (auto it = find(key_like); it != end())
286 return erase(it);
287 return 0;
288 }
289
290 // ----- erase_if (predicate sees a pair-like proxy) -----
291 template<class Predicate>
292 JPL_INLINE size_t erase_if(Predicate&& pred)
293 {
294 // Stable single-pass compaction over both arrays.
295 const size_type n = size();
296 size_type w = 0; // write index
297 for (size_type i = 0; i < n; ++i)
298 {
299 pair_ref<const Key, T> ref{ cont.keys[i], cont.values[i] };
300 if (!std::invoke(pred, ref))
301 {
302 // keep item i -> move it to slot w (only if gaps have opened)
303 if (w != i)
304 {
305 cont.keys[w] = std::move(cont.keys[i]);
306 cont.values[w] = std::move(cont.values[i]);
307 }
308 ++w;
309 }
310 }
311 const size_t removed = n - w;
312 cont.keys.resize(w);
313 cont.values.resize(w);
314 return removed;
315 }
316
317 [[nodiscard]] JPL_INLINE bool contains(const Key& key) const
318 {
319 return find(key) != end();
320 }
321
322 template<class K2> requires (EqComparable<Key, K2> || EqComparable<K2, Key>)
323 [[nodiscard]] JPL_INLINE bool contains(const K2& key_like) const
324 {
325 return find(key_like) != end();
326 }
327
328 friend void swap(FlatMap& a, FlatMap& b) noexcept(
329 noexcept(std::swap(a.cont.keys, b.cont.keys)) &&
330 noexcept(std::swap(a.cont.values, b.cont.values)) &&
331 noexcept(std::swap(a.eq, b.eq))
332 )
333 {
334 using std::swap;
335 swap(a.cont.keys, b.cont.keys);
336 swap(a.cont.values, b.cont.values);
337 swap(a.eq, b.eq);
338 }
339
340 private:
341 struct containers
342 {
343 key_container_type keys;
344 mapped_container_type values;
345
346 containers() = default;
347 containers(const allocator_type& allocator)
348 : keys(allocator)
349 , values(allocator)
350 {
351 }
352 } cont;
353
354 [[no_unique_address]] Equals eq;
355 };
356#endif
357
358 // Alias to override just the allocator for the FlatMap
359 template<class KeyType, class T, template<class> class AllocatorType>
361 FlatMap<
362 KeyType,
363 T,
364 std::equal_to<KeyType>,
365 std::vector<KeyType, AllocatorType<KeyType>>,
366 std::vector<T, AllocatorType<T>>
367 >;
368} // namespace JPL
Definition FlatMap.h:51
KeyContainer key_container_type
Definition FlatMap.h:60
JPL_INLINE const_iterator end() const noexcept
Definition FlatMap.h:121
JPL_INLINE std::pair< iterator, bool > emplace(Args &&... args)
Definition FlatMap.h:238
FlatMap(std::initializer_list< value_type > init, const allocator_type &allocator={})
Definition FlatMap.h:103
JPL_INLINE const_iterator begin() const noexcept
Definition FlatMap.h:120
JPL_INLINE const_iterator cbegin() const noexcept
Definition FlatMap.h:122
KeyContainer::allocator_type allocator_type
Definition FlatMap.h:53
JPL_INLINE size_t erase(const_iterator iter)
Definition FlatMap.h:253
JPL_INLINE mapped_type & operator[](const Key &key)
Definition FlatMap.h:217
JPL_INLINE size_t erase(const Key &key)
Definition FlatMap.h:274
JPL_INLINE const_iterator find(const K2 &key_like) const
Definition FlatMap.h:180
JPL_INLINE iterator begin() noexcept
Definition FlatMap.h:118
std::pair< const key_type &, mapped_type & > reference
Definition FlatMap.h:66
FlatMap(InputIter first, InputIter last, Equals eq={}, const allocator_type &allocator={})
Definition FlatMap.h:89
JPL_INLINE const_iterator find(const Key &key) const
Definition FlatMap.h:151
JPL_INLINE bool empty() const
Definition FlatMap.h:115
T mapped_type
Definition FlatMap.h:56
JPL_INLINE const key_container_type & keys() const noexcept
Definition FlatMap.h:109
Key key_type
Definition FlatMap.h:55
JPL_INLINE size_t erase_if(Predicate &&pred)
Definition FlatMap.h:292
JPL_INLINE bool contains(const Key &key) const
Definition FlatMap.h:317
std::size_t size_type
Definition FlatMap.h:63
JPL_INLINE iterator find(const K2 &key_like)
Definition FlatMap.h:161
JPL_INLINE const mapped_type & at(const Key &key) const
Definition FlatMap.h:207
friend void swap(FlatMap &a, FlatMap &b) noexcept(noexcept(std::swap(a.cont.keys, b.cont.keys)) &&noexcept(std::swap(a.cont.values, b.cont.values)) &&noexcept(std::swap(a.eq, b.eq)))
Definition FlatMap.h:328
std::pair< key_type, mapped_type > value_type
Definition FlatMap.h:57
FlatMap(const FlatMap &)=default
JPL_INLINE iterator find(const Key &key)
Definition FlatMap.h:143
FlatMap(FlatMap &&) noexcept=default
JPL_INLINE size_t erase(const K2 &key_like)
Definition FlatMap.h:283
JPL_INLINE void reserve(std::size_t n)
Definition FlatMap.h:113
MappedContainer mapped_container_type
Definition FlatMap.h:61
JPL_INLINE const_iterator cend() const noexcept
Definition FlatMap.h:123
JPL_INLINE iterator end() noexcept
Definition FlatMap.h:119
std::ptrdiff_t difference_type
Definition FlatMap.h:64
JPL_INLINE void push_back(const Key &k, const T &v)
Definition FlatMap.h:126
JPL_INLINE size_t erase(iterator it)
Definition FlatMap.h:263
JPL_INLINE bool contains(const K2 &key_like) const
Definition FlatMap.h:323
JPL_INLINE const mapped_container_type & values() const noexcept
Definition FlatMap.h:110
JPL_INLINE mapped_type & at(const Key &key)
Definition FlatMap.h:199
Equals key_compare
Definition FlatMap.h:58
FlatMap(Equals eq, const allocator_type &allocator={})
Definition FlatMap.h:86
JPL_INLINE size_type size() const
Definition FlatMap.h:114
JPL_INLINE mapped_type & operator[](Key &&key)
Definition FlatMap.h:226
std::pair< const key_type &, const mapped_type & > const_reference
Definition FlatMap.h:67
Definition ZipIterator.h:51
Definition AcousticMaterial.h:36
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
Definition ChannelMap.h:268
Zip iterator for the FlatMap.
Definition ZipIterator.h:33