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! 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> 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_(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