Actual source code: unordered_map.hpp
1: #pragma once
3: #include <petsc/private/cpp/type_traits.hpp>
4: #include <petsc/private/cpp/utility.hpp>
5: #include <petsc/private/cpp/functional.hpp>
7: #if PETSC_CPP_VERSION >= 17
8: #include <optional>
9: #define PETSC_OPTIONAL_GET_KEY(...) *(__VA_ARGS__)
10: #define PETSC_KHASH_MAP_USE_OPTIONAL 1
11: #else
12: #define PETSC_OPTIONAL_GET_KEY(...) __VA_ARGS__
13: #define PETSC_KHASH_MAP_USE_OPTIONAL 0
14: #endif
16: #include <cstdint> // std::uint32_t
17: #include <climits> // CHAR_BIT
18: #include <iterator> // std::inserter
19: #include <limits> // std::numeric_limits
20: #include <algorithm> // std::fill
21: #include <vector>
23: namespace Petsc
24: {
26: namespace khash
27: {
29: // ==========================================================================================
30: // KHashTable - The hash table implementation which underpins UnorderedMap (and possibly
31: // UnorderedSet in the future).
32: //
33: // This class serves to implement the majority of functionality for both classes. In fact, it
34: // is possible to use -- without modification -- as a khash_unordered_set already.
35: //
36: // Template parameters are as follows:
37: //
38: // Value:
39: // The value type of the hash table, i.e. the set of unique items it stores
40: //
41: // Hash:
42: // The hasher type, provides a std::size_t operator()(const Value&) to produce a hash of Value
43: //
44: // Eq:
45: // The comparison type, provides a bool operator()(const Value&, const Value&) to compare two
46: // different Value's
47: // ==========================================================================================
49: template <typename Value, typename Hash, typename Eq>
50: class KHashTable : util::compressed_pair<Hash, Eq> {
51: // Note we derive from compressed_pair<Hash, Eq>! This is to enable us to efficiently
52: // implement hash_function() and key_eq() since -- if Hash and Eq are empty -- we do not have
53: // to pay to store them due to empty base-class optimization.
54: template <bool>
55: class table_iterator;
57: template <bool>
58: friend class table_iterator;
60: public:
61: using value_type = Value;
62: using hasher = Hash;
63: using key_equal = Eq;
64: using size_type = std::size_t;
65: using reference = value_type &;
66: using const_reference = const value_type &;
67: using difference_type = std::ptrdiff_t;
68: using iterator = table_iterator</* is const = */ false>;
69: using const_iterator = table_iterator</* is const = */ true>;
70: using khash_int = std::uint32_t; // used as the internal iterator type
71: using flags_type = std::uint32_t;
73: using flag_bucket_width = std::integral_constant<unsigned long, sizeof(flags_type) * CHAR_BIT>;
74: using flag_pairs_per_bucket = std::integral_constant<unsigned long, flag_bucket_width::value / 2>;
76: static_assert(std::numeric_limits<flags_type>::is_integer && std::is_unsigned<flags_type>::value, "");
77: static_assert(flag_bucket_width::value % 2 == 0, "");
79: KHashTable() = default;
80: ~KHashTable() = default;
82: KHashTable(const KHashTable &) = default;
83: KHashTable &operator=(const KHashTable &) = default;
85: KHashTable(KHashTable &&) noexcept;
86: KHashTable &operator=(KHashTable &&) noexcept;
88: template <typename Iter>
89: KHashTable(Iter, Iter) noexcept;
91: PETSC_NODISCARD iterator begin() noexcept;
92: PETSC_NODISCARD const_iterator cbegin() const noexcept;
93: PETSC_NODISCARD const_iterator begin() const noexcept;
95: PETSC_NODISCARD iterator end() noexcept;
96: PETSC_NODISCARD const_iterator cend() const noexcept;
97: PETSC_NODISCARD const_iterator end() const noexcept;
99: PETSC_NODISCARD size_type bucket_count() const noexcept;
100: PETSC_NODISCARD size_type size() const noexcept;
101: PETSC_NODISCARD size_type capacity() const noexcept;
102: PETSC_NODISCARD bool empty() const noexcept;
104: PetscErrorCode reserve(size_type) noexcept;
105: PetscErrorCode resize(size_type) noexcept;
106: PetscErrorCode clear() noexcept;
108: PETSC_NODISCARD bool occupied(khash_int) const noexcept;
109: PETSC_NODISCARD bool occupied(const_iterator) const noexcept;
111: iterator erase(iterator) noexcept;
112: iterator erase(const_iterator) noexcept;
113: iterator erase(const_iterator, const_iterator) noexcept;
115: template <typename... Args>
116: std::pair<iterator, bool> emplace(Args &&...) noexcept;
118: std::pair<iterator, bool> insert(const value_type &) noexcept;
119: std::pair<iterator, bool> insert(value_type &&) noexcept;
120: iterator insert(const_iterator, const value_type &) noexcept;
121: iterator insert(const_iterator, value_type &&) noexcept;
123: hasher hash_function() const noexcept;
124: key_equal key_eq() const noexcept;
126: PetscErrorCode shrink_to_fit() noexcept;
128: void swap(KHashTable &) noexcept;
130: protected:
131: PETSC_NODISCARD iterator make_iterator_(khash_int) noexcept;
132: PETSC_NODISCARD const_iterator make_iterator_(khash_int) const noexcept;
134: template <typename T>
135: PetscErrorCode khash_find_(T &&, khash_int *) const noexcept;
137: // emplacement for the hash map, where key and value are constructed separately
138: template <typename KeyType, typename... ValueTypeArgs>
139: PETSC_NODISCARD std::pair<iterator, bool> find_and_emplace_(KeyType &&, ValueTypeArgs &&...) noexcept;
140: template <typename KeyValueType>
141: PETSC_NODISCARD std::pair<iterator, bool> find_and_emplace_(KeyValueType &&) noexcept;
143: private:
144: template <typename Iter>
145: KHashTable(Iter, Iter, std::input_iterator_tag) noexcept;
146: template <typename Iter>
147: KHashTable(Iter, Iter, std::random_access_iterator_tag) noexcept;
149: // Every element in the table has a pair of 2 flags that describe its current state. These
150: // are addressed via the *index* into values_ for the element. These flags are:
151: //
152: // 1. Empty: has the element *ever* been constructed? Note empty of yes implies deleted no.
153: // 2. Deleted: has the element been constructed and marked as deleted?
154: //
155: // Since these flags are combineable we can store them in compressed form in a bit-table,
156: // where each pair of consecutive 2*i and 2*i+1 bits denote the flags for element i.
157: //
158: // Thus if we use a vector of std::bitset's (which are each N-bits wide) we can effectively
159: // store N / 2 flags per index, for example:
160: //
161: // std::vector<std::bitset<32>> flags;
162: //
163: // int flags_per_idx = 32 / 2; (16 pairs of flags)
164: // int value_index = 24;
165: // int flags_index = value_index / flags_per_idx; (index into the right bucket in flags)
166: // int flags_bucket_index = (value_index % flags_per_idx) << 1; (within that bucket grab the
167: // right entry)
168: //
169: // or visually speaking:
170: //
171: // flags = [
172: // [00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00],
173: // [00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00], <--- flags_index (1)
174: // ... ^--- flags_bucket_index (16)
175: // ]
176: //
177: // Thus to access a particular flag pair, one must right-shift flags[flags_index] by
178: // flags_bucket_index. Then the desired flag pair will be the first and second bits of the
179: // result.
180: PETSC_NODISCARD static constexpr khash_int flag_bucket_index_(khash_int) noexcept;
182: PETSC_NODISCARD static flags_type &flag_bucket_at_(khash_int, std::vector<flags_type> &) noexcept;
183: PETSC_NODISCARD static const flags_type &flag_bucket_at_(khash_int, const std::vector<flags_type> &) noexcept;
184: PETSC_NODISCARD flags_type &flag_bucket_at_(khash_int) noexcept;
185: PETSC_NODISCARD const flags_type &flag_bucket_at_(khash_int) const noexcept;
187: template <unsigned>
188: PETSC_NODISCARD static bool khash_test_flag_(khash_int, const std::vector<flags_type> &) noexcept;
189: PETSC_NODISCARD static bool khash_is_del_(khash_int, const std::vector<flags_type> &) noexcept;
190: PETSC_NODISCARD static bool khash_is_empty_(khash_int, const std::vector<flags_type> &) noexcept;
191: PETSC_NODISCARD static bool khash_is_either_(khash_int, const std::vector<flags_type> &) noexcept;
192: PETSC_NODISCARD static bool khash_occupied_(khash_int, const std::vector<flags_type> &) noexcept;
193: PETSC_NODISCARD bool khash_is_del_(khash_int) const noexcept;
194: PETSC_NODISCARD bool khash_is_empty_(khash_int) const noexcept;
195: PETSC_NODISCARD bool khash_is_either_(khash_int) const noexcept;
197: template <unsigned, bool>
198: static PetscErrorCode khash_set_flag_(khash_int, std::vector<flags_type> &) noexcept;
199: template <bool>
200: static PetscErrorCode khash_set_deleted_(khash_int, std::vector<flags_type> &) noexcept;
201: template <bool>
202: static PetscErrorCode khash_set_empty_(khash_int, std::vector<flags_type> &) noexcept;
203: template <bool>
204: static PetscErrorCode khash_set_both_(khash_int, std::vector<flags_type> &) noexcept;
205: template <bool>
206: PetscErrorCode khash_set_deleted_(khash_int) noexcept;
207: template <bool>
208: PetscErrorCode khash_set_empty_(khash_int) noexcept;
209: template <bool>
210: PetscErrorCode khash_set_both_(khash_int) noexcept;
212: // produce the default bit pattern:
213: //
214: // v--- deleted: no
215: // 0b101010 ... 10
216: // ^---- empty: yes
217: template <std::size_t mask_width>
218: static PETSC_CONSTEXPR_14 flags_type default_bit_pattern_impl_() noexcept
219: {
220: flags_type x{};
222: for (std::size_t i = 0; i < mask_width; ++i) {
223: if (i % 2) {
224: // odd,
225: x |= 1ULL << i;
226: } else {
227: // even
228: x &= ~(1UL << i);
229: }
230: }
231: return x;
232: }
234: public:
235: PETSC_NODISCARD static PETSC_CONSTEXPR_14 flags_type default_bit_pattern() noexcept
236: {
237: // forces constexpr evaluation, which may not be guaranteed. Note that after GCC 6.1+
238: // tries to constexpr-evaluate _any_ function marked constexpr and will inline evaluate
239: // default_bit_mask_impl_() at any optimization level > 0.
240: //
241: // clang constexpr evaluates this at 3.7 but is inconsistent between versions at which
242: // optimization level the call is fully unraveled.
243: PETSC_CONSTEXPR_14 auto ret = default_bit_pattern_impl_<flag_bucket_width::value>();
244: return ret;
245: }
247: private:
248: template <typename KeyType, typename ValueConstructor>
249: PETSC_NODISCARD std::pair<iterator, bool> find_and_emplace_final_(KeyType &&, ValueConstructor &&) noexcept;
251: PetscErrorCode khash_maybe_rehash_() noexcept;
252: PetscErrorCode khash_erase_(khash_int) noexcept;
254: #if PETSC_KHASH_MAP_USE_OPTIONAL
255: using internal_value_type = std::optional<value_type>;
256: #else
257: using internal_value_type = value_type;
258: #endif
260: std::vector<internal_value_type> values_{};
261: std::vector<flags_type> flags_{};
262: size_type count_ = 0;
263: size_type n_occupied_ = 0;
264: size_type upper_bound_ = 0;
265: };
267: // ==========================================================================================
268: // KHashTable::table_iterator
269: // ==========================================================================================
271: template <typename V, typename H, typename KE>
272: template <bool is_const_it>
273: class KHashTable<V, H, KE>::table_iterator {
274: template <typename U>
275: using conditional_const = util::conditional_t<is_const_it, util::add_const_t<U>, U>;
277: template <bool>
278: friend class table_iterator;
280: friend class KHashTable;
282: public:
283: // internal typedef
284: using table_type = conditional_const<KHashTable>;
285: using khash_int = typename table_type::khash_int;
287: // iterator-related typedefs
288: using iterator_category = std::bidirectional_iterator_tag;
289: using difference_type = typename table_type::difference_type;
290: using value_type = conditional_const<typename table_type::value_type>;
291: using reference = value_type &;
292: using pointer = value_type *;
294: table_iterator() noexcept = default;
295: table_iterator(table_type *map, khash_int it) noexcept : map_{std::move(map)}, it_{std::move(it)} { }
297: table_iterator(const table_iterator &) noexcept = default;
298: table_iterator &operator=(const table_iterator &) noexcept = default;
300: table_iterator(table_iterator &&) noexcept = default;
301: table_iterator &operator=(table_iterator &&) noexcept = default;
303: template <bool other_is_const_it, util::enable_if_t<is_const_it && !other_is_const_it> * = nullptr>
304: table_iterator(const table_iterator<other_is_const_it> &other) noexcept : table_iterator{other.map_, other.it_}
305: {
306: }
308: template <bool other_is_const_it, util::enable_if_t<is_const_it && !other_is_const_it> * = nullptr>
309: table_iterator &operator=(const table_iterator<other_is_const_it> &other) noexcept
310: {
311: // self assignment is OK here
312: PetscFunctionBegin;
313: map_ = other.map_;
314: it_ = other.it_;
315: PetscFunctionReturn(*this);
316: }
318: // prefix
319: table_iterator &operator--() noexcept
320: {
321: constexpr khash_int map_begin = 0;
323: PetscFunctionBegin;
324: // Use of map_begin + 1 instead of map_begin (like in operator++()) is deliberate. We do
325: // not want it_ == map_begin here since that would mean that the while-loop decrements it
326: // out of bounds!
327: // Likewise we are allowed to be 1 past the bucket size, otherwise backwards iteration
328: // would not work!
329: PetscCallAbort(PETSC_COMM_SELF, check_iterator_inbounds_(1, 1));
330: do {
331: --it_;
332: } while ((it_ > map_begin) && !map_->occupied(it_));
333: PetscFunctionReturn(*this);
334: }
336: // postfix
337: table_iterator operator--(int) noexcept
338: {
339: table_iterator old{*this};
341: PetscFunctionBegin;
342: --(*this);
343: PetscFunctionReturn(old);
344: }
346: // prefix
347: table_iterator &operator++() noexcept
348: {
349: khash_int map_end = 0;
351: PetscFunctionBegin;
352: PetscCallAbort(PETSC_COMM_SELF, check_iterator_inbounds_());
353: map_end = map_->bucket_count();
354: do {
355: ++it_;
356: } while (it_ != map_end && !map_->occupied(it_));
357: PetscFunctionReturn(*this);
358: }
360: // postfix
361: table_iterator operator++(int) noexcept
362: {
363: table_iterator old{*this};
365: PetscFunctionBegin;
366: ++(*this);
367: PetscFunctionReturn(old);
368: }
370: PETSC_NODISCARD reference operator*() const noexcept
371: {
372: PetscFunctionBegin;
373: PetscCallAbort(PETSC_COMM_SELF, check_iterator_inbounds_());
374: PetscFunctionReturn(PETSC_OPTIONAL_GET_KEY(map_->values_[it_]));
375: }
377: PETSC_NODISCARD pointer operator->() const noexcept
378: {
379: PetscFunctionBegin;
380: PetscCallAbort(PETSC_COMM_SELF, check_iterator_inbounds_());
381: PetscFunctionReturn(std::addressof(PETSC_OPTIONAL_GET_KEY(map_->values_[it_])));
382: }
384: template <bool rc>
385: PETSC_NODISCARD bool operator==(const table_iterator<rc> &r) const noexcept
386: {
387: return std::tie(map_, it_) == std::tie(r.map_, r.it_);
388: }
390: template <bool rc>
391: PETSC_NODISCARD bool operator!=(const table_iterator<rc> &r) const noexcept
392: {
393: return !(*this == r);
394: }
396: private:
397: table_type *map_ = nullptr;
398: khash_int it_ = 0;
400: PetscErrorCode check_iterator_inbounds_(int map_begin_offset = 0, int map_end_offset = 0) const noexcept
401: {
402: PetscFunctionBegin;
403: if (PetscDefined(USE_DEBUG)) {
404: std::int64_t map_begin = map_begin_offset;
405: std::int64_t map_end = map_end_offset;
407: PetscCheck(map_, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Iterator has a NULL map pointer");
408: map_end += map_->bucket_count();
409: PetscCheck((it_ >= map_begin) && (it_ < map_end), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Iterator index value %" PRId32 " is out of range for map (%p): [%" PRId64 ", %" PRId64 ")", it_, (void *)map_, map_begin, map_end);
410: } else {
411: static_cast<void>(map_begin_offset);
412: static_cast<void>(map_end_offset);
413: }
414: PetscFunctionReturn(PETSC_SUCCESS);
415: }
416: };
418: // ==========================================================================================
419: // KHashTable - Private API
420: // ==========================================================================================
422: // Generic iterator constructor
423: template <typename V, typename H, typename KE>
424: template <typename Iter>
425: inline KHashTable<V, H, KE>::KHashTable(Iter first, Iter last, std::input_iterator_tag) noexcept
426: {
427: PetscFunctionBegin;
428: PetscCallCXXAbort(PETSC_COMM_SELF, std::copy(std::move(first), std::move(last), std::inserter(*this, begin())));
429: PetscFunctionReturnVoid();
430: }
432: // An optimization for random_access_iterators. Since these mandate that std::distance() is
433: // equivalent to end-begin, we can use this to pre-allocate the hashmap for free before we
434: // insert
435: template <typename V, typename H, typename KE>
436: template <typename Iter>
437: inline KHashTable<V, H, KE>::KHashTable(Iter first, Iter last, std::random_access_iterator_tag) noexcept
438: {
439: PetscFunctionBegin;
440: PetscCallAbort(PETSC_COMM_SELF, reserve(static_cast<size_type>(std::distance(first, last))));
441: PetscCallCXXAbort(PETSC_COMM_SELF, std::copy(std::move(first), std::move(last), std::inserter(*this, begin())));
442: PetscFunctionReturnVoid();
443: }
445: // ------------------------------------------------------------------------------------------
446: // KHashTable - Private API - flag bucket API - accessors
447: // ------------------------------------------------------------------------------------------
449: template <typename V, typename H, typename KE>
450: inline constexpr typename KHashTable<V, H, KE>::khash_int KHashTable<V, H, KE>::flag_bucket_index_(khash_int it) noexcept
451: {
452: return (it % flag_pairs_per_bucket::value) << 1;
453: }
455: template <typename V, typename H, typename KE>
456: inline typename KHashTable<V, H, KE>::flags_type &KHashTable<V, H, KE>::flag_bucket_at_(khash_int it, std::vector<flags_type> &flags) noexcept
457: {
458: return flags[it / flag_pairs_per_bucket::value];
459: }
461: template <typename V, typename H, typename KE>
462: inline const typename KHashTable<V, H, KE>::flags_type &KHashTable<V, H, KE>::flag_bucket_at_(khash_int it, const std::vector<flags_type> &flags) noexcept
463: {
464: return flags[it / flag_pairs_per_bucket::value];
465: }
467: template <typename V, typename H, typename KE>
468: inline typename KHashTable<V, H, KE>::flags_type &KHashTable<V, H, KE>::flag_bucket_at_(khash_int it) noexcept
469: {
470: return flag_bucket_at_(it, flags_);
471: }
473: template <typename V, typename H, typename KE>
474: inline const typename KHashTable<V, H, KE>::flags_type &KHashTable<V, H, KE>::flag_bucket_at_(khash_int it) const noexcept
475: {
476: return flag_bucket_at_(it, flags_);
477: }
479: // ------------------------------------------------------------------------------------------
480: // KHashTable - Private API - flag bucket API - query
481: // ------------------------------------------------------------------------------------------
483: template <typename V, typename H, typename KE>
484: template <unsigned selector>
485: inline bool KHashTable<V, H, KE>::khash_test_flag_(khash_int it, const std::vector<flags_type> &flags) noexcept
486: {
487: static_assert(selector > 0 || selector <= 3, "");
488: return (flag_bucket_at_(it, flags) >> flag_bucket_index_(it)) & selector;
489: }
491: template <typename V, typename H, typename KE>
492: inline bool KHashTable<V, H, KE>::khash_is_del_(khash_int it, const std::vector<flags_type> &flags) noexcept
493: {
494: return khash_test_flag_<1>(it, flags);
495: }
497: template <typename V, typename H, typename KE>
498: inline bool KHashTable<V, H, KE>::khash_is_empty_(khash_int it, const std::vector<flags_type> &flags) noexcept
499: {
500: return khash_test_flag_<2>(it, flags);
501: }
503: template <typename V, typename H, typename KE>
504: inline bool KHashTable<V, H, KE>::khash_is_either_(khash_int it, const std::vector<flags_type> &flags) noexcept
505: {
506: return khash_test_flag_<3>(it, flags);
507: }
509: template <typename V, typename H, typename KE>
510: inline bool KHashTable<V, H, KE>::khash_occupied_(khash_int it, const std::vector<flags_type> &flags) noexcept
511: {
512: return !khash_is_either_(it, flags);
513: }
515: template <typename V, typename H, typename KE>
516: inline bool KHashTable<V, H, KE>::khash_is_del_(khash_int it) const noexcept
517: {
518: return khash_is_del_(it, flags_);
519: }
521: template <typename V, typename H, typename KE>
522: inline bool KHashTable<V, H, KE>::khash_is_empty_(khash_int it) const noexcept
523: {
524: return khash_is_empty_(it, flags_);
525: }
527: template <typename V, typename H, typename KE>
528: inline bool KHashTable<V, H, KE>::khash_is_either_(khash_int it) const noexcept
529: {
530: return khash_is_either_(it, flags_);
531: }
533: // ------------------------------------------------------------------------------------------
534: // KHashTable - Private API - flag bucket API - set
535: // ------------------------------------------------------------------------------------------
537: template <typename V, typename H, typename KE>
538: template <unsigned flag_selector, bool set>
539: inline PetscErrorCode KHashTable<V, H, KE>::khash_set_flag_(khash_int it, std::vector<flags_type> &flags) noexcept
540: {
541: static_assert(flag_selector > 0U && flag_selector <= 3U, "");
543: PetscFunctionBegin;
544: if (set) {
545: flag_bucket_at_(it, flags) |= flag_selector << flag_bucket_index_(it);
546: } else {
547: flag_bucket_at_(it, flags) &= ~(flag_selector << flag_bucket_index_(it));
548: }
549: PetscFunctionReturn(PETSC_SUCCESS);
550: }
552: template <typename V, typename H, typename KE>
553: template <bool b>
554: inline PetscErrorCode KHashTable<V, H, KE>::khash_set_deleted_(khash_int it, std::vector<flags_type> &flags) noexcept
555: {
556: PetscFunctionBegin;
557: PetscCall(khash_set_flag_<1, b>(it, flags));
558: PetscFunctionReturn(PETSC_SUCCESS);
559: }
561: template <typename V, typename H, typename KE>
562: template <bool b>
563: inline PetscErrorCode KHashTable<V, H, KE>::khash_set_empty_(khash_int it, std::vector<flags_type> &flags) noexcept
564: {
565: PetscFunctionBegin;
566: PetscCall(khash_set_flag_<2, b>(it, flags));
567: PetscFunctionReturn(PETSC_SUCCESS);
568: }
570: template <typename V, typename H, typename KE>
571: template <bool b>
572: inline PetscErrorCode KHashTable<V, H, KE>::khash_set_both_(khash_int it, std::vector<flags_type> &flags) noexcept
573: {
574: PetscFunctionBegin;
575: PetscCall(khash_set_flag_<3, b>(it, flags));
576: PetscFunctionReturn(PETSC_SUCCESS);
577: }
579: template <typename V, typename H, typename KE>
580: template <bool b>
581: inline PetscErrorCode KHashTable<V, H, KE>::khash_set_deleted_(khash_int it) noexcept
582: {
583: PetscFunctionBegin;
584: PetscCall(khash_set_deleted_<b>(it, flags_));
585: PetscFunctionReturn(PETSC_SUCCESS);
586: }
588: template <typename V, typename H, typename KE>
589: template <bool b>
590: inline PetscErrorCode KHashTable<V, H, KE>::khash_set_empty_(khash_int it) noexcept
591: {
592: PetscFunctionBegin;
593: PetscCall(khash_set_empty_<b>(it, flags_));
594: PetscFunctionReturn(PETSC_SUCCESS);
595: }
597: template <typename V, typename H, typename KE>
598: template <bool b>
599: inline PetscErrorCode KHashTable<V, H, KE>::khash_set_both_(khash_int it) noexcept
600: {
601: PetscFunctionBegin;
602: PetscCall(khash_set_both_<b>(it, flags_));
603: PetscFunctionReturn(PETSC_SUCCESS);
604: }
606: template <typename V, typename H, typename KE>
607: template <typename KeyType, typename ValueConstructor>
608: inline std::pair<typename KHashTable<V, H, KE>::iterator, bool> KHashTable<V, H, KE>::find_and_emplace_final_(KeyType &&key, ValueConstructor &&constructor) noexcept
609: {
610: khash_int it = 0;
612: PetscFunctionBegin;
613: PetscCallAbort(PETSC_COMM_SELF, khash_maybe_rehash_());
614: {
615: const auto nb = bucket_count();
616: const auto mask = nb - 1;
617: const auto hash = hash_function()(key);
618: auto i = hash & mask;
620: PetscAssertAbort(nb > 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Have %zu bucket count after rehash", nb);
621: if (khash_is_empty_(i)) {
622: it = i; // for speed up
623: } else {
624: const auto last = i;
625: auto site = nb;
626: khash_int step = 0;
628: it = nb;
629: while (!khash_is_empty_(i) && (khash_is_del_(i) || !key_eq()(PETSC_OPTIONAL_GET_KEY(values_[i]), key))) {
630: if (khash_is_del_(i)) site = i;
631: ++step;
632: i = (i + step) & mask;
633: if (i == last) {
634: it = site;
635: break;
636: }
637: }
638: if (it == nb) {
639: // didn't find a completely empty place to put it, see if we can reuse an existing
640: // bucket
641: if (khash_is_empty_(i) && (site != nb)) {
642: // reuse a deleted element (I think)
643: it = site;
644: } else {
645: it = i;
646: }
647: }
648: }
649: }
650: if (occupied(it)) PetscFunctionReturn({make_iterator_(it), false});
651: // not present at all or deleted, so create (or replace) the element using the constructor
652: // lambda
653: PetscCallCXXAbort(PETSC_COMM_SELF, values_[it] = constructor());
654: ++count_;
655: if (khash_is_empty_(it)) ++n_occupied_;
656: // order matters, must do this _after_ we check is_empty() since this call sets is_empty to
657: // false!
658: PetscCallAbort(PETSC_COMM_SELF, khash_set_both_<false>(it));
659: PetscFunctionReturn({make_iterator_(it), true});
660: }
662: template <typename V, typename H, typename KE>
663: inline PetscErrorCode KHashTable<V, H, KE>::khash_maybe_rehash_() noexcept
664: {
665: PetscFunctionBegin;
666: if (n_occupied_ >= upper_bound_) {
667: auto target_size = bucket_count();
669: if (target_size > (size() << 1)) {
670: // clear "deleted" elements
671: --target_size;
672: } else {
673: // expand the hash table
674: ++target_size;
675: }
676: PetscCall(resize(target_size));
677: }
678: PetscFunctionReturn(PETSC_SUCCESS);
679: }
681: template <typename V, typename H, typename KE>
682: inline PetscErrorCode KHashTable<V, H, KE>::khash_erase_(khash_int it) noexcept
683: {
684: PetscFunctionBegin;
685: PetscAssert(it < bucket_count(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Attempting to erase iterator (index %d) which did not exist in the map", it);
686: PetscAssert(count_ > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Attempting to erase iterator (index %d) which did not exist in the map, have element count %zu", it, count_);
687: PetscAssert(occupied(it), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Attempting to erase iterator (index %d) which exists in the map but is not occupied", it);
688: --count_;
689: PetscCall(khash_set_deleted_<true>(it));
690: PetscCallCXX(values_[it] = internal_value_type{});
691: PetscFunctionReturn(PETSC_SUCCESS);
692: }
694: // ==========================================================================================
695: // KHashTable - Protected API
696: // ==========================================================================================
698: template <typename V, typename H, typename KE>
699: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::make_iterator_(khash_int it) noexcept
700: {
701: return {this, it};
702: }
704: template <typename V, typename H, typename KE>
705: inline typename KHashTable<V, H, KE>::const_iterator KHashTable<V, H, KE>::make_iterator_(khash_int it) const noexcept
706: {
707: return {this, it};
708: }
710: template <typename V, typename H, typename KE>
711: template <typename T>
712: inline PetscErrorCode KHashTable<V, H, KE>::khash_find_(T &&key, khash_int *it) const noexcept
713: {
714: const auto nb = bucket_count();
715: auto ret = nb;
717: PetscFunctionBegin;
718: if (nb) {
719: const auto mask = nb - 1;
720: const auto hash = hash_function()(key);
721: auto i = hash & mask;
722: const auto last = i;
723: khash_int step = 0;
725: while (!khash_is_empty_(i) && (khash_is_del_(i) || !key_equal()(PETSC_OPTIONAL_GET_KEY(values_[i]), key))) {
726: ++step;
727: i = (i + step) & mask;
728: if (i == last) {
729: *it = ret;
730: PetscFunctionReturn(PETSC_SUCCESS);
731: }
732: }
733: if (occupied(i)) ret = i;
734: }
735: *it = ret;
736: PetscFunctionReturn(PETSC_SUCCESS);
737: }
739: template <typename V, typename H, typename KE>
740: template <typename KeyType, typename... ValueTypeArgs>
741: inline std::pair<typename KHashTable<V, H, KE>::iterator, bool> KHashTable<V, H, KE>::find_and_emplace_(KeyType &&key, ValueTypeArgs &&...value_ctor_args) noexcept
742: {
743: return find_and_emplace_final_(std::forward<KeyType>(key), [&] { return value_type{std::forward<ValueTypeArgs>(value_ctor_args)...}; });
744: }
746: template <typename V, typename H, typename KE>
747: template <typename KeyValueType>
748: inline std::pair<typename KHashTable<V, H, KE>::iterator, bool> KHashTable<V, H, KE>::find_and_emplace_(KeyValueType &&key_value) noexcept
749: {
750: return find_and_emplace_final_(std::forward<KeyValueType>(key_value), [&] { return std::forward<KeyValueType>(key_value); });
751: }
753: // ==========================================================================================
754: // KHashTable - Public API
755: // ==========================================================================================
757: // Generic iterator constructor
758: template <typename V, typename H, typename KE>
759: template <typename Iter>
760: inline KHashTable<V, H, KE>::KHashTable(Iter first, Iter last) noexcept : KHashTable(std::move(first), std::move(last), typename std::iterator_traits<Iter>::iterator_category{})
761: {
762: }
764: template <typename V, typename H, typename KE>
765: inline KHashTable<V, H, KE>::KHashTable(KHashTable &&other) noexcept :
766: values_(std::move(other.values_)), flags_(std::move(other.flags_)), count_(util::exchange(other.count_, 0)), n_occupied_(util::exchange(other.n_occupied_, 0)), upper_bound_(util::exchange(other.upper_bound_, 0))
767: {
768: }
770: template <typename V, typename H, typename KE>
771: inline KHashTable<V, H, KE> &KHashTable<V, H, KE>::operator=(KHashTable &&other) noexcept
772: {
773: PetscFunctionBegin;
774: if (this != &other) {
775: PetscCallCXXAbort(PETSC_COMM_SELF, values_ = std::move(other.values_));
776: PetscCallCXXAbort(PETSC_COMM_SELF, flags_ = std::move(other.flags_));
777: count_ = util::exchange(other.count_, 0);
778: n_occupied_ = util::exchange(other.n_occupied_, 0);
779: upper_bound_ = util::exchange(other.upper_bound_, 0);
780: }
781: PetscFunctionReturn(*this);
782: }
784: template <typename V, typename H, typename KE>
785: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::begin() noexcept
786: {
787: khash_int it = 0;
789: PetscFunctionBegin;
790: for (; it < bucket_count(); ++it) {
791: if (occupied(it)) break;
792: }
793: PetscFunctionReturn(make_iterator_(it));
794: }
796: template <typename V, typename H, typename KE>
797: inline typename KHashTable<V, H, KE>::const_iterator KHashTable<V, H, KE>::cbegin() const noexcept
798: {
799: khash_int it = 0;
801: PetscFunctionBegin;
802: for (; it < bucket_count(); ++it) {
803: if (occupied(it)) break;
804: }
805: PetscFunctionReturn(make_iterator_(it));
806: }
808: template <typename V, typename H, typename KE>
809: inline typename KHashTable<V, H, KE>::const_iterator KHashTable<V, H, KE>::begin() const noexcept
810: {
811: return cbegin();
812: }
814: template <typename V, typename H, typename KE>
815: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::end() noexcept
816: {
817: return make_iterator_(bucket_count());
818: }
820: template <typename V, typename H, typename KE>
821: inline typename KHashTable<V, H, KE>::const_iterator KHashTable<V, H, KE>::cend() const noexcept
822: {
823: return make_iterator_(bucket_count());
824: }
826: template <typename V, typename H, typename KE>
827: inline typename KHashTable<V, H, KE>::const_iterator KHashTable<V, H, KE>::end() const noexcept
828: {
829: return cend();
830: }
832: template <typename V, typename H, typename KE>
833: inline typename KHashTable<V, H, KE>::size_type KHashTable<V, H, KE>::bucket_count() const noexcept
834: {
835: return values_.size();
836: }
838: template <typename V, typename H, typename KE>
839: inline typename KHashTable<V, H, KE>::size_type KHashTable<V, H, KE>::size() const noexcept
840: {
841: return count_;
842: }
844: template <typename V, typename H, typename KE>
845: inline typename KHashTable<V, H, KE>::size_type KHashTable<V, H, KE>::capacity() const noexcept
846: {
847: return flags_.size() * flag_pairs_per_bucket::value;
848: }
850: template <typename V, typename H, typename KE>
851: inline bool KHashTable<V, H, KE>::empty() const noexcept
852: {
853: return size() == 0;
854: }
856: // REVIEW ME: should really be called rehash()
857: template <typename V, typename H, typename KE>
858: inline PetscErrorCode KHashTable<V, H, KE>::reserve(size_type req_size) noexcept
859: {
860: PetscFunctionBegin;
861: if (size() < req_size) PetscCall(resize(req_size));
862: PetscFunctionReturn(PETSC_SUCCESS);
863: }
865: namespace detail
866: {
868: // templated version of
869: // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2. See also
870: // https://stackoverflow.com/questions/466204/rounding-up-to-next-power-of-2
871: template <typename T>
872: static inline PETSC_CONSTEXPR_14 T round_up_to_next_pow2(T v) noexcept
873: {
874: static_assert(std::numeric_limits<T>::is_integer && std::is_unsigned<T>::value, "");
875: if (v <= 1) return 1;
876: --v;
877: for (std::size_t i = 1; i < (sizeof(v) * CHAR_BIT); i *= 2) v |= v >> i;
878: ++v;
879: return v;
880: }
882: // compilers sadly don't yet recognize that the above is just searching for the next nonzero
883: // bit (https://godbolt.org/z/3q1qxqK4a) and won't emit the versions below, which usually
884: // boil down to a single tailor-made instruction.
885: //
886: // __builtin_clz():
887: // Returns the number of leading 0-bits in x, starting at the most significant bit
888: // position. If x is 0, the result is undefined.
889: //
890: // see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
892: #if PetscHasBuiltin(__builtin_clz)
893: template <>
894: inline constexpr unsigned int round_up_to_next_pow2(unsigned int v) noexcept
895: {
896: return v <= 1 ? 1 : 1 << ((sizeof(v) * CHAR_BIT) - __builtin_clz(v - 1));
897: }
898: #endif
900: #if PetscHasBuiltin(__builtin_clzl)
901: template <>
902: inline constexpr unsigned long round_up_to_next_pow2(unsigned long v) noexcept
903: {
904: return v <= 1 ? 1 : 1 << ((sizeof(v) * CHAR_BIT) - __builtin_clzl(v - 1));
905: }
906: #endif
908: // both MSVC and Intel compilers lie about having __builtin_clzll so just disable this
909: #if PetscHasBuiltin(__builtin_clzll) && !PetscDefined(HAVE_WINDOWS_COMPILERS)
910: template <>
911: inline constexpr unsigned long long round_up_to_next_pow2(unsigned long long v) noexcept
912: {
913: return v <= 1 ? 1 : 1 << ((sizeof(v) * CHAR_BIT) - __builtin_clzll(v - 1));
914: }
915: #endif
917: template <typename T>
918: static inline constexpr unsigned integer_log2(T x) noexcept
919: {
920: static_assert(std::numeric_limits<T>::is_integer && std::is_unsigned<T>::value, "");
921: return x ? 1 + integer_log2(x >> 1) : std::numeric_limits<unsigned>::max();
922: }
924: } // namespace detail
926: template <typename V, typename H, typename KE>
927: inline PetscErrorCode KHashTable<V, H, KE>::resize(size_type req_size) noexcept
928: {
929: constexpr size_type min_n_buckets = 4;
930: const auto new_n_buckets = std::max(detail::round_up_to_next_pow2(req_size), min_n_buckets);
931: const auto new_size = (new_n_buckets >> 1) + (new_n_buckets >> 2);
933: PetscFunctionBegin;
934: if (req_size == 0) {
935: // resize(0) is nominally equivalent to clear(), but clear() does not actually reduce
936: // capacity (only resets flags_ to default_bit_pattern()). So we manually kill the capacity
937: // first.
938: PetscCallCXX(flags_.clear());
939: PetscCall(clear());
940: PetscFunctionReturn(PETSC_SUCCESS);
941: }
942: // hash table count to be changed (shrink or expand); rehash
943: if (size() < new_size) {
944: const auto old_n_buckets = bucket_count();
945: const auto new_mask = new_n_buckets - 1;
946: const auto khash_fsize = [](size_type size) -> size_type {
947: if (size >= flag_pairs_per_bucket::value) {
948: // use constexpr here to force compiler to evaluate this at all optimization levels
949: constexpr auto shift_val = detail::integer_log2(flag_pairs_per_bucket::value);
951: return size >> shift_val;
952: }
953: return 1;
954: };
955: std::vector<flags_type> new_flags(khash_fsize(new_n_buckets), default_bit_pattern());
957: // grow the hash table, note order is important! we cannot just call
958: // values_.resize(new_n_buckets) because that might drop buckets containing data. The loop
959: // below (if new_n_buckets < bucket_count()) will compress the table, such that we can
960: // shrink afterwards
961: if (old_n_buckets < new_n_buckets) PetscCallCXX(values_.resize(new_n_buckets));
962: for (size_type i = 0; i < old_n_buckets; ++i) {
963: if (!occupied(i)) continue;
964: // kick-out process; sort of like in Cuckoo hashing
965: PetscCall(khash_set_deleted_<true>(i));
966: while (true) {
967: // key is updated every loop from the swap so need to recompute the hash function each
968: // time... could possibly consider stashing the hash value in the key-value pair
969: auto &key = values_[i];
970: const auto hash = hash_function()(PETSC_OPTIONAL_GET_KEY(key));
971: auto j = hash & new_mask;
972: khash_int step = 0;
974: while (!khash_is_empty_(j, new_flags)) {
975: ++step;
976: j = (j + step) & new_mask;
977: }
978: PetscCall(khash_set_empty_<false>(j, new_flags));
979: if (j < old_n_buckets && occupied(j)) {
980: using std::swap;
982: // i == j should never reach this point since occupied(j) (in this case equivalent
983: // to occupied(i)) should never be true because we call khash_set_deleted_<true>(i)
984: // above!
985: PetscAssert(i != j, PETSC_COMM_SELF, PETSC_ERR_PLIB, "i %zu = j %zu. About to swap the same element!", static_cast<std::size_t>(i), static_cast<std::size_t>(j));
986: // kick out the existing element
987: PetscCallCXX(swap(values_[j], key));
988: // mark it as deleted in the old hash table
989: PetscCall(khash_set_deleted_<true>(j));
990: } else {
991: // write the element and jump out of the loop but check that we don't self
992: // move-assign
993: if (i != j) PetscCallCXX(values_[j] = std::move(key));
994: break;
995: }
996: }
997: }
999: // shrink the hash table
1000: if (old_n_buckets > new_n_buckets) PetscCallCXX(values_.resize(new_n_buckets));
1001: PetscCallCXX(flags_ = std::move(new_flags));
1002: n_occupied_ = count_;
1003: upper_bound_ = new_size;
1004: }
1005: PetscFunctionReturn(PETSC_SUCCESS);
1006: }
1008: template <typename V, typename H, typename KE>
1009: inline PetscErrorCode KHashTable<V, H, KE>::clear() noexcept
1010: {
1011: PetscFunctionBegin;
1012: PetscCallCXX(values_.clear());
1013: PetscCallCXX(std::fill(flags_.begin(), flags_.end(), default_bit_pattern()));
1014: count_ = 0;
1015: n_occupied_ = 0;
1016: upper_bound_ = 0;
1017: PetscAssert(size() == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "clear() did not set size (%zu) to 0", size());
1018: PetscFunctionReturn(PETSC_SUCCESS);
1019: }
1021: template <typename V, typename H, typename KE>
1022: inline bool KHashTable<V, H, KE>::occupied(khash_int it) const noexcept
1023: {
1024: return khash_occupied_(it, flags_);
1025: }
1027: template <typename V, typename H, typename KE>
1028: inline bool KHashTable<V, H, KE>::occupied(const_iterator it) const noexcept
1029: {
1030: return occupied(it.it_);
1031: }
1033: template <typename V, typename H, typename KE>
1034: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::erase(iterator pos) noexcept
1035: {
1036: iterator ret(pos);
1038: PetscFunctionBegin;
1039: ++ret;
1040: PetscCallAbort(PETSC_COMM_SELF, khash_erase_(pos.it_));
1041: PetscFunctionReturn(ret);
1042: }
1044: template <typename V, typename H, typename KE>
1045: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::erase(const_iterator pos) noexcept
1046: {
1047: iterator ret(pos);
1049: PetscFunctionBegin;
1050: ++ret;
1051: PetscCallAbort(PETSC_COMM_SELF, khash_erase_(pos.it_));
1052: PetscFunctionReturn(ret);
1053: }
1055: template <typename V, typename H, typename KE>
1056: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::erase(const_iterator begin_pos, const_iterator end_pos) noexcept
1057: {
1058: PetscFunctionBegin;
1059: if (begin_pos == cbegin() && end_pos == cend()) {
1060: PetscCallAbort(PETSC_COMM_SELF, clear());
1061: PetscFunctionReturn(end());
1062: }
1063: for (; begin_pos != end_pos; ++begin_pos) PetscCallAbort(PETSC_COMM_SELF, khash_erase_(begin_pos.it_));
1064: PetscFunctionReturn(make_iterator_(begin_pos.it_));
1065: }
1067: template <typename V, typename H, typename KE>
1068: template <typename... Args>
1069: inline std::pair<typename KHashTable<V, H, KE>::iterator, bool> KHashTable<V, H, KE>::emplace(Args &&...args) noexcept
1070: {
1071: return find_and_emplace_(value_type{std::forward<Args>(args)...});
1072: }
1074: template <typename V, typename H, typename KE>
1075: inline std::pair<typename KHashTable<V, H, KE>::iterator, bool> KHashTable<V, H, KE>::insert(const value_type &val) noexcept
1076: {
1077: return find_and_emplace_(val);
1078: }
1080: template <typename V, typename H, typename KE>
1081: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::insert(const_iterator, const value_type &val) noexcept
1082: {
1083: return insert(val).first;
1084: }
1086: template <typename V, typename H, typename KE>
1087: inline std::pair<typename KHashTable<V, H, KE>::iterator, bool> KHashTable<V, H, KE>::insert(value_type &&val) noexcept
1088: {
1089: return find_and_emplace_(std::move(val));
1090: }
1092: template <typename V, typename H, typename KE>
1093: inline typename KHashTable<V, H, KE>::iterator KHashTable<V, H, KE>::insert(const_iterator, value_type &&val) noexcept
1094: {
1095: return insert(std::move(val)).first;
1096: }
1098: template <typename V, typename H, typename KE>
1099: inline typename KHashTable<V, H, KE>::hasher KHashTable<V, H, KE>::hash_function() const noexcept
1100: {
1101: return this->first();
1102: }
1104: template <typename V, typename H, typename KE>
1105: inline typename KHashTable<V, H, KE>::key_equal KHashTable<V, H, KE>::key_eq() const noexcept
1106: {
1107: return this->second();
1108: }
1110: template <typename V, typename H, typename KE>
1111: inline PetscErrorCode KHashTable<V, H, KE>::shrink_to_fit() noexcept
1112: {
1113: PetscFunctionBegin;
1114: PetscCall(resize(size()));
1115: PetscFunctionReturn(PETSC_SUCCESS);
1116: }
1118: template <typename V, typename H, typename KE>
1119: inline void KHashTable<V, H, KE>::swap(KHashTable &other) noexcept
1120: {
1121: PetscFunctionBegin;
1122: if (PetscUnlikely(this != &other)) {
1123: using std::swap;
1125: swap(values_, other.values_);
1126: swap(flags_, other.flags_);
1127: swap(count_, other.count_);
1128: swap(n_occupied_, other.n_occupied_);
1129: swap(upper_bound_, other.upper_bound_);
1130: }
1131: PetscFunctionReturnVoid();
1132: }
1134: namespace detail
1135: {
1137: template <typename KeyType, typename Hasher>
1138: struct indirect_hasher : Hasher {
1139: using nested_value_type = Hasher;
1140: using key_type = KeyType;
1142: template <typename T>
1143: PETSC_NODISCARD std::size_t operator()(const std::pair<key_type, T> &kv) const noexcept
1144: {
1145: return static_cast<const nested_value_type &>(*this)(kv.first);
1146: }
1148: template <typename T>
1149: PETSC_NODISCARD std::size_t operator()(const std::pair<key_type, T> &kv) noexcept
1150: {
1151: return static_cast<nested_value_type &>(*this)(kv.first);
1152: }
1154: using nested_value_type::operator();
1155: };
1157: template <typename KeyType, typename KeyEqual>
1158: struct indirect_equal : KeyEqual {
1159: using nested_value_type = KeyEqual;
1160: using key_type = KeyType;
1162: template <typename T>
1163: PETSC_NODISCARD bool operator()(const std::pair<key_type, T> &lhs, const std::pair<key_type, T> &rhs) const noexcept
1164: {
1165: return static_cast<const nested_value_type &>(*this)(lhs.first, rhs.first);
1166: }
1168: template <typename T>
1169: PETSC_NODISCARD bool operator()(const std::pair<key_type, T> &lhs, const key_type &rhs) const noexcept
1170: {
1171: return static_cast<const nested_value_type &>(*this)(lhs.first, rhs);
1172: }
1173: };
1175: } // namespace detail
1177: } // namespace khash
1179: // ==========================================================================================
1180: // UnorderedMap - A drop-in replacement for std::unordered_map that is more memory efficient
1181: // and performant.
1182: //
1183: // Has identical API to a C++17 conformant std::unordered_map, and behaves identically to
1184: // it. The only exception is iterator invalidation:
1185: //
1186: // Operation | std::unorderd_map | Petsc::UnorderedMap
1187: // --------------------------------------------|-----------------------|---------------------
1188: // - All read only operations, swap, std::swap | Never | Never
1189: // - clear, operator= | Always | Always
1190: // - rehash, reserve | Always | Only if causes
1191: // | | resizing
1192: // - insert, emplace, emplace_hint, operator[] | Only if causes rehash | Only if it causes
1193: // | | rehash, in which case
1194: // | | rehash will ALWAYS
1195: // | | resize
1196: // - erase | Only to the element | Only to the element
1197: // | erased | erased
1198: // ==========================================================================================
1199: template <typename K, typename T, typename H = std::hash<K>,
1200: #if PETSC_CPP_VERSION >= 14
1201: typename KE = std::equal_to<>
1202: #else
1203: typename KE = std::equal_to<K>
1204: #endif
1205: >
1206: class UnorderedMap;
1208: template <typename KeyType, typename T, typename Hash, typename KeyEqual>
1209: class UnorderedMap : public khash::KHashTable<std::pair<KeyType, T>, khash::detail::indirect_hasher<KeyType, Hash>, khash::detail::indirect_equal<KeyType, KeyEqual>> {
1210: using table_type = khash::KHashTable<std::pair<KeyType, T>, khash::detail::indirect_hasher<KeyType, Hash>, khash::detail::indirect_equal<KeyType, KeyEqual>>;
1211: using typename table_type::khash_int;
1213: public:
1214: // workaround for MSVC bug
1215: // https://developercommunity.visualstudio.com/t/error-c2244-unable-to-match-function-definition-to/225941
1216: using value_type = typename table_type::value_type;
1217: using key_type = typename value_type::first_type;
1218: using mapped_type = typename value_type::second_type;
1219: using hasher = typename table_type::hasher::nested_value_type;
1220: using key_equal = typename table_type::key_equal::nested_value_type;
1221: using size_type = typename table_type::size_type;
1222: using reference = typename table_type::reference;
1223: using const_reference = typename table_type::const_reference;
1224: using iterator = typename table_type::iterator;
1225: using const_iterator = typename table_type::const_iterator;
1227: using table_type::table_type; // inherit constructors
1229: PETSC_NODISCARD iterator find(const key_type &) noexcept;
1230: PETSC_NODISCARD const_iterator find(const key_type &) const noexcept;
1232: template <typename... Args>
1233: std::pair<iterator, bool> emplace(Args &&...) noexcept;
1235: using table_type::erase; // inherit erase overloads
1236: size_type erase(const key_type &) noexcept;
1238: mapped_type &operator[](const key_type &) noexcept;
1240: PETSC_NODISCARD std::pair<iterator, iterator> equal_range(const key_type &) noexcept;
1241: PETSC_NODISCARD std::pair<const_iterator, const_iterator> equal_range(const key_type &) const noexcept;
1243: PETSC_NODISCARD size_type count(const key_type &) const noexcept;
1244: PETSC_NODISCARD bool contains(const key_type &) const noexcept;
1246: // must be declared in class definition...
1247: friend void swap(UnorderedMap &lhs, UnorderedMap &rhs) noexcept
1248: {
1249: PetscFunctionBegin;
1250: PetscCallCXXAbort(PETSC_COMM_SELF, lhs.swap(rhs));
1251: PetscFunctionReturnVoid();
1252: }
1254: private:
1255: template <typename KeyTuple, typename ArgTuple>
1256: PETSC_NODISCARD std::pair<iterator, bool> emplace_(std::piecewise_construct_t, KeyTuple &&, ArgTuple &&) noexcept;
1257: template <typename Key, typename... Args>
1258: PETSC_NODISCARD std::pair<iterator, bool> emplace_(Key &&, Args &&...) noexcept;
1259: };
1261: // ==========================================================================================
1262: // UnorderedMap - Private API
1263: // ==========================================================================================
1265: template <typename K, typename T, typename H, typename KE>
1266: template <typename KeyTuple, typename MappedTuple>
1267: inline std::pair<typename UnorderedMap<K, T, H, KE>::iterator, bool> UnorderedMap<K, T, H, KE>::emplace_(std::piecewise_construct_t pcw, KeyTuple &&key_tuple, MappedTuple &&mapped_type_constructor_args) noexcept
1268: {
1269: // clang-format off
1270: return this->find_and_emplace_(
1271: std::get<0>(key_tuple),
1272: pcw,
1273: std::forward<KeyTuple>(key_tuple),
1274: std::forward<MappedTuple>(mapped_type_constructor_args)
1275: );
1276: // clang-format on
1277: }
1279: template <typename K, typename T, typename H, typename KE>
1280: template <typename Key, typename... Args>
1281: inline std::pair<typename UnorderedMap<K, T, H, KE>::iterator, bool> UnorderedMap<K, T, H, KE>::emplace_(Key &&key, Args &&...mapped_type_constructor_args) noexcept
1282: {
1283: return this->emplace_(std::piecewise_construct, std::forward_as_tuple(std::forward<Key>(key)), std::forward_as_tuple(std::forward<Args>(mapped_type_constructor_args)...));
1284: }
1286: // ==========================================================================================
1287: // UnorderedMap - Public API
1288: // ==========================================================================================
1290: template <typename K, typename T, typename H, typename KE>
1291: inline typename UnorderedMap<K, T, H, KE>::iterator UnorderedMap<K, T, H, KE>::find(const key_type &key) noexcept
1292: {
1293: khash_int it = 0;
1295: PetscFunctionBegin;
1296: PetscCallAbort(PETSC_COMM_SELF, this->khash_find_(key, &it));
1297: PetscFunctionReturn(this->make_iterator_(it));
1298: }
1300: template <typename K, typename T, typename H, typename KE>
1301: inline typename UnorderedMap<K, T, H, KE>::const_iterator UnorderedMap<K, T, H, KE>::find(const key_type &key) const noexcept
1302: {
1303: khash_int it = 0;
1305: PetscFunctionBegin;
1306: PetscCallAbort(PETSC_COMM_SELF, this->khash_find_(key, &it));
1307: PetscFunctionReturn(this->make_iterator_(it));
1308: }
1310: template <typename K, typename T, typename H, typename KE>
1311: template <typename... Args>
1312: inline std::pair<typename UnorderedMap<K, T, H, KE>::iterator, bool> UnorderedMap<K, T, H, KE>::emplace(Args &&...args) noexcept
1313: {
1314: return this->emplace_(std::forward<Args>(args)...);
1315: }
1317: template <typename K, typename T, typename H, typename KE>
1318: inline typename UnorderedMap<K, T, H, KE>::mapped_type &UnorderedMap<K, T, H, KE>::operator[](const key_type &key) noexcept
1319: {
1320: return this->emplace(key).first->second;
1321: }
1323: template <typename K, typename T, typename H, typename KE>
1324: inline typename UnorderedMap<K, T, H, KE>::size_type UnorderedMap<K, T, H, KE>::erase(const key_type &key) noexcept
1325: {
1326: PetscFunctionBegin;
1327: {
1328: auto it = this->find(key);
1330: if (it == this->end()) PetscFunctionReturn(0);
1331: PetscCallCXX(this->erase(it));
1332: }
1333: PetscFunctionReturn(1);
1334: }
1336: template <typename K, typename T, typename H, typename KE>
1337: inline std::pair<typename UnorderedMap<K, T, H, KE>::iterator, typename UnorderedMap<K, T, H, KE>::iterator> UnorderedMap<K, T, H, KE>::equal_range(const key_type &key) noexcept
1338: {
1339: auto it = this->find(key);
1340: return {it, it == this->end() ? it : std::next(it)};
1341: }
1343: template <typename K, typename T, typename H, typename KE>
1344: inline std::pair<typename UnorderedMap<K, T, H, KE>::const_iterator, typename UnorderedMap<K, T, H, KE>::const_iterator> UnorderedMap<K, T, H, KE>::equal_range(const key_type &key) const noexcept
1345: {
1346: auto it = this->find(key);
1347: return {it, it == this->end() ? it : std::next(it)};
1348: }
1350: template <typename K, typename T, typename H, typename KE>
1351: inline typename UnorderedMap<K, T, H, KE>::size_type UnorderedMap<K, T, H, KE>::count(const key_type &key) const noexcept
1352: {
1353: return this->contains(key);
1354: }
1356: template <typename K, typename T, typename H, typename KE>
1357: inline bool UnorderedMap<K, T, H, KE>::contains(const key_type &key) const noexcept
1358: {
1359: return this->find(key) != this->end();
1360: }
1362: // ==========================================================================================
1363: // UnorderedMap - Global functions
1364: // ==========================================================================================
1366: template <typename K, typename T, typename H, typename KE>
1367: PETSC_NODISCARD bool operator==(const UnorderedMap<K, T, H, KE> &lhs, const UnorderedMap<K, T, H, KE> &rhs) noexcept
1368: {
1369: PetscFunctionBegin;
1370: if (lhs.size() != rhs.size()) PetscFunctionReturn(false);
1371: for (auto it = lhs.begin(), lhs_end = lhs.end(), rhs_end = rhs.end(); it != lhs_end; ++it) {
1372: const auto rhs_it = rhs.find(it->first);
1374: if (rhs_it == rhs_end || !(*it == *rhs_it)) PetscFunctionReturn(false);
1375: }
1376: PetscFunctionReturn(true);
1377: }
1379: template <typename K, typename T, typename H, typename KE>
1380: PETSC_NODISCARD bool operator!=(const UnorderedMap<K, T, H, KE> &lhs, const UnorderedMap<K, T, H, KE> &rhs) noexcept
1381: {
1382: return !(lhs == rhs);
1383: }
1385: } // namespace Petsc
1387: #undef PETSC_OPTIONAL_GET_KEY