SpECTRE Documentation Coverage Report
Current view: top level - DataStructures - FixedHashMap.hpp Hit Total Coverage
Commit: 37c384043430860f87787999aa7399d01bb3d213 Lines: 9 64 14.1 %
Date: 2024-04-20 02:24:02
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <array>
       7             : #include <cstddef>
       8             : #include <iterator>
       9             : #include <memory>  // std::addressof
      10             : #include <optional>
      11             : #include <pup.h>
      12             : #include <pup_stl.h>
      13             : #include <stdexcept>
      14             : #include <type_traits>
      15             : 
      16             : #include "Utilities/Algorithm.hpp"
      17             : #include "Utilities/ConstantExpressions.hpp"
      18             : #include "Utilities/ErrorHandling/Assert.hpp"
      19             : #include "Utilities/ErrorHandling/Error.hpp"
      20             : #include "Utilities/ForceInline.hpp"
      21             : #include "Utilities/GetOutput.hpp"
      22             : #include "Utilities/Gsl.hpp"
      23             : #include "Utilities/Numeric.hpp"
      24             : #include "Utilities/PrintHelpers.hpp"
      25             : #include "Utilities/Serialization/PupStlCpp17.hpp"
      26             : #include "Utilities/TMPL.hpp"
      27             : #include "Utilities/TypeTraits.hpp"
      28             : 
      29             : /// \cond
      30             : template <size_t MaxSize, class Key, class ValueType, class Hash,
      31             :           class KeyEqual>
      32             : class FixedHashMapIterator;
      33             : /// \endcond
      34             : 
      35             : namespace FixedHashMap_detail {
      36             : template <typename T, typename = std::void_t<>>
      37             : struct has_is_perfect_member : std::false_type {};
      38             : 
      39             : template <typename T>
      40             : struct has_is_perfect_member<T,
      41             :                              std::void_t<decltype(T::template is_perfect<0>)>>
      42             :     : std::true_type {};
      43             : 
      44             : template <typename T>
      45             : constexpr bool has_is_perfect_member_v = has_is_perfect_member<T>::value;
      46             : 
      47             : template <class T, size_t MaxSize,
      48             :           bool HasIsPerfect = has_is_perfect_member_v<T>>
      49             : constexpr bool is_perfect = T::template is_perfect<MaxSize>;
      50             : 
      51             : template <class T, size_t MaxSize>
      52             : constexpr bool is_perfect<T, MaxSize, false> = false;
      53             : }  // namespace FixedHashMap_detail
      54             : 
      55             : /*!
      56             :  * \ingroup DataStructuresGroup
      57             :  * \brief A hash table with a compile-time specified maximum size and ability to
      58             :  * efficiently handle perfect hashes.
      59             :  *
      60             :  * There are a few requirements on the types passed to `FixedHashMap`. These
      61             :  * are:
      62             :  * - `Key` is CopyConstructible for copy and move semantics, as well as
      63             :  *   serialization
      64             :  * - `ValueType` is MoveConstructible
      65             :  * - If `Hash` is a perfect hash for some values of `MaxSize` then in order to
      66             :  *   use the perfect hashing improvements `Hash` must have a member variable
      67             :  *   template `is_perfect` that takes as its only template parameter a `size_t`
      68             :  *   equal to `MaxSize`. A perfect hash must map each `Key` to `[0, MaxSize)`
      69             :  *   uniquely.
      70             :  * - Keys are distinct.
      71             :  *
      72             :  * The interface is similar to std::unordered_map, so see the documentation for
      73             :  * that for details.
      74             :  *
      75             :  * #### Implementation Details
      76             :  *
      77             :  * - Uses linear probing for non-perfect hashes.
      78             :  * - The hash `Hash` is assumed to be a perfect hash for `MaxSize` if
      79             :  *   `Hash::template is_perfect<MaxSize>` is true.
      80             :  */
      81             : template <size_t MaxSize, class Key, class ValueType,
      82             :           class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
      83           1 : class FixedHashMap {
      84           0 :   static constexpr bool hash_is_perfect =
      85             :       FixedHashMap_detail::is_perfect<Hash, MaxSize>;
      86             : 
      87             :  public:
      88           0 :   using key_type = Key;
      89           0 :   using mapped_type = ValueType;
      90           0 :   using value_type = std::pair<const key_type, mapped_type>;
      91           0 :   using size_type = size_t;
      92           0 :   using difference_type = ptrdiff_t;
      93           0 :   using hasher = Hash;
      94           0 :   using key_equal = KeyEqual;
      95           0 :   using reference = value_type&;
      96           0 :   using const_reference = const value_type&;
      97           0 :   using pointer = value_type*;
      98           0 :   using const_pointer = const value_type*;
      99           0 :   using iterator =
     100             :       FixedHashMapIterator<MaxSize, key_type, value_type, hasher, key_equal>;
     101           0 :   using const_iterator =
     102             :       FixedHashMapIterator<MaxSize, key_type, const value_type, hasher,
     103             :                            key_equal>;
     104             : 
     105           0 :   FixedHashMap() = default;
     106           0 :   FixedHashMap(std::initializer_list<value_type> init);
     107           0 :   FixedHashMap(const FixedHashMap&) = default;
     108           0 :   FixedHashMap& operator=(const FixedHashMap& other);
     109           0 :   FixedHashMap(FixedHashMap&&) = default;
     110           0 :   FixedHashMap& operator=(FixedHashMap&& other);
     111           0 :   ~FixedHashMap() = default;
     112             : 
     113           0 :   iterator begin() { return unconst(cbegin()); }
     114           0 :   const_iterator begin() const {
     115             :     return const_iterator(data_.begin(), data_.end());
     116             :   }
     117           0 :   const_iterator cbegin() const { return begin(); }
     118             : 
     119           0 :   iterator end() { return unconst(cend()); }
     120           0 :   const_iterator end() const {
     121             :     return const_iterator(data_.end(), data_.end());
     122             :   }
     123           0 :   const_iterator cend() const { return end(); }
     124             : 
     125           0 :   bool empty() const { return size_ == 0; }
     126           0 :   size_t size() const { return size_; }
     127             : 
     128           0 :   void clear();
     129             : 
     130             :   /// @{
     131             :   /// Inserts the element if it does not exists.
     132           1 :   std::pair<iterator, bool> insert(const value_type& value) {
     133             :     return insert(value_type(value));
     134             :   }
     135           1 :   std::pair<iterator, bool> insert(value_type&& value) {
     136             :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     137             :     return insert_or_assign_impl<false>(const_cast<key_type&&>(value.first),
     138             :                                         std::move(value.second));
     139             :   }
     140             :   template <typename... Args>
     141           1 :   std::pair<iterator, bool> emplace(Args&&... args) {
     142             :     return insert(value_type(std::forward<Args>(args)...));
     143             :   }
     144             :   /// @}
     145             :   /// @{
     146             :   /// Inserts the element if it does not exists, otherwise assigns to it the new
     147             :   /// value.
     148             :   template <class M>
     149           1 :   std::pair<iterator, bool> insert_or_assign(const key_type& key, M&& obj) {
     150             :     return insert_or_assign(key_type{key}, std::forward<M>(obj));
     151             :   }
     152             :   template <class M>
     153           1 :   std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj) {
     154             :     return insert_or_assign_impl<true>(std::move(key), std::forward<M>(obj));
     155             :   }
     156             :   /// @}
     157             : 
     158           0 :   iterator erase(const const_iterator& pos);
     159           0 :   size_t erase(const key_type& key);
     160             : 
     161           0 :   mapped_type& at(const key_type& key);
     162           0 :   const mapped_type& at(const key_type& key) const;
     163           0 :   mapped_type& operator[](const key_type& key);
     164             : 
     165           0 :   size_t count(const key_type& key) const;
     166           0 :   iterator find(const key_type& key) {
     167             :     return unconst(std::as_const(*this).find(key));
     168             :   }
     169           0 :   const_iterator find(const key_type& key) const {
     170             :     auto it = get_data_entry(key);
     171             :     return it != data_.end() and is_set(*it) ? const_iterator(&*it, data_.end())
     172             :                                              : end();
     173             :   }
     174             : 
     175             :   /// Check if `key` is in the map
     176           1 :   bool contains(const key_type& key) const { return find(key) != end(); }
     177             : 
     178             :   /// Get key equal function object
     179           1 :   key_equal key_eq() const { return key_equal{}; }
     180             :   /// Get hash function object
     181           1 :   hasher hash_function() const { return hasher{}; }
     182             : 
     183             :   // NOLINTNEXTLINE(google-runtime-references)
     184           0 :   void pup(PUP::er& p) {
     185             :     size_t version = 0;
     186             :     p | version;
     187             :     // Remember to increment the version number when making changes to this
     188             :     // function. Retain support for unpacking data written by previous versions
     189             :     // whenever possible. See `Domain` docs for details.
     190             :     if (version >= 0) {
     191             :       p | data_;
     192             :       p | size_;
     193             :     }
     194             :   }
     195             : 
     196             :  private:
     197             :   template <size_t FMaxSize, class FKey, class FValueType, class FHash,
     198             :             class FKeyEqual>
     199             :   // NOLINTNEXTLINE(readability-redundant-declaration) false positive
     200           0 :   friend bool operator==(
     201             :       const FixedHashMap<FMaxSize, FKey, FValueType, FHash, FKeyEqual>& a,
     202             :       const FixedHashMap<FMaxSize, FKey, FValueType, FHash, FKeyEqual>& b);
     203             : 
     204             :   template <bool Assign, class M>
     205           0 :   std::pair<iterator, bool> insert_or_assign_impl(key_type&& key, M&& obj);
     206             : 
     207           0 :   using storage_type = std::array<std::optional<value_type>, MaxSize>;
     208             : 
     209           0 :   SPECTRE_ALWAYS_INLINE size_type hash(const key_type& key) const {
     210             :     if constexpr (hash_is_perfect) {
     211             :       return Hash{}(key);
     212             :     } else {
     213             :       return Hash{}(key) % MaxSize;
     214             :     }
     215             :   }
     216             : 
     217             :   template <bool IsInserting>
     218           0 :   typename storage_type::iterator get_data_entry(const Key& key);
     219             : 
     220           0 :   typename storage_type::const_iterator get_data_entry(const Key& key) const {
     221             :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     222             :     return const_cast<FixedHashMap&>(*this).get_data_entry<false>(key);
     223             :   }
     224             : 
     225           0 :   iterator unconst(const const_iterator& it) {
     226             :     return iterator(&it.get_optional() - data_.begin() + data_.begin(),
     227             :                     data_.end());
     228             :   }
     229             : 
     230           0 :   SPECTRE_ALWAYS_INLINE static bool is_set(
     231             :       const std::optional<value_type>& opt) {
     232             :     return static_cast<bool>(opt);
     233             :   }
     234             : 
     235           0 :   storage_type data_{};
     236           0 :   size_t size_ = 0;
     237             : };
     238             : 
     239             : /// \cond HIDDEN_SYMBOLS
     240             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     241             :           class KeyEqual>
     242             : class FixedHashMapIterator {
     243             :  public:
     244             :   using iterator_category = std::forward_iterator_tag;
     245             :   using value_type = std::remove_const_t<ValueType>;
     246             :   using difference_type = ptrdiff_t;
     247             :   using pointer = ValueType*;
     248             :   using reference = ValueType&;
     249             : 
     250             :   FixedHashMapIterator() = default;
     251             : 
     252             :   /// Implicit conversion from mutable to const iterator.
     253             :   // NOLINTNEXTLINE(google-explicit-constructor)
     254             :   operator FixedHashMapIterator<MaxSize, Key, const ValueType, Hash, KeyEqual>()
     255             :       const {
     256             :     return {entry_, end_};
     257             :   }
     258             : 
     259             :   reference operator*() const {
     260             :     ASSERT(entry_ != nullptr, "Invalid FixedHashMapIterator");
     261             :     ASSERT(is_set(*entry_),
     262             :            "FixedHashMapIterator points to an invalid value in the map.");
     263             :     // deference pointer, then dereference std::optional
     264             :     return entry_->value();
     265             :   }
     266             :   pointer operator->() const {
     267             :     ASSERT(entry_ != nullptr, "Invalid FixedHashMapIterator");
     268             :     ASSERT(is_set(*entry_),
     269             :            "FixedHashMapIterator points to an invalid value in the map.");
     270             :     return std::addressof(entry_->value());
     271             :   }
     272             : 
     273             :   FixedHashMapIterator& operator++();
     274             :   FixedHashMapIterator operator++(int);
     275             : 
     276             :  private:
     277             :   friend class FixedHashMap<MaxSize, Key, typename value_type::second_type,
     278             :                             Hash, KeyEqual>;
     279             :   friend class FixedHashMapIterator<MaxSize, Key, value_type, Hash, KeyEqual>;
     280             : 
     281             :   friend bool operator==(const FixedHashMapIterator& a,
     282             :                          const FixedHashMapIterator& b) {
     283             :     return a.entry_ == b.entry_;
     284             :   }
     285             :   friend bool operator<(const FixedHashMapIterator& a,
     286             :                         const FixedHashMapIterator& b) {
     287             :     return a.entry_ < b.entry_;
     288             :   }
     289             : 
     290             :   // The rest of the comparison operators don't need access to the
     291             :   // class internals, but they need to be instantiated for each
     292             :   // instantiation of the class.
     293             :   friend bool operator!=(const FixedHashMapIterator& a,
     294             :                          const FixedHashMapIterator& b) {
     295             :     return not(a == b);
     296             :   }
     297             :   friend bool operator>(const FixedHashMapIterator& a,
     298             :                         const FixedHashMapIterator& b) {
     299             :     return b < a;
     300             :   }
     301             :   friend bool operator<=(const FixedHashMapIterator& a,
     302             :                          const FixedHashMapIterator& b) {
     303             :     return not(b < a);
     304             :   }
     305             :   friend bool operator>=(const FixedHashMapIterator& a,
     306             :                          const FixedHashMapIterator& b) {
     307             :     return not(a < b);
     308             :   }
     309             : 
     310             :   using map_optional_type = std::optional<std::remove_const_t<ValueType>>;
     311             :   using optional_type =
     312             :       tmpl::conditional_t<std::is_const<ValueType>::value,
     313             :                           const map_optional_type, map_optional_type>;
     314             : 
     315             :   SPECTRE_ALWAYS_INLINE static bool is_set(const optional_type& opt) {
     316             :     return static_cast<bool>(opt);
     317             :   }
     318             : 
     319             :   FixedHashMapIterator(optional_type* const entry, optional_type* const end)
     320             :       : entry_(entry), end_(end) {
     321             :     if (entry_ != end_ and not*entry_) {
     322             :       operator++();
     323             :     }
     324             :   }
     325             : 
     326             :   optional_type& get_optional() const { return *entry_; }
     327             : 
     328             :   optional_type* entry_{nullptr};
     329             :   optional_type* end_{nullptr};
     330             : };
     331             : 
     332             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     333             :           class KeyEqual>
     334             : FixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>&
     335             : FixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>::operator++() {
     336             :   ASSERT(entry_ != end_,
     337             :          "Tried to increment an end iterator, which is undefined behavior.");
     338             :   // Move to next element, which might not be the next element in the std::array
     339             :   do {
     340             :     ++entry_;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
     341             :   } while (entry_ < end_ and not is_set(*entry_));
     342             :   return *this;
     343             : }
     344             : 
     345             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     346             :           class KeyEqual>
     347             : FixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>
     348             : FixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>::operator++(int) {
     349             :   const auto ret = *this;
     350             :   operator++();
     351             :   return ret;
     352             : }
     353             : /// \endcond
     354             : 
     355             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     356             :           class KeyEqual>
     357             : FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>&
     358             : FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::operator=(
     359             :     const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& other) {
     360             :   if (this == &other) {
     361             :     return *this;
     362             :   }
     363             :   clear();
     364             :   size_ = other.size_;
     365             :   for (size_t i = 0; i < data_.size(); ++i) {
     366             :     const auto& other_optional = gsl::at(other.data_, i);
     367             :     if (is_set(other_optional)) {
     368             :       // The std::optionals cannot be assigned to because they
     369             :       // contain the map keys, which are const, so we have to replace
     370             :       // the contents instead, since that counts as a destroy +
     371             :       // initialize.
     372             :       gsl::at(data_, i).emplace(*other_optional);
     373             :     }
     374             :   }
     375             :   return *this;
     376             : }
     377             : 
     378             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     379             :           class KeyEqual>
     380             : FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>&
     381             : FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::operator=(
     382             :     FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>&& other) {
     383             :   if (this == &other) {
     384             :     return *this;
     385             :   }
     386             :   clear();
     387             :   size_ = other.size_;
     388             :   for (size_t i = 0; i < data_.size(); ++i) {
     389             :     auto& other_optional = gsl::at(other.data_, i);
     390             :     if (is_set(other_optional)) {
     391             :       // The std::optionals cannot be assigned to because they
     392             :       // contain the map keys, which are const, so we have to replace
     393             :       // the contents instead, since that counts as a destroy +
     394             :       // initialize.
     395             :       gsl::at(data_, i).emplace(std::move(*other_optional));
     396             :     }
     397             :   }
     398             :   return *this;
     399             : }
     400             : 
     401             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     402             :           class KeyEqual>
     403             : FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::FixedHashMap(
     404             :     std::initializer_list<value_type> init) {
     405             :   // size_ is set by calling insert
     406             :   for (const auto& entry : init) {
     407             :     insert(entry);
     408             :   }
     409             : }
     410             : 
     411             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     412             :           class KeyEqual>
     413             : void FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::clear() {
     414             :   for (auto& entry : data_) {
     415             :     entry.reset();
     416             :   }
     417             :   size_ = 0;
     418             : }
     419             : 
     420             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     421             :           class KeyEqual>
     422             : template <bool Assign, class M>
     423             : auto FixedHashMap<MaxSize, Key, ValueType, Hash,
     424           0 :                   KeyEqual>::insert_or_assign_impl(key_type&& key, M&& obj)
     425             :     -> std::pair<iterator, bool> {
     426             :   auto data_it = get_data_entry<true>(key);
     427             :   if (UNLIKELY(data_it == data_.end())) {
     428             :     ERROR("Unable to insert element into FixedHashMap of maximum size "
     429             :           << MaxSize << " because it is full. If the current size (" << size_
     430             :           << ") is not equal to the maximum size then please file an issue "
     431             :              "with the code you used to produce this error. "
     432             :           << (hash_is_perfect ? " If a perfect hash is used and it hashes to "
     433             :                                 "past the last element stored, then this "
     434             :                                 "error may also be triggered."
     435             :                               : ""));
     436             :   }
     437             :   // data_it points to either the existing element or a new bucket to place the
     438             :   // element.
     439             :   const auto is_new_entry = not is_set(*data_it);
     440             :   if (is_new_entry or Assign) {
     441             :     if (is_new_entry) {
     442             :       ++size_;
     443             :     }
     444             :     data_it->emplace(std::move(key), std::forward<M>(obj));
     445             :   }
     446             :   return std::make_pair(iterator{data_it, data_.end()}, is_new_entry);
     447             : }
     448             : 
     449             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     450             :           class KeyEqual>
     451             : auto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::erase(
     452             :     const const_iterator& pos) -> iterator {
     453             :   auto next_it = &(unconst(pos).get_optional());
     454             :   --size_;
     455             :   next_it->reset();
     456             :   // pos now points to an unset entry, advance to next valid one
     457             :   do {
     458             :     ++next_it;
     459             :     if (next_it == data_.end()) {
     460             :       next_it = data_.begin();
     461             :     }
     462             :     if (is_set(*next_it)) {
     463             :       return {next_it, data_.end()};
     464             :     }
     465             :   } while (next_it != &pos.get_optional());
     466             :   return end();
     467             : }
     468             : 
     469             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     470             :           class KeyEqual>
     471             : size_t FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::erase(
     472             :     const key_type& key) {
     473             :   auto it = get_data_entry<false>(key);
     474             :   if (it == data_.end() or not is_set(*it)) {
     475             :     return 0;
     476             :   }
     477             :   it->reset();
     478             :   --size_;
     479             :   return 1;
     480             : }
     481             : 
     482             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     483             :           class KeyEqual>
     484             : auto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::at(
     485             :     const key_type& key) -> mapped_type& {
     486             :   auto it = get_data_entry<false>(key);
     487             :   if (it == data_.end() or not is_set(*it)) {
     488             :     // Use `ERROR_AS` instead of `throw` to print a backtrace.
     489             :     ERROR_AS(get_output(key) + " not in FixedHashMap", std::out_of_range);
     490             :   }
     491             :   return it->value().second;
     492             : }
     493             : 
     494             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     495             :           class KeyEqual>
     496             : auto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::at(
     497             :     const key_type& key) const -> const mapped_type& {
     498             :   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     499             :   return const_cast<FixedHashMap&>(*this).at(key);
     500             : }
     501             : 
     502             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     503             :           class KeyEqual>
     504             : auto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::operator[](
     505             :     const key_type& key) -> mapped_type& {
     506             :   auto it = get_data_entry<true>(key);
     507             :   if (it == data_.end()) {
     508             :     ERROR("Unable to insert element into FixedHashMap of maximum size "
     509             :           << MaxSize << " because it is full. If the current size (" << size_
     510             :           << ") is not equal to the maximum size then please file an issue "
     511             :              "with the code you used to produce this error. "
     512             :           << (hash_is_perfect ? " If a perfect hash is used and it hashes to "
     513             :                                 "past the last element stored, then this "
     514             :                                 "error may also be triggered."
     515             :                               : ""));
     516             :   }
     517             :   if (not is_set(*it)) {
     518             :     ++size_;
     519             :     it->emplace(key, mapped_type{});
     520             :   }
     521             :   return it->value().second;
     522             : }
     523             : 
     524             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     525             :           class KeyEqual>
     526             : size_t FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::count(
     527             :     const key_type& key) const {
     528             :   const auto it = get_data_entry(key);
     529             :   return it == data_.end() or not is_set(*it) ? 0 : 1;
     530             : }
     531             : 
     532             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     533             :           class KeyEqual>
     534             : template <bool IsInserting>
     535           0 : auto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::get_data_entry(
     536             :     const Key& key) -> typename storage_type::iterator {
     537             :   const auto hashed_key = hash(key);
     538             :   auto it = data_.begin() + hashed_key;
     539             :   if constexpr (not hash_is_perfect) {
     540             :     // First search for an existing key.
     541             :     while (not is_set(*it) or it->value().first != key) {
     542             :       if (++it == data_.end()) {
     543             :         it = data_.begin();
     544             :       }
     545             :       if (UNLIKELY(it == data_.begin() + hashed_key)) {
     546             :         // not found, return end iterator to storage_type
     547             :         it = data_.end();
     548             :         break;
     549             :       }
     550             :     }
     551             :     // If we are inserting an element but couldn't find an existing key, then
     552             :     // find the first available bucket.
     553             :     if (IsInserting and it == data_.end()) {
     554             :       it = data_.begin() + hashed_key;
     555             :       while (is_set(*it)) {
     556             :         if (++it == data_.end()) {
     557             :           it = data_.begin();
     558             :         }
     559             :         if (UNLIKELY(it == data_.begin() + hashed_key)) {
     560             :           // could not find any open bucket
     561             :           return data_.end();
     562             :         }
     563             :       }
     564             :     }
     565             :   }
     566             :   return it;
     567             : }
     568             : 
     569             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     570             :           class KeyEqual>
     571           0 : bool operator==(
     572             :     const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& a,
     573             :     const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& b) {
     574             :   if (a.size_ != b.size_) {
     575             :     return false;
     576             :   }
     577             :   for (const auto& [key, value] : a) {
     578             :     const auto found_in_b = b.find(key);
     579             :     if (found_in_b == b.end() or found_in_b->second != value) {
     580             :       return false;
     581             :     }
     582             :   }
     583             :   return true;
     584             : }
     585             : 
     586             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     587             :           class KeyEqual>
     588           0 : bool operator!=(
     589             :     const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& a,
     590             :     const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& b) {
     591             :   return not(a == b);
     592             : }
     593             : 
     594             : template <size_t MaxSize, class Key, class ValueType, class Hash,
     595             :           class KeyEqual>
     596           0 : std::ostream& operator<<(
     597             :     std::ostream& os,
     598             :     const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& m) {
     599             :   unordered_print_helper(
     600             :       os, std::begin(m), std::end(m),
     601             :       [](std::ostream& out,
     602             :          const typename FixedHashMap<MaxSize, Key, ValueType, Hash,
     603             :                                      KeyEqual>::value_type& entry) {
     604             :         out << "[";
     605             :         print_value(out, entry.first);
     606             :         out << ",";
     607             :         print_value(out, entry.second);
     608             :         out << "]";
     609             :       });
     610             :   return os;
     611             : }

Generated by: LCOV version 1.14