C/C++ API Reference
Loading...
Searching...
No Matches
flat_map.h
1// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <algorithm>
17#include <array>
18#include <cstddef>
19#include <iterator>
20#include <type_traits>
21#include <utility>
22
23#include "pw_assert/assert.h"
24
25namespace pw::containers {
26
27// Define and use a custom Pair object. This is because std::pair does not
28// support constexpr assignment until C++20. The assignment is needed since
29// the array of pairs will be sorted in the constructor (if not already).
30template <typename First, typename Second>
31struct Pair {
32 First first;
33 Second second;
34
35 bool operator==(const Pair& p) const {
36 return first == p.first && second == p.second;
37 }
38
39 bool operator!=(const Pair& p) const { return !(*this == p); }
40};
41
42template <typename T1, typename T2>
43Pair(T1, T2) -> Pair<T1, T2>;
44
46
49
54template <typename Key, typename Value, size_t kArraySize>
55class FlatMap {
56 public:
57 using key_type = Key;
58 using mapped_type = Value;
60 using pointer = value_type*;
61 using reference = value_type&;
62 using size_type = size_t;
63 using difference_type = ptrdiff_t;
64 using container_type = typename std::array<value_type, kArraySize>;
65 using iterator = typename container_type::iterator;
66 using const_iterator = typename container_type::const_iterator;
67
72 public:
73 using value_type = FlatMap::mapped_type;
74 using difference_type = std::ptrdiff_t;
75 using pointer = value_type*;
76 using reference = value_type&;
77 using iterator_category = std::bidirectional_iterator_tag;
78
79 constexpr mapped_iterator() : current_{} {}
80
81 constexpr mapped_iterator(const mapped_iterator& other) = default;
82 constexpr mapped_iterator& operator=(const mapped_iterator& other) =
83 default;
84
85 constexpr reference operator*() const {
86 reference value = current_->second;
87 return value;
88 }
89
90 constexpr pointer operator->() const { return &operator*(); }
91
92 constexpr mapped_iterator& operator++() {
93 ++current_;
94 return *this;
95 }
96
97 constexpr mapped_iterator operator++(int) {
98 mapped_iterator original = *this;
99 operator++();
100 return original;
101 }
102
103 constexpr mapped_iterator& operator--() {
104 --current_;
105 return *this;
106 }
107
108 constexpr mapped_iterator operator--(int) {
109 mapped_iterator original = *this;
110 operator--();
111 return original;
112 }
113
114 constexpr bool operator==(const mapped_iterator& other) const {
115 return current_ == other.current_;
116 }
117
118 constexpr bool operator!=(const mapped_iterator& other) const {
119 return !(*this == other);
120 }
121
122 private:
123 friend class FlatMap;
124
125 constexpr mapped_iterator(FlatMap::iterator current) : current_(current) {}
126
127 FlatMap::iterator current_;
128 };
129
130 constexpr FlatMap(const std::array<value_type, kArraySize>& items)
131 : items_(items) {
132 ConstexprSort(items_.begin(), kArraySize);
133 }
134
135 constexpr FlatMap(std::array<value_type, kArraySize>&& items)
136 : items_(std::move(items)) {
137 ConstexprSort(items_.begin(), kArraySize);
138 }
139
140 // Omits explicit here to support assignment-like syntax, which is common to
141 // initialize a container.
142 template <typename... Items,
143 typename = std::enable_if_t<
144 std::conjunction_v<std::is_same<Items, value_type>...>>>
145 constexpr FlatMap(const Items&... items) : FlatMap(std::array{items...}) {}
146
147 FlatMap(FlatMap&) = delete;
148 FlatMap& operator=(FlatMap&) = delete;
149
150 // Capacity.
151 constexpr size_type size() const { return kArraySize; }
152 constexpr size_type empty() const { return size() == 0; }
153 constexpr size_type max_size() const { return kArraySize; }
154
155 // Lookup.
163 constexpr mapped_type& at(const key_type& key) {
164 PW_ASSERT(end() != begin());
165 iterator it = std::lower_bound(
166 items_.begin(), items_.end(), key, IsItemKeyLessThanGivenKey);
167 const key_type& found_key = it->first;
168 PW_ASSERT(it != items_.end() && found_key == key);
169 mapped_type& found_value = it->second;
170 return found_value;
171 }
172
180 constexpr const mapped_type& at(const key_type& key) const {
181 PW_ASSERT(end() != begin());
182 const_iterator it = std::lower_bound(
183 items_.cbegin(), items_.cend(), key, IsItemKeyLessThanGivenKey);
184 const key_type& found_key = it->first;
185 PW_ASSERT(it != items_.cend() && found_key == key);
186 const mapped_type& found_value = it->second;
187 return found_value;
188 }
189
190 constexpr bool contains(const key_type& key) const {
191 return find(key) != end();
192 }
193
194 constexpr const_iterator find(const key_type& key) const {
195 if (end() == begin()) {
196 return end();
197 }
198
199 const_iterator it = lower_bound(key);
200 if (it == end() || it->first != key) {
201 return end();
202 }
203 return it;
204 }
205
206 constexpr const_iterator lower_bound(const key_type& key) const {
207 return std::lower_bound(begin(), end(), key, IsItemKeyLessThanGivenKey);
208 }
209
210 constexpr const_iterator upper_bound(const key_type& key) const {
211 return std::upper_bound(
212 begin(), end(), key, [](key_type lkey, const value_type& item) {
213 return item.first > lkey;
214 });
215 }
216
217 constexpr std::pair<const_iterator, const_iterator> equal_range(
218 const key_type& key) const {
219 if (end() == begin()) {
220 return std::make_pair(end(), end());
221 }
222
223 return std::make_pair(lower_bound(key), upper_bound(key));
224 }
225
226 // Iterators.
227 constexpr const_iterator begin() const { return cbegin(); }
228 constexpr const_iterator cbegin() const { return items_.cbegin(); }
229 constexpr const_iterator end() const { return cend(); }
230 constexpr const_iterator cend() const { return items_.cend(); }
231
239 return mapped_iterator(items_.begin());
240 }
241
249 return mapped_iterator(items_.end());
250 }
251
252 private:
253 // Simple stable insertion sort function for constexpr support.
254 // std::stable_sort is not constexpr. Should not be a problem with performance
255 // in regards to the sizes that are typically dealt with.
256 static constexpr void ConstexprSort(iterator data, size_type size) {
257 if (size < 2) {
258 return;
259 }
260
261 for (iterator it = data + 1,
262 end = data + static_cast<difference_type>(size);
263 it < end;
264 ++it) {
265 if (it->first < it[-1].first) {
266 // Rotate the value into place.
267 value_type temp = std::move(*it);
268 iterator it2 = it - 1;
269 while (true) {
270 *(it2 + 1) = std::move(*it2);
271 if (it2 == data || !(temp.first < it2[-1].first)) {
272 break;
273 }
274 --it2;
275 }
276 *it2 = std::move(temp);
277 }
278 }
279 }
280
281 static constexpr bool IsItemKeyLessThanGivenKey(const value_type& item,
282 key_type key) {
283 const key_type& item_key = item.first;
284 return item_key < key;
285 }
286
287 std::array<value_type, kArraySize> items_;
288};
289
290template <typename K, typename V, typename... Items>
291FlatMap(const Pair<K, V>& item1, const Items&... items)
292 -> FlatMap<K, V, 1 + sizeof...(items)>;
293
294} // namespace pw::containers
Definition: flat_map.h:55
constexpr mapped_type & at(const key_type &key)
Definition: flat_map.h:163
constexpr mapped_iterator mapped_begin()
Definition: flat_map.h:238
constexpr mapped_iterator mapped_end()
Definition: flat_map.h:248
constexpr const mapped_type & at(const key_type &key) const
Definition: flat_map.h:180
Definition: flat_map.h:31