P4C
The P4 Compiler
Loading...
Searching...
No Matches
hvec_map.h
1/*
2Copyright 2023-present Intel
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifndef LIB_HVEC_MAP_H_
18#define LIB_HVEC_MAP_H_
19
20#include <initializer_list>
21#include <tuple>
22#include <vector>
23
24#include "exceptions.h"
25#include "hashvec.h"
26
27namespace P4 {
28
29template <class KEY, class VAL, class HASH = std::hash<KEY>, class PRED = std::equal_to<KEY>,
30 class ALLOC = std::allocator<std::pair<const KEY, VAL>>>
32 HASH hf; // FIXME -- use empty base optimization for these?
33 PRED eql;
34
35 public:
36 typedef KEY key_type;
37 typedef std::pair<const KEY, VAL> value_type;
38 typedef VAL mapped_type;
39 typedef HASH hasher;
40 typedef PRED key_equal;
41 typedef ALLOC allocator_type;
42
43 typedef typename std::vector<value_type>::pointer pointer;
44 typedef typename std::vector<value_type>::const_pointer const_pointer;
45 typedef typename std::vector<value_type>::reference reference;
46 typedef typename std::vector<value_type>::const_reference const_reference;
47
49
50 explicit hvec_map(size_t icap = 0, const hasher &hf = hasher(),
51 const key_equal &eql = key_equal(),
52 const allocator_type &a = allocator_type())
53 : hash_vector_base(true, false, icap), hf(hf), eql(eql), data(a) {
54 data.reserve(icap);
55 }
56 hvec_map(const hvec_map &) = default;
57 hvec_map(hvec_map &&) = default;
58 hvec_map &operator=(const hvec_map &that) {
59 if (this != std::addressof(that)) {
60 clear();
61 hf = that.hf;
62 eql = that.eql;
63
64 data.reserve(that.size());
65 insert(that.begin(), that.end());
66 }
67
68 return *this;
69 }
70 hvec_map &operator=(hvec_map &&) = default;
71 ~hvec_map() = default;
72 template <class ITER>
73 hvec_map(ITER begin, ITER end, const hasher &hf = hasher(), const key_equal &eql = key_equal(),
74 const allocator_type &a = allocator_type())
75 : hash_vector_base(true, false, end - begin), hf(hf), eql(eql), data(a) {
76 data.reserve(end - begin);
77 for (auto it = begin; it != end; ++it) insert(*it);
78 }
79 hvec_map(std::initializer_list<value_type> il, const hasher &hf = hasher(),
80 const key_equal &eql = key_equal(), const allocator_type &a = allocator_type())
81 : hash_vector_base(true, false, il.size()), hf(hf), eql(eql), data(a) {
82 data.reserve(il.size());
83 for (auto &i : il) insert(i);
84 }
85
86 private:
87 template <class HVM, class VT>
88 class _iter {
89 HVM *self;
90 size_t idx;
91
92 friend class hvec_map;
93 _iter(HVM &s, size_t i) : self(&s), idx(i) {}
94
95 public:
96 using value_type = VT;
97 using difference_type = ssize_t;
98 using pointer = typename HVM::pointer;
99 using reference = typename HVM::reference;
100 using iterator_category = std::bidirectional_iterator_tag;
101 _iter(const _iter &) = default;
102 _iter &operator=(const _iter &a) {
103 self = a.self;
104 idx = a.idx;
105 return *this;
106 }
107 value_type &operator*() const { return self->data[idx]; }
108 value_type *operator->() const { return &self->data[idx]; }
109 _iter &operator++() {
110 do {
111 ++idx;
112 } while (self->erased[idx]);
113 return *this;
114 }
115 _iter &operator--() {
116 do {
117 --idx;
118 } while (self->erased[idx]);
119 return *this;
120 }
121 _iter operator++(int) {
122 auto copy = *this;
123 ++*this;
124 return copy;
125 }
126 _iter operator--(int) {
127 auto copy = *this;
128 --*this;
129 return copy;
130 }
131 bool operator==(const _iter &a) const { return self == a.self && idx == a.idx; }
132 bool operator!=(const _iter &a) const { return self != a.self || idx != a.idx; }
133 operator _iter<const HVM, const VT>() const {
134 return _iter<const HVM, const VT>(*self, idx);
135 }
136 };
137
138 public:
139 typedef _iter<hvec_map, value_type> iterator;
140 typedef _iter<const hvec_map, const value_type> const_iterator;
141 iterator begin() { return iterator(*this, erased.ffz()); }
142 iterator end() { return iterator(*this, data.size()); }
143 const_iterator begin() const { return const_iterator(*this, erased.ffz()); }
144 const_iterator end() const { return const_iterator(*this, data.size()); }
145 const_iterator cbegin() const { return const_iterator(*this, erased.ffz()); }
146 const_iterator cend() const { return const_iterator(*this, data.size()); }
147
148 bool empty() const { return inuse == 0; }
149 size_t size() const { return inuse; }
150 size_t max_size() const { return UINT32_MAX; }
151 bool operator==(const hvec_map &a) const {
152 if (inuse != a.inuse) return false;
153 auto it = begin();
154 for (auto &el : a)
155 if (el != *it++) return false;
156 return true;
157 }
158 bool operator!=(const hvec_map &a) const { return !(*this == a); }
159 void clear() {
160 hash_vector_base::clear();
161 data.clear();
162 }
163
164 iterator find(const KEY &k) {
166 size_t idx = hash_vector_base::find(&k, &cache);
167 return idx ? iterator(*this, idx - 1) : end();
168 }
169 const_iterator find(const KEY &k) const {
171 size_t idx = hash_vector_base::find(&k, &cache);
172 return idx ? const_iterator(*this, idx - 1) : end();
173 }
174 size_t count(const KEY &k) const {
176 size_t idx = hash_vector_base::find(&k, &cache);
177 return idx > 0;
178 }
179
180 VAL &at(const KEY &k) {
182 size_t idx = hash_vector_base::find(&k, &cache);
183 if (!idx || erased[idx - 1]) throw std::out_of_range("hvec_map::at");
184 return data[idx - 1].second;
185 }
186 const VAL &at(const KEY &k) const {
188 size_t idx = hash_vector_base::find(&k, &cache);
189 if (!idx || erased[idx - 1]) throw std::out_of_range("hvec_map::at");
190 return data[idx - 1].second;
191 }
192
193 // FIXME -- how to do this without duplicating the code for lvalue/rvalue?
194 VAL &operator[](const KEY &k) {
195 size_t idx = hv_insert(&k);
196 if (idx >= data.size()) {
197 data.emplace_back(k, VAL());
198 return data.back().second;
199 } else {
200 if (erased[idx]) {
201 erased[idx] = 0;
202 const_cast<KEY &>(data[idx].first) = k;
203 data[idx].second = VAL();
204 }
205 return data[idx].second;
206 }
207 }
208 VAL &operator[](KEY &&k) {
209 size_t idx = hv_insert(&k);
210 if (idx >= data.size()) {
211 data.emplace_back(std::move(k), VAL());
212 return data.back().second;
213 } else {
214 if (erased[idx]) {
215 erased[idx] = 0;
216 const_cast<KEY &>(data[idx].first) = std::move(k);
217 data[idx].second = VAL();
218 }
219 return data[idx].second;
220 }
221 }
222
223 // FIXME -- how to do this without duplicating the code for lvalue/rvalue?
224 template <typename... VV>
225 std::pair<iterator, bool> emplace(const KEY &k, VV &&...v) {
226 bool new_key = false;
227 size_t idx = hv_insert(&k);
228 if (idx >= data.size()) {
229 idx = data.size();
230 data.emplace_back(std::piecewise_construct_t(), std::forward_as_tuple(k),
231 std::forward_as_tuple(std::forward<VV>(v)...));
232 new_key = true;
233 } else {
234 if ((new_key = erased[idx])) {
235 erased[idx] = 0;
236 const_cast<KEY &>(data[idx].first) = k;
237 data[idx].second = VAL(std::forward<VV>(v)...);
238 } else {
239 data[idx].second = VAL(std::forward<VV>(v)...);
240 }
241 }
242 return std::make_pair(iterator(*this, idx), new_key);
243 }
244 template <typename... VV>
245 std::pair<iterator, bool> emplace(KEY &&k, VV &&...v) {
246 bool new_key = false;
247 size_t idx = hv_insert(&k);
248 if (idx >= data.size()) {
249 idx = data.size();
250 data.emplace_back(std::piecewise_construct_t(), std::forward_as_tuple(std::move(k)),
251 std::forward_as_tuple(std::forward<VV>(v)...));
252 new_key = true;
253 } else if ((new_key = erased[idx])) {
254 erased[idx] = 0;
255 const_cast<KEY &>(data[idx].first) = std::move(k);
256 data[idx].second = VAL(std::forward<VV>(v)...);
257 }
258 return std::make_pair(iterator(*this, idx), new_key);
259 }
260 std::pair<iterator, bool> insert(const value_type &v) {
261 bool new_key = false;
262 size_t idx = hv_insert(&v.first);
263 if (idx >= data.size()) {
264 idx = data.size();
265 data.push_back(v);
266 new_key = true;
267 } else if ((new_key = erased[idx])) {
268 erased[idx] = 0;
269 const_cast<KEY &>(data[idx].first) = v.first;
270 data[idx].second = v.second;
271 }
272 return std::make_pair(iterator(*this, idx), new_key);
273 }
274 template <typename InputIterator>
275 void insert(InputIterator first, InputIterator last) {
276 for (; first != last; ++first) insert(*first);
277 }
278 void insert(std::initializer_list<value_type> vl) { return insert(vl.begin(), vl.end()); }
279 template <class HVM, class VT>
280 _iter<HVM, VT> erase(_iter<HVM, VT> it) {
281 BUG_CHECK(this == it.self, "incorrect iterator for hvec_map::erase");
282 erased[it.idx] = 1;
283 // FIXME -- would be better to call dtor here, but that will cause
284 // problems with the vector when it resized or is destroyed. Could
285 // use raw memory and manual construct instead.
286 const_cast<KEY &>(data[it.idx].first) = KEY();
287 data[it.idx].second = VAL();
288 ++it;
289 --inuse;
290 return it;
291 }
292 size_t erase(const KEY &k) {
293 size_t idx = remove(&k);
294 if (idx + 1 == 0) return 0;
295 if (idx < data.size()) {
296 const_cast<KEY &>(data[idx].first) = KEY();
297 data[idx].second = VAL();
298 }
299 return 1;
300 }
301#ifdef DEBUG
302 using hash_vector_base::dump;
303#endif
304
305 private:
306 std::vector<value_type, ALLOC> data;
307 size_t hashfn(const void *a) const override { return hf(*static_cast<const KEY *>(a)); }
308 bool cmpfn(const void *a, const void *b) const override {
309 return eql(*static_cast<const KEY *>(a), *static_cast<const KEY *>(b));
310 }
311 bool cmpfn(const void *a, size_t b) const override {
312 return eql(*static_cast<const KEY *>(a), data[b].first);
313 }
314 const void *getkey(uint32_t i) const override { return &data[i].first; }
315 void *getval(uint32_t i) override { return &data[i].second; }
316 uint32_t limit() override { return data.size(); }
317 void resizedata(size_t sz) override { data.resize(sz); }
318 void moveentry(size_t to, size_t from) override {
319 // can't just assign, as the keys are const -- need to destruct & construct
320 data[to].~value_type();
321 new (&data[to]) value_type(std::move(data[from]));
322 }
323};
324
325template <class K, class T, class V, class Comp, class Alloc>
326inline V get(const hvec_map<K, V, Comp, Alloc> &m, T key, V def = V()) {
327 auto it = m.find(key);
328 if (it != m.end()) return it->second;
329 return def;
330}
331
332template <class K, class T, class V, class Comp, class Alloc>
333inline V *getref(hvec_map<K, V, Comp, Alloc> &m, T key) {
334 auto it = m.find(key);
335 if (it != m.end()) return &it->second;
336 return 0;
337}
338
339template <class K, class T, class V, class Comp, class Alloc>
340inline const V *getref(const hvec_map<K, V, Comp, Alloc> &m, T key) {
341 auto it = m.find(key);
342 if (it != m.end()) return &it->second;
343 return 0;
344}
345
346template <class K, class T, class V, class Comp, class Alloc>
347inline V get(const hvec_map<K, V, Comp, Alloc> *m, T key, V def = V()) {
348 return m ? get(*m, key, def) : def;
349}
350
351template <class K, class T, class V, class Comp, class Alloc>
352inline V *getref(hvec_map<K, V, Comp, Alloc> *m, T key) {
353 return m ? getref(*m, key) : 0;
354}
355
356template <class K, class T, class V, class Comp, class Alloc>
357inline const V *getref(const hvec_map<K, V, Comp, Alloc> *m, T key) {
358 return m ? getref(*m, key) : 0;
359}
360
361} // namespace P4
362
363#endif /* LIB_HVEC_MAP_H_ */
Definition hashvec.h:29
Definition hvec_map.h:31
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
Definition hashvec.h:51