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