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
63template <typename Key, typename Value, size_t kArraySize>
64class FlatMap {
65 public:
66 using key_type = Key;
67 using mapped_type = Value;
69 using pointer = value_type*;
70 using reference = value_type&;
71 using size_type = size_t;
72 using difference_type = ptrdiff_t;
73 using container_type = typename std::array<value_type, kArraySize>;
74 using iterator = typename container_type::iterator;
75 using const_iterator = typename container_type::const_iterator;
76
81 public:
82 using value_type = FlatMap::mapped_type;
83 using difference_type = std::ptrdiff_t;
84 using pointer = value_type*;
85 using reference = value_type&;
86 using iterator_category = std::bidirectional_iterator_tag;
87
88 constexpr mapped_iterator() : current_{} {}
89
90 constexpr mapped_iterator(const mapped_iterator& other) = default;
91 constexpr mapped_iterator& operator=(const mapped_iterator& other) =
92 default;
93
94 constexpr reference operator*() const {
95 reference value = current_->second;
96 return value;
97 }
98
99 constexpr pointer operator->() const { return &operator*(); }
100
101 constexpr mapped_iterator& operator++() {
102 ++current_;
103 return *this;
104 }
105
106 constexpr mapped_iterator operator++(int) {
107 mapped_iterator original = *this;
108 operator++();
109 return original;
110 }
111
112 constexpr mapped_iterator& operator--() {
113 --current_;
114 return *this;
115 }
116
117 constexpr mapped_iterator operator--(int) {
118 mapped_iterator original = *this;
119 operator--();
120 return original;
121 }
122
123 constexpr bool operator==(const mapped_iterator& other) const {
124 return current_ == other.current_;
125 }
126
127 constexpr bool operator!=(const mapped_iterator& other) const {
128 return !(*this == other);
129 }
130
131 private:
132 friend class FlatMap;
133
134 constexpr mapped_iterator(FlatMap::iterator current) : current_(current) {}
135
136 FlatMap::iterator current_;
137 };
138
139 constexpr FlatMap(const std::array<value_type, kArraySize>& items)
140 : items_(items) {
141 ConstexprSort(items_.begin(), kArraySize);
142 }
143
144 constexpr FlatMap(std::array<value_type, kArraySize>&& items)
145 : items_(std::move(items)) {
146 ConstexprSort(items_.begin(), kArraySize);
147 }
148
149 // Omits explicit here to support assignment-like syntax, which is common to
150 // initialize a container.
151 template <typename... Items,
152 typename = std::enable_if_t<
153 std::conjunction_v<std::is_same<Items, value_type>...>>>
154 constexpr FlatMap(const Items&... items) : FlatMap(std::array{items...}) {}
155
156 FlatMap(FlatMap&) = delete;
157 FlatMap& operator=(FlatMap&) = delete;
158
159 // Capacity.
160 constexpr size_type size() const { return kArraySize; }
161 constexpr size_type empty() const { return size() == 0; }
162 constexpr size_type max_size() const { return kArraySize; }
163
164 // Lookup.
172 constexpr mapped_type& at(const key_type& key) {
173 PW_ASSERT(end() != begin());
174 iterator it = std::lower_bound(
175 items_.begin(), items_.end(), key, IsItemKeyLessThanGivenKey);
176 const key_type& found_key = it->first;
177 PW_ASSERT(it != items_.end() && found_key == key);
178 mapped_type& found_value = it->second;
179 return found_value;
180 }
181
189 constexpr const mapped_type& at(const key_type& key) const {
190 PW_ASSERT(end() != begin());
191 const_iterator it = std::lower_bound(
192 items_.cbegin(), items_.cend(), key, IsItemKeyLessThanGivenKey);
193 const key_type& found_key = it->first;
194 PW_ASSERT(it != items_.cend() && found_key == key);
195 const mapped_type& found_value = it->second;
196 return found_value;
197 }
198
199 constexpr bool contains(const key_type& key) const {
200 return find(key) != end();
201 }
202
203 constexpr const_iterator find(const key_type& key) const {
204 if (end() == begin()) {
205 return end();
206 }
207
208 const_iterator it = lower_bound(key);
209 if (it == end() || it->first != key) {
210 return end();
211 }
212 return it;
213 }
214
215 constexpr const_iterator lower_bound(const key_type& key) const {
216 return std::lower_bound(begin(), end(), key, IsItemKeyLessThanGivenKey);
217 }
218
219 constexpr const_iterator upper_bound(const key_type& key) const {
220 return std::upper_bound(
221 begin(), end(), key, [](key_type lkey, const value_type& item) {
222 return item.first > lkey;
223 });
224 }
225
226 constexpr std::pair<const_iterator, const_iterator> equal_range(
227 const key_type& key) const {
228 if (end() == begin()) {
229 return std::make_pair(end(), end());
230 }
231
232 return std::make_pair(lower_bound(key), upper_bound(key));
233 }
234
235 // Iterators.
236 constexpr const_iterator begin() const { return cbegin(); }
237 constexpr const_iterator cbegin() const { return items_.cbegin(); }
238 constexpr const_iterator end() const { return cend(); }
239 constexpr const_iterator cend() const { return items_.cend(); }
240
248 return mapped_iterator(items_.begin());
249 }
250
258 return mapped_iterator(items_.end());
259 }
260
261 private:
262 // Simple stable insertion sort function for constexpr support.
263 // std::stable_sort is not constexpr. Should not be a problem with performance
264 // in regards to the sizes that are typically dealt with.
265 static constexpr void ConstexprSort(iterator data, size_type size) {
266 if (size < 2) {
267 return;
268 }
269
270 for (iterator it = data + 1,
271 end = data + static_cast<difference_type>(size);
272 it < end;
273 ++it) {
274 if (it->first < it[-1].first) {
275 // Rotate the value into place.
276 value_type temp = std::move(*it);
277 iterator it2 = it - 1;
278 while (true) {
279 *(it2 + 1) = std::move(*it2);
280 if (it2 == data || !(temp.first < it2[-1].first)) {
281 break;
282 }
283 --it2;
284 }
285 *it2 = std::move(temp);
286 }
287 }
288 }
289
290 static constexpr bool IsItemKeyLessThanGivenKey(const value_type& item,
291 key_type key) {
292 const key_type& item_key = item.first;
293 return item_key < key;
294 }
295
296 std::array<value_type, kArraySize> items_;
297};
298
299template <typename K, typename V, typename... Items>
300FlatMap(const Pair<K, V>& item1, const Items&... items)
301 -> FlatMap<K, V, 1 + sizeof...(items)>;
302
303} // namespace pw::containers
Definition: flat_map.h:64
constexpr mapped_type & at(const key_type &key)
Definition: flat_map.h:172
constexpr mapped_iterator mapped_begin()
Definition: flat_map.h:247
constexpr mapped_iterator mapped_end()
Definition: flat_map.h:257
constexpr const mapped_type & at(const key_type &key) const
Definition: flat_map.h:189
Definition: flat_map.h:31