FastLED 3.9.15
Loading...
Searching...
No Matches
flat_map.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/stl/stdint.h"
4#include "fl/stl/assert.h"
5#include "fl/stl/algorithm.h"
6#include "fl/stl/assert.h" // IWYU pragma: keep
8#include "fl/stl/pair.h"
9#include "fl/stl/vector.h"
10#include "fl/stl/allocator.h"
12#include "platforms/assert_defs.h"
13#include "fl/stl/noexcept.h"
14
15namespace fl {
16
17// A cache-friendly sorted map using contiguous storage.
18//
19// flat_map stores key-value pairs in a sorted vector for optimal memory layout.
20// This provides excellent cache locality and iteration performance, but slower
21// insertions and deletions (O(n)) compared to tree-based maps. Use flat_map when:
22// - You have frequent lookups and iteration (better cache locality)
23// - You don't do many insertions/deletions
24// - Memory layout matters for performance
25//
26// The container maintains sorted order by key and uses binary search for lookups.
27template <typename Key, typename Value,
28 typename Less = fl::less<Key>>
29class flat_map {
30 public:
31 enum insert_result { inserted = 0, exists = 1, at_capacity = 2 };
32
33 using key_type = Key;
34 using mapped_type = Value;
36 using size_type = fl::size;
38 using key_compare = Less;
42 using const_pointer = const value_type*;
43
47 using reverse_iterator = typename vector_type::reverse_iterator;
48 using const_reverse_iterator = typename vector_type::const_reverse_iterator;
49
50 private:
52 Less mLess;
53
54 public:
55 // Constructors
56 flat_map() = default;
57
59 : mData(resource) {}
60
61 explicit flat_map(const Less& less) FL_NOEXCEPT
62 : mLess(less) {}
63
64 flat_map(const Less& less, memory_resource* resource) FL_NOEXCEPT
65 : mData(resource), mLess(less) {}
66
68 : mData(other.mData), mLess(other.mLess) {}
69 flat_map& operator=(const flat_map& other) = default;
70
72 : mData(fl::move(other.mData)), mLess(fl::move(other.mLess)) {}
73
75 if (this != &other) {
76 mData = fl::move(other.mData);
77 mLess = fl::move(other.mLess);
78 }
79 return *this;
80 }
81
82 // Iterators
83 iterator begin() FL_NOEXCEPT { return mData.begin(); }
84 iterator end() FL_NOEXCEPT { return mData.end(); }
85 const_iterator begin() const FL_NOEXCEPT { return mData.begin(); }
86 const_iterator end() const FL_NOEXCEPT { return mData.end(); }
87 const_iterator cbegin() const FL_NOEXCEPT { return mData.begin(); }
88 const_iterator cend() const FL_NOEXCEPT { return mData.end(); }
89
92 const_reverse_iterator rbegin() const FL_NOEXCEPT { return mData.rbegin(); }
93 const_reverse_iterator rend() const FL_NOEXCEPT { return mData.rend(); }
94
95 // Capacity
96 size_type size() const FL_NOEXCEPT { return mData.size(); }
97 bool empty() const FL_NOEXCEPT { return mData.empty(); }
98 size_type capacity() const FL_NOEXCEPT { return mData.capacity(); }
99 size_type max_size() const FL_NOEXCEPT { return mData.max_size(); }
100 bool full() const FL_NOEXCEPT { return size() >= capacity(); }
101
102 // Element access - check keys are equal (not less than in either direction)
103 Value& operator[](const Key& key) FL_NOEXCEPT {
104 auto it = lower_bound(key);
105 if (it != end() && !mLess(key, it->first) && !mLess(it->first, key)) {
106 return it->second;
107 }
108 // Key not found, insert it with default value
109 bool success = mData.insert(it, value_type(key, Value()));
110 FASTLED_ASSERT(success, "Insert failed in flat_map::operator[]");
111 // Find the newly inserted element
112 it = find(key);
113 return it->second;
114 }
115
116 Value& at(const Key& key) FL_NOEXCEPT {
117 auto it = lower_bound(key);
118 if (it != end() && !mLess(key, it->first) && !mLess(it->first, key)) {
119 return it->second;
120 }
121 // Key not found - could throw, but FastLED style is to assert
122 FASTLED_ASSERT(false, "Key not found in flat_map");
123 return mData.front().second; // unreachable
124 }
125
126 const Value& at(const Key& key) const FL_NOEXCEPT {
127 auto it = lower_bound(key);
128 if (it != end() && !mLess(key, it->first) && !mLess(it->first, key)) {
129 return it->second;
130 }
131 FASTLED_ASSERT(false, "Key not found in flat_map");
132 return mData.front().second; // unreachable
133 }
134
135 // Lookup
137 auto it = lower_bound(key);
138 if (it != end() && !mLess(key, it->first) && !mLess(it->first, key)) {
139 return it;
140 }
141 return end();
142 }
143
144 const_iterator find(const Key& key) const FL_NOEXCEPT {
145 auto it = lower_bound(key);
146 if (it != end() && !mLess(key, it->first) && !mLess(it->first, key)) {
147 return it;
148 }
149 return end();
150 }
151
152 size_type count(const Key& key) const FL_NOEXCEPT {
153 return find(key) != end() ? 1 : 0;
154 }
155
156 bool contains(const Key& key) const FL_NOEXCEPT {
157 return find(key) != end();
158 }
159
160 // Alias for contains (FastLED compatibility)
161 bool has(const Key& key) const FL_NOEXCEPT {
162 return contains(key);
163 }
164
165 // Bounds - binary search using pointer arithmetic
167 // Binary search: find first element where !(element < key)
168 iterator first = mData.begin();
169 size_type count = mData.size();
170
171 while (count > 0) {
172 size_type step = count / 2;
173 iterator it = first + step;
174 if (mLess(it->first, key)) {
175 first = it + 1;
176 count -= step + 1;
177 } else {
178 count = step;
179 }
180 }
181 return first;
182 }
183
185 // Binary search: find first element where !(element < key)
186 const_iterator first = mData.begin();
187 size_type count = mData.size();
188
189 while (count > 0) {
190 size_type step = count / 2;
191 const_iterator it = first + step;
192 if (mLess(it->first, key)) {
193 first = it + 1;
194 count -= step + 1;
195 } else {
196 count = step;
197 }
198 }
199 return first;
200 }
201
203 // Binary search: find first element where key < element
204 iterator first = mData.begin();
205 size_type count = mData.size();
206
207 while (count > 0) {
208 size_type step = count / 2;
209 iterator it = first + step;
210 if (!mLess(key, it->first)) {
211 first = it + 1;
212 count -= step + 1;
213 } else {
214 count = step;
215 }
216 }
217 return first;
218 }
219
221 // Binary search: find first element where key < element
222 const_iterator first = mData.begin();
223 size_type count = mData.size();
224
225 while (count > 0) {
226 size_type step = count / 2;
227 const_iterator it = first + step;
228 if (!mLess(key, it->first)) {
229 first = it + 1;
230 count -= step + 1;
231 } else {
232 count = step;
233 }
234 }
235 return first;
236 }
237
239 auto lower = lower_bound(key);
240 auto upper = upper_bound(key);
241 return fl::pair<iterator, iterator>(lower, upper);
242 }
243
245 auto lower = lower_bound(key);
246 auto upper = upper_bound(key);
247 return fl::pair<const_iterator, const_iterator>(lower, upper);
248 }
249
250 // Insertion
252 auto it = lower_bound(value.first);
253 if (it != end() && !mLess(value.first, it->first) && !mLess(it->first, value.first)) {
254 return fl::pair<iterator, bool>(it, false); // Already exists
255 }
256 bool success = mData.insert(it, value);
257 if (success) {
258 // After insert, find the newly inserted element
259 it = find(value.first);
260 return fl::pair<iterator, bool>(it, true);
261 }
262 return fl::pair<iterator, bool>(end(), false);
263 }
264
266 auto key = value.first;
267 auto it = lower_bound(key);
268 if (it != end() && !mLess(key, it->first) && !mLess(it->first, key)) {
269 return fl::pair<iterator, bool>(it, false); // Already exists
270 }
271 bool success = mData.insert(it, fl::move(value));
272 if (success) {
273 // After insert, find the newly inserted element
274 it = find(key);
275 return fl::pair<iterator, bool>(it, true);
276 }
277 return fl::pair<iterator, bool>(end(), false);
278 }
279
280 // Insert with hint (optimization hint, may be ignored)
284
288
289 bool insert(const Key& key, const Value& value, insert_result* result = nullptr) FL_NOEXCEPT {
290 auto it = lower_bound(key);
291 if (it != end() && !mLess(key, it->first) && !mLess(it->first, key)) {
292 if (result) *result = exists;
293 return false;
294 }
295 bool success = mData.insert(it, value_type(key, value));
296 if (success) {
297 if (result) *result = inserted;
298 return true;
299 }
300 if (result) *result = at_capacity;
301 return false;
302 }
303
304 bool insert(Key&& key, Value&& value, insert_result* result = nullptr) FL_NOEXCEPT {
305 auto key_copy = key;
306 auto it = lower_bound(key_copy);
307 if (it != end() && !mLess(key_copy, it->first) && !mLess(it->first, key_copy)) {
308 if (result) *result = exists;
309 return false;
310 }
311 bool success = mData.insert(it, value_type(fl::move(key), fl::move(value)));
312 if (success) {
313 if (result) *result = inserted;
314 return true;
315 }
316 if (result) *result = at_capacity;
317 return false;
318 }
319
320 // Emplace
321 template <typename... Args>
326
327 template <typename... Args>
332
333 // Deletion
335 // Vector erase() returns iterator in some versions, bool in others
336 // To be safe, erase and return the next element
337 iterator next = pos + 1;
338 mData.erase(pos);
339 return next;
340 }
341
343 // Convert const_iterator to iterator and erase
344 iterator it = const_cast<iterator>(pos);
345 iterator next = it + 1;
346 mData.erase(it);
347 return next;
348 }
349
351 // Erase range [first, last) by repeatedly erasing the element at first
352 fl::size count = last - first;
353 iterator pos = const_cast<iterator>(first);
354
355 for (fl::size i = 0; i < count && pos != end(); ++i) {
356 // Erase the element at pos. After erase, pos points to the next element
357 // (because all elements shift left)
358 mData.erase(pos);
359 // Don't increment pos - it already points to the next element after erase
360 }
361 return pos;
362 }
363
365 auto it = find(key);
366 if (it != end()) {
367 erase(it);
368 return 1;
369 }
370 return 0;
371 }
372
373 // Clear
375 mData.clear();
376 }
377
378 // Swap
379 void swap(flat_map& other) FL_NOEXCEPT {
380 mData.swap(other.mData);
381 fl::swap(mLess, other.mLess);
382 }
383
384 // Comparison
386 return mLess;
387 }
388
389 // Element access
391 FASTLED_ASSERT(!empty(), "flat_map::front() on empty map");
392 return mData.front();
393 }
394
395 const value_type& front() const FL_NOEXCEPT {
396 FASTLED_ASSERT(!empty(), "flat_map::front() on empty map");
397 return mData.front();
398 }
399
401 FASTLED_ASSERT(!empty(), "flat_map::back() on empty map");
402 return mData.back();
403 }
404
405 const value_type& back() const FL_NOEXCEPT {
406 FASTLED_ASSERT(!empty(), "flat_map::back() on empty map");
407 return mData.back();
408 }
409
410 // FastLED-specific methods
411
412 // Get value with default return if not found
413 Value get(const Key& key, const Value& defaultValue) const FL_NOEXCEPT {
414 auto it = find(key);
415 if (it != end()) {
416 return it->second;
417 }
418 return defaultValue;
419 }
420
421 // Get value with out parameter
422 bool get(const Key& key, Value* out_value) const FL_NOEXCEPT {
423 auto it = find(key);
424 if (it != end()) {
425 *out_value = it->second;
426 return true;
427 }
428 return false;
429 }
430
431 // Insert or update (returns whether it was inserted)
433 iterator it = find(key);
434 if (it != end()) {
435 it->second = value;
436 return fl::pair<iterator, bool>(it, false); // Updated, not inserted
437 }
438 return insert(value_type(key, value));
439 }
440
441 // FastLED-style update: insert if missing, update if exists
442 bool update(const Key& key, const Value& value) FL_NOEXCEPT {
443 iterator it = find(key);
444 if (it != end()) {
445 it->second = value;
446 return true; // Updated
447 }
448 // Key doesn't exist, insert it
449 auto result = insert(value_type(key, value));
450 return result.second; // Return whether insertion succeeded
451 }
452
453 // Move version of update
454 bool update(const Key& key, Value&& value) FL_NOEXCEPT {
455 iterator it = find(key);
456 if (it != end()) {
457 it->second = fl::move(value);
458 return true; // Updated
459 }
460 // Key doesn't exist, insert it
461 auto result = insert(value_type(key, fl::move(value)));
462 return result.second; // Return whether insertion succeeded
463 }
464
465 // Navigate to next element by key (FastLED compatibility)
466 bool next(const Key& key, Key* next_key, bool allow_rollover = false) const FL_NOEXCEPT {
467 auto it = find(key);
468 if (it != end()) {
469 ++it;
470 if (it != end()) {
471 *next_key = it->first;
472 return true;
473 } else if (allow_rollover && !empty()) {
474 *next_key = begin()->first;
475 return true;
476 }
477 }
478 return false;
479 }
480
481 // Navigate to previous element by key (FastLED compatibility)
482 bool prev(const Key& key, Key* prev_key, bool allow_rollover = false) const FL_NOEXCEPT {
483 auto it = find(key);
484 if (it != end()) {
485 if (it != begin()) {
486 --it;
487 *prev_key = it->first;
488 return true;
489 } else if (allow_rollover && !empty()) {
490 *prev_key = mData.back().first;
491 return true;
492 }
493 }
494 return false;
495 }
496
497 // Allows pre-allocating space
499 mData.reserve(n);
500 }
501
502 // Shrink capacity to fit size
504 mData.shrink_to_fit();
505 }
506
507 memory_resource* get_memory_resource() const FL_NOEXCEPT { return mData.get_resource(); }
508};
509
510// Comparison operators
511template <typename Key, typename Value, typename Less>
514 return lhs.size() == rhs.size() &&
515 fl::equal(lhs.begin(), lhs.end(), rhs.begin());
516}
517
518template <typename Key, typename Value, typename Less>
521 return !(lhs == rhs);
522}
523
524template <typename Key, typename Value, typename Less>
527 return fl::lexicographical_compare(lhs.begin(), lhs.end(),
528 rhs.begin(), rhs.end());
529}
530
531template <typename Key, typename Value, typename Less>
534 return !(rhs < lhs);
535}
536
537template <typename Key, typename Value, typename Less>
540 return rhs < lhs;
541}
542
543template <typename Key, typename Value, typename Less>
546 return !(lhs < rhs);
547}
548
549// Swap
550template <typename Key, typename Value, typename Less>
553 lhs.swap(rhs);
554}
555
556} // namespace fl
uint8_t pos
Definition Blur.ino:11
bool insert(Key &&key, Value &&value, insert_result *result=nullptr) FL_NOEXCEPT
Definition flat_map.h:304
size_type size() const FL_NOEXCEPT
Definition flat_map.h:96
void reserve(size_type n) FL_NOEXCEPT
Definition flat_map.h:498
const_reverse_iterator rend() const FL_NOEXCEPT
Definition flat_map.h:93
fl::pair< iterator, bool > insert_or_update(const Key &key, const Value &value) FL_NOEXCEPT
Definition flat_map.h:432
flat_map(memory_resource *resource) FL_NOEXCEPT
Definition flat_map.h:58
fl::pair< iterator, bool > emplace(Args &&... args) FL_NOEXCEPT
Definition flat_map.h:322
void shrink_to_fit() FL_NOEXCEPT
Definition flat_map.h:503
bool full() const FL_NOEXCEPT
Definition flat_map.h:100
bool contains(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:156
const_iterator end() const FL_NOEXCEPT
Definition flat_map.h:86
bool prev(const Key &key, Key *prev_key, bool allow_rollover=false) const FL_NOEXCEPT
Definition flat_map.h:482
Value get(const Key &key, const Value &defaultValue) const FL_NOEXCEPT
Definition flat_map.h:413
typename vector_type::reverse_iterator reverse_iterator
Definition flat_map.h:47
typename vector_type::iterator iterator
Definition flat_map.h:45
fl::pair< const_iterator, const_iterator > equal_range(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:244
bool update(const Key &key, const Value &value) FL_NOEXCEPT
Definition flat_map.h:442
const_iterator upper_bound(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:220
typename vector_type::const_reverse_iterator const_reverse_iterator
Definition flat_map.h:48
flat_map & operator=(flat_map &&other) FL_NOEXCEPT
Definition flat_map.h:74
const_iterator find(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:144
void clear() FL_NOEXCEPT
Definition flat_map.h:374
flat_map(const Less &less) FL_NOEXCEPT
Definition flat_map.h:61
const_iterator begin() const FL_NOEXCEPT
Definition flat_map.h:85
flat_map & operator=(const flat_map &other)=default
bool update(const Key &key, Value &&value) FL_NOEXCEPT
Definition flat_map.h:454
fl::less< int > mLess
Definition flat_map.h:52
size_type max_size() const FL_NOEXCEPT
Definition flat_map.h:99
iterator insert(const_iterator, const value_type &value) FL_NOEXCEPT
Definition flat_map.h:281
const_iterator lower_bound(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:184
key_compare key_comp() const FL_NOEXCEPT
Definition flat_map.h:385
const value_type & const_reference
Definition flat_map.h:40
size_type count(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:152
flat_map(flat_map &&other) FL_NOEXCEPT
Definition flat_map.h:71
fl::pair< int, FxPtr > value_type
Definition flat_map.h:35
bool insert(const Key &key, const Value &value, insert_result *result=nullptr) FL_NOEXCEPT
Definition flat_map.h:289
iterator lower_bound(const Key &key) FL_NOEXCEPT
Definition flat_map.h:166
iterator erase(const_iterator pos) FL_NOEXCEPT
Definition flat_map.h:342
memory_resource * get_memory_resource() const FL_NOEXCEPT
Definition flat_map.h:507
size_type capacity() const FL_NOEXCEPT
Definition flat_map.h:98
const Value & at(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:126
const value_type & back() const FL_NOEXCEPT
Definition flat_map.h:405
reverse_iterator rend() FL_NOEXCEPT
Definition flat_map.h:91
value_type & front() FL_NOEXCEPT
Definition flat_map.h:390
flat_map()=default
value_type & back() FL_NOEXCEPT
Definition flat_map.h:400
const_iterator cend() const FL_NOEXCEPT
Definition flat_map.h:88
iterator begin() FL_NOEXCEPT
Definition flat_map.h:83
bool next(const int &key, int *next_key, bool allow_rollover=false) const FL_NOEXCEPT
Definition flat_map.h:466
fl::pair< iterator, bool > insert(value_type &&value) FL_NOEXCEPT
Definition flat_map.h:265
size_type erase(const Key &key) FL_NOEXCEPT
Definition flat_map.h:364
fl::pair< iterator, iterator > equal_range(const Key &key) FL_NOEXCEPT
Definition flat_map.h:238
const value_type & front() const FL_NOEXCEPT
Definition flat_map.h:395
iterator end() FL_NOEXCEPT
Definition flat_map.h:84
bool get(const Key &key, Value *out_value) const FL_NOEXCEPT
Definition flat_map.h:422
const_iterator cbegin() const FL_NOEXCEPT
Definition flat_map.h:87
Value & operator[](const Key &key) FL_NOEXCEPT
Definition flat_map.h:103
iterator emplace_hint(const_iterator hint, Args &&... args) FL_NOEXCEPT
Definition flat_map.h:328
reverse_iterator rbegin() FL_NOEXCEPT
Definition flat_map.h:90
iterator find(const Key &key) FL_NOEXCEPT
Definition flat_map.h:136
typename vector_type::const_iterator const_iterator
Definition flat_map.h:46
flat_map(const flat_map &other) FL_NOEXCEPT
Definition flat_map.h:67
const value_type * const_pointer
Definition flat_map.h:42
iterator erase(iterator pos) FL_NOEXCEPT
Definition flat_map.h:334
iterator erase(const_iterator first, const_iterator last) FL_NOEXCEPT
Definition flat_map.h:350
fl::vector< value_type > vector_type
Definition flat_map.h:44
bool has(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:161
fl::less< int > key_compare
Definition flat_map.h:38
Value & at(const Key &key) FL_NOEXCEPT
Definition flat_map.h:116
void swap(flat_map &other) FL_NOEXCEPT
Definition flat_map.h:379
iterator upper_bound(const Key &key) FL_NOEXCEPT
Definition flat_map.h:202
iterator insert(const_iterator, value_type &&value) FL_NOEXCEPT
Definition flat_map.h:285
const_reverse_iterator rbegin() const FL_NOEXCEPT
Definition flat_map.h:92
fl::pair< iterator, bool > insert(const value_type &value) FL_NOEXCEPT
Definition flat_map.h:251
bool empty() const FL_NOEXCEPT
Definition flat_map.h:97
flat_map(const Less &less, memory_resource *resource) FL_NOEXCEPT
Definition flat_map.h:64
Polymorphic memory resource base class (PMR-style).
const value_type * const_iterator
Definition vector.h:453
value_type * iterator
Definition vector.h:452
PMR-style polymorphic memory resource for type-erased allocation.
constexpr T && forward(typename remove_reference< T >::type &t) FL_NOEXCEPT
Definition s16x16x4.h:234
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
Definition s16x16x4.h:28
void swap(T &a, T &b) FL_NOEXCEPT
Definition s16x16x4.h:877
constexpr int type_rank< T >::value
bool lexicographical_compare(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) FL_NOEXCEPT
Definition algorithm.h:146
FASTLED_FORCE_INLINE bool operator!=(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if two CRGB objects do not have the same color data.
Definition crgb.h:739
bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2) FL_NOEXCEPT
Definition algorithm.h:96
void swap(array< T, N > &lhs, array< T, N > &rhs) FL_NOEXCEPT
Definition array.h:209
FASTLED_FORCE_INLINE bool operator<(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if the sum of the color channels in one CRGB object is less than another.
Definition crgb.h:745
FASTLED_FORCE_INLINE bool operator==(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if two CRGB objects have the same color data.
Definition crgb.h:733
FASTLED_FORCE_INLINE bool operator>(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if the sum of the color channels in one CRGB object is greater than another.
Definition crgb.h:754
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
FASTLED_FORCE_INLINE bool operator<=(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if the sum of the color channels in one CRGB object is less than or equal to another.
Definition crgb.h:772
constexpr enable_if< is_fixed_point< T >::value, T >::type step(T edge, T x) FL_NOEXCEPT
fl::ptrdiff ptrdiff_t
Definition s16x16x4.h:226
FASTLED_FORCE_INLINE bool operator>=(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if the sum of the color channels in one CRGB object is greater than or equal to another.
Definition crgb.h:763
Base definition for an LED controller.
Definition crgb.hpp:179
corkscrew_args args
Definition old.h:149
#define FL_NOEXCEPT
Definition Keyboard.h:22
Binary function object that returns whether the first argument is less than the second.
Definition utility.h:15