P4C
The P4 Compiler
Loading...
Searching...
No Matches
flat_map.h
1/*
2Licensed under the Apache License, Version 2.0 (the "License");
3you may not use this file except in compliance with the License.
4You may obtain a copy of the License at
5
6 http://www.apache.org/licenses/LICENSE-2.0
7
8Unless required by applicable law or agreed to in writing, software
9distributed under the License is distributed on an "AS IS" BASIS,
10WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11See the License for the specific language governing permissions and
12limitations under the License.
13*/
14#ifndef LIB_FLAT_MAP_H_
15#define LIB_FLAT_MAP_H_
16
17#include <algorithm>
18#include <functional>
19#include <stdexcept>
20#include <vector>
21
22namespace P4 {
23
27template <typename K, typename V, typename Compare = std::less<>,
28 typename Container = std::vector<std::pair<K, V>>>
29struct flat_map {
30 using key_type = K;
31 using mapped_type = V;
32 using value_type = typename Container::value_type;
33 using key_compare = Compare;
34
36 bool operator()(const value_type &lhs, const value_type &rhs) const {
37 return key_compare()(lhs.first, rhs.first);
38 }
39 };
40
41 using allocator_type = typename Container::allocator_type;
42 using reference = typename Container::reference;
43 using const_reference = typename Container::const_reference;
44 using pointer = typename Container::pointer;
45 using const_pointer = typename Container::const_pointer;
46 using iterator = typename Container::iterator;
47 using const_iterator = typename Container::const_iterator;
48 using reverse_iterator = typename Container::reverse_iterator;
49 using const_reverse_iterator = typename Container::const_reverse_iterator;
50 using difference_type = typename Container::difference_type;
51 using size_type = typename Container::size_type;
52
53 flat_map() = default;
54
55 template <typename It>
56 flat_map(It begin, It end) {
57 insert(begin, end);
58 }
59
60 flat_map(std::initializer_list<value_type> il) : flat_map(il.begin(), il.end()) {}
61
62 iterator begin() { return data_.begin(); }
63 iterator end() { return data_.end(); }
64 const_iterator begin() const { return data_.begin(); }
65 const_iterator end() const { return data_.end(); }
66 const_iterator cbegin() const { return data_.cbegin(); }
67 const_iterator cend() const { return data_.cend(); }
68 reverse_iterator rbegin() { return data_.rbegin(); }
69 reverse_iterator rend() { return data_.rend(); }
70 const_reverse_iterator rbegin() const { return data_.rbegin(); }
71 const_reverse_iterator rend() const { return data_.rend(); }
72 const_reverse_iterator crbegin() const { return data_.crbegin(); }
73 const_reverse_iterator crend() const { return data_.crend(); }
74
75 bool empty() const { return data_.empty(); }
76 size_type size() const { return data_.size(); }
77 size_type max_size() const { return data_.max_size(); }
78 size_type capacity() const { return data_.capacity(); }
79 void reserve(size_type size) { data_.reserve(size); }
80 void shrink_to_fit() { data_.shrink_to_fit(); }
81 size_type bytes_used() const { return capacity() * sizeof(value_type) + sizeof(data_); }
82
83 mapped_type &operator[](const key_type &key) {
84 KeyOrValueCompare comp;
85 auto lower = lower_bound(key);
86 if (lower == end() || comp(key, *lower))
87 return data_.emplace(lower, key, mapped_type())->second;
88
89 return lower->second;
90 }
91
92 mapped_type &operator[](key_type &&key) {
93 KeyOrValueCompare comp;
94 auto lower = lower_bound(key);
95 if (lower == end() || comp(key, *lower))
96 return data_.emplace(lower, std::move(key), mapped_type())->second;
97
98 return lower->second;
99 }
100
101 template <class Key>
102 mapped_type &at(const Key &key) {
103 auto found = lower_bound(key);
104 if (found == end()) throw std::out_of_range("key is out of range");
105 return found->second;
106 }
107 template <class Key>
108 const mapped_type &at(const Key &key) const {
109 auto found = lower_bound(key);
110 if (found == end()) throw std::out_of_range("key is out of range");
111 return found->second;
112 }
113
114 std::pair<iterator, bool> insert(value_type &&value) { return emplace(std::move(value)); }
115
116 std::pair<iterator, bool> insert(const value_type &value) { return emplace(value); }
117
118 iterator insert(const_iterator hint, value_type &&value) {
119 return emplace_hint(hint, std::move(value));
120 }
121
122 iterator insert(const_iterator hint, const value_type &value) {
123 return emplace_hint(hint, value);
124 }
125
126 template <typename It>
127 void insert(It begin, It end) {
128 // If we need to increase the capacity, utilize this fact and emplace
129 // the stuff.
130 for (; begin != end && size() == capacity(); ++begin) {
131 emplace(*begin);
132 }
133 if (begin == end) return;
134
135 // If we don't need to increase capacity, then we can use a more efficient
136 // insert method where everything is just put in the same vector
137 // and then merge in place.
138 size_type size_before = data_.size();
139 try {
140 for (size_t i = capacity(); i > size_before && begin != end; --i, ++begin) {
141 data_.emplace_back(*begin);
142 }
143 } catch (...) {
144 // If emplace_back throws an exception, the easiest way to make sure
145 // that our invariants are still in place is to resize to the state
146 // we were in before
147 for (size_t i = data_.size(); i > size_before; --i) {
148 data_.pop_back();
149 }
150 throw;
151 }
152
153 value_compare comp;
154 auto mid = data_.begin() + size_before;
155 std::stable_sort(mid, data_.end(), comp);
156 std::inplace_merge(data_.begin(), mid, data_.end(), comp);
157 data_.erase(std::unique(data_.begin(), data_.end(), std::not_fn(comp)), data_.end());
158
159 // Make sure that we inserted at least one element before
160 // recursing. Otherwise we'd recurse too often if we were to insert the
161 // same element many times
162 if (data_.size() == size_before) {
163 for (; begin != end; ++begin) {
164 if (emplace(*begin).second) {
165 ++begin;
166 break;
167 }
168 }
169 }
170
171 // Insert the remaining elements that didn't fit by calling this function recursively.
172 return insert(begin, end);
173 }
174
175 void insert(std::initializer_list<value_type> il) { insert(il.begin(), il.end()); }
176
177 iterator erase(iterator it) { return data_.erase(it); }
178
179 iterator erase(const_iterator it) { return erase(iterator_const_cast(it)); }
180
181 size_type erase(const key_type &key) {
182 auto found = find(key);
183 if (found == end()) return 0;
184 erase(found);
185 return 1;
186 }
187
188 iterator erase(const_iterator first, const_iterator last) {
189 return data_.erase(iterator_const_cast(first), iterator_const_cast(last));
190 }
191
192 void swap(flat_map &other) { data_.swap(other.data_); }
193
194 void clear() { data_.clear(); }
195
196 template <typename First, typename... Args>
197 std::pair<iterator, bool> emplace(First &&first, Args &&...args) {
198 KeyOrValueCompare comp;
199 auto lower_bound = std::lower_bound(data_.begin(), data_.end(), first, comp);
200 if (lower_bound == data_.end() || comp(first, *lower_bound))
201 return {
202 data_.emplace(lower_bound, std::forward<First>(first), std::forward<Args>(args)...),
203 true};
204
205 return {lower_bound, false};
206 }
207
208 std::pair<iterator, bool> emplace() { return emplace(value_type()); }
209
210 template <typename First, typename... Args>
211 iterator emplace_hint(const_iterator hint, First &&first, Args &&...args) {
212 KeyOrValueCompare comp;
213 if (hint == cend() || comp(first, *hint)) {
214 if (hint == cbegin() || comp(*(hint - 1), first))
215 return data_.emplace(iterator_const_cast(hint), std::forward<First>(first),
216 std::forward<Args>(args)...);
217
218 return emplace(std::forward<First>(first), std::forward<Args>(args)...).first;
219 } else if (!comp(*hint, first)) {
220 return begin() + (hint - cbegin());
221 }
222
223 return emplace(std::forward<First>(first), std::forward<Args>(args)...).first;
224 }
225
226 iterator emplace_hint(const_iterator hint) { return emplace_hint(hint, value_type()); }
227
228 key_compare key_comp() const { return key_compare(); }
229 value_compare value_comp() const { return value_compare(); }
230
231 template <typename T>
232 iterator find(const T &key) {
233 return binary_find(begin(), end(), key, KeyOrValueCompare());
234 }
235 template <typename T>
236 const_iterator find(const T &key) const {
237 return binary_find(begin(), end(), key, KeyOrValueCompare());
238 }
239 template <typename T>
240 size_type count(const T &key) const {
241 return std::binary_search(begin(), end(), key, KeyOrValueCompare()) ? 1 : 0;
242 }
243 template <typename T>
244 iterator lower_bound(const T &key) {
245 return std::lower_bound(begin(), end(), key, KeyOrValueCompare());
246 }
247 template <typename T>
248 const_iterator lower_bound(const T &key) const {
249 return std::lower_bound(begin(), end(), key, KeyOrValueCompare());
250 }
251 template <typename T>
252 iterator upper_bound(const T &key) {
253 return std::upper_bound(begin(), end(), key, KeyOrValueCompare());
254 }
255 template <typename T>
256 const_iterator upper_bound(const T &key) const {
257 return std::upper_bound(begin(), end(), key, KeyOrValueCompare());
258 }
259 template <typename T>
260 std::pair<iterator, iterator> equal_range(const T &key) {
261 return std::equal_range(begin(), end(), key, KeyOrValueCompare());
262 }
263 template <typename T>
264 std::pair<const_iterator, const_iterator> equal_range(const T &key) const {
265 return std::equal_range(begin(), end(), key, KeyOrValueCompare());
266 }
267 allocator_type get_allocator() const { return data_.get_allocator(); }
268
269 bool operator==(const flat_map &other) const { return data_ == other.data_; }
270 bool operator!=(const flat_map &other) const { return !(*this == other); }
271 bool operator<(const flat_map &other) const { return data_ < other.data_; }
272 bool operator>(const flat_map &other) const { return other < *this; }
273 bool operator<=(const flat_map &other) const { return !(other < *this); }
274 bool operator>=(const flat_map &other) const { return !(*this < other); }
275
276 private:
277 Container data_;
278
279 iterator iterator_const_cast(const_iterator it) { return begin() + (it - cbegin()); }
280
281 struct KeyOrValueCompare {
282 template <typename T, typename U>
283 bool operator()(const T &lhs, const U &rhs) const {
284 return key_compare()(extract(lhs), extract(rhs));
285 }
286
287 private:
288 const key_type &extract(const value_type &v) const { return v.first; }
289
290 template <typename Key>
291 const Key &extract(const Key &k) const {
292 return k;
293 }
294 };
295
296 template <typename It, typename T, typename Comp>
297 static It binary_find(It begin, It end, const T &value, const Comp &cmp) {
298 auto lower_bound = std::lower_bound(begin, end, value, cmp);
299 if (lower_bound == end || cmp(value, *lower_bound)) return end;
300
301 return lower_bound;
302 }
303};
304
305template <typename K, typename V, typename C, typename Cont>
306void swap(flat_map<K, V, C, Cont> &lhs, flat_map<K, V, C, Cont> &rhs) {
307 lhs.swap(rhs);
308}
309
310} // namespace P4
311
312#endif // LIB_FLAT_MAP_H_
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
Definition flat_map.h:35
Definition flat_map.h:29