2  * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
 
    5  * @file CxxUtils/ConcurrentToValMap.icc
 
    6  * @author scott snyder <snyder@bnl.gov>
 
    8  * @brief Hash map from pointers/integers to arbitrary objects allowing
 
    9  *        concurrent, lockless reads.
 
   16 #define T_CONCURRENTTOVALMAP template <class KEY, class VALUE, \
 
   17                                        template <class> class UPDATER,  \
 
   18                                        class HASHER, class MATCHER,     \
 
   19                                        detail::ConcurrentHashmapVal_t NULLVAL> \
 
   20   requires (detail::IsConcurrentHashmapPayload<KEY> &&                  \
 
   21             detail::IsUpdater<UPDATER> &&                               \
 
   22             detail::IsHash<HASHER, KEY> &&                              \
 
   23             detail::IsBinaryPredicate<MATCHER, KEY>)
 
   25 #define CONCURRENTTOVALMAP ConcurrentToValMap<KEY, VALUE, UPDATER, HASHER, MATCHER, NULLVAL>
 
   30  * @param updater Object used to manage memory
 
   31  *                (see comments at the start of the class).
 
   32  * @param capacity The initial table capacity.
 
   33  *                 (Will be rounded up to a power of two.)
 
   34  * @param ctx Execution context.
 
   37 CONCURRENTTOVALMAP::ConcurrentToValMap (Updater_t&& updater,
 
   38                                         size_type capacity /*= 64*/,
 
   40                                           /* = Updater_t::defaultContext()*/) 
 
   41   : m_impl (std::move (updater),
 
   51  * @brief Constructor from another map.
 
   52  * @param updater Object used to manage memory
 
   53  *                (see comments at the start of the class).
 
   54  * @param capacity The initial table capacity of the new table.
 
   55  *                 (Will be rounded up to a power of two.)
 
   56  * @param ctx Execution context.
 
   58  * (Not really a copy constructor since we need to pass @c updater.)
 
   61 CONCURRENTTOVALMAP::ConcurrentToValMap (const ConcurrentToValMap& other,
 
   63                                         size_type capacity /*= 64*/,
 
   65                                           /*= Updater_t::defaultContext()*/)
 
   66   : m_impl (std::move (updater),
 
   72   // not using reference, because our iterator doesn't return a reference
 
   73   for (const auto p : other) {
 
   74     this->put (p.first, std::make_unique<mapped_type> (p.second), ctx);
 
   80  * @brief Constructor from a range.
 
   81  * @param f Start iterator for the range.
 
   82  * @param l End iterator for the range.
 
   83  * @param updater Object used to manage memory
 
   84  *                (see comments at the start of the class).
 
   85  * @param capacity The initial table capacity of the new table.
 
   86  *                 (Will be rounded up to a power of two.)
 
   87  * @param ctx Execution context.
 
   89  * Constructor from a range of pairs.
 
   92 template <class InputIterator>
 
   93 CONCURRENTTOVALMAP::ConcurrentToValMap (InputIterator f,
 
   96                                         size_t capacity /*= 64*/,
 
   98                                           /*= Updater_t::defaultContext()*/)
 
   99   : m_impl (std::move (updater),
 
  105   if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
 
  107     for (; f != l; ++f) {
 
  108       emplace (f->first, std::move (f->second), ctx);
 
  112     for (; f != l; ++f) {
 
  113       emplace (f->first, f->second, ctx);
 
  123 CONCURRENTTOVALMAP::~ConcurrentToValMap()
 
  125   // Need to delete the values that we've stored.
 
  126   auto [begin, end] = m_impl.range();
 
  127   while (begin != end) {
 
  128     if (begin.key() != Impl_t::nullval) {
 
  129       delete mappedAsMapped (begin.value());
 
  137  * @brief Return the number of items currently in the map.
 
  141 auto CONCURRENTTOVALMAP::size() const -> size_type
 
  143   return m_impl.size();
 
  148  * @brief Test if the map is currently empty.
 
  152 bool CONCURRENTTOVALMAP::empty() const
 
  154   return !m_impl.size();
 
  159  * @brief Return the current size (capacity) of the hash table.
 
  163 auto CONCURRENTTOVALMAP::capacity() const -> size_t
 
  165   return m_impl.capacity();
 
  170  * @brief Constructor.
 
  171  * @param it Iterator of the underlying table.
 
  175 CONCURRENTTOVALMAP::const_iterator::const_iterator (typename Impl_t::const_iterator it)
 
  182  * @brief Conversion from non-const iterator (for interoperability).
 
  183  * @param other The other iterator.
 
  187 CONCURRENTTOVALMAP::const_iterator::const_iterator (const iterator& other)
 
  188   : m_impl (other.m_impl)
 
  194  * @brief Test if this iterator is valid.
 
  196  * This should be the same as testing for != end().
 
  200 auto CONCURRENTTOVALMAP::const_iterator::valid() const -> bool
 
  202   return m_impl.valid();
 
  207  * @brief iterator_facade requirement: Increment the iterator.
 
  211 auto CONCURRENTTOVALMAP::const_iterator::increment() -> void
 
  218  * @brief iterator_facade requirement: Decrement the iterator.
 
  222 auto CONCURRENTTOVALMAP::const_iterator::decrement() -> void
 
  229  * @brief iterator_facade requirement: Dereference the iterator.
 
  233 auto CONCURRENTTOVALMAP::const_iterator::dereference() const
 
  234   -> const const_iterator_value
 
  236   return const_iterator_value (keyAsKey (m_impl.key()),
 
  237                                *mappedAsMapped (m_impl.value()));
 
  242  * @brief iterator_facade requirement: Equality test.
 
  246 auto CONCURRENTTOVALMAP::const_iterator::equal (const const_iterator& other) const
 
  249   return !(m_impl != other.m_impl);
 
  254  * @brief iterator_facade requirement: Equality test.
 
  258 auto CONCURRENTTOVALMAP::const_iterator::equal (const iterator& other) const
 
  261   return !(m_impl != other.m_impl);
 
  266  * @brief Constructor.
 
  267  * @param it Iterator of the underlying table.
 
  271 CONCURRENTTOVALMAP::iterator::iterator (typename Impl_t::const_iterator it)
 
  278  * @brief Test if this iterator is valid.
 
  280  * This should be the same as testing for != end().
 
  284 auto CONCURRENTTOVALMAP::iterator::valid() const -> bool
 
  286   return m_impl.valid();
 
  291  * @brief iterator_facade requirement: Increment the iterator.
 
  295 auto CONCURRENTTOVALMAP::iterator::increment() -> void
 
  302  * @brief iterator_facade requirement: Decrement the iterator.
 
  306 auto CONCURRENTTOVALMAP::iterator::decrement() -> void
 
  313  * @brief iterator_facade requirement: Dereference the iterator.
 
  317 auto CONCURRENTTOVALMAP::iterator::dereference() const
 
  318   -> const iterator_value
 
  320   return iterator_value (keyAsKey (m_impl.key()),
 
  321                          *mappedAsMapped (m_impl.value()));
 
  326  * @brief iterator_facade requirement: Equality test.
 
  330 auto CONCURRENTTOVALMAP::iterator::equal (const iterator& other) const
 
  333   return !(m_impl != other.m_impl);
 
  338  * @brief iterator_facade requirement: Equality test.  (Interoperability.)
 
  342 auto CONCURRENTTOVALMAP::iterator::equal (const const_iterator& other) const
 
  345   return !(m_impl != other.m_impl);
 
  350  * @brief Return an iterator range covering the entire map.
 
  353 auto CONCURRENTTOVALMAP::range() const -> const_iterator_range
 
  355   auto [begin, end] = m_impl.range();
 
  356   return const_iterator_range (begin, end);
 
  361  * @brief Return an iterator range covering the entire map.
 
  363  * The mapped objects must themselves be thread-safe in order to make
 
  364  * any changes to them through the returned iterators.
 
  367 auto CONCURRENTTOVALMAP::range() -> iterator_range
 
  369   auto [begin, end] = m_impl.range();
 
  370   return iterator_range (begin, end);
 
  375  * @brief Iterator at the start of the map.
 
  379 auto CONCURRENTTOVALMAP::begin() const -> const_iterator
 
  381   return const_iterator (m_impl.begin());
 
  386  * @brief Iterator at the end of the map.
 
  390 auto CONCURRENTTOVALMAP::end() const -> const_iterator
 
  392   return const_iterator (m_impl.end());
 
  397  * @brief Iterator at the start of the map.
 
  399  * The mapped objects must themselves be thread-safe in order to make
 
  400  * any changes to them through the returned iterators.
 
  404 auto CONCURRENTTOVALMAP::begin() -> iterator
 
  406   return iterator (m_impl.begin());
 
  411  * @brief Iterator at the end of the map.
 
  413  * The mapped objects must themselves be thread-safe in order to make
 
  414  * any changes to them through the returned iterators.
 
  418 auto CONCURRENTTOVALMAP::end() -> iterator
 
  420   return iterator (m_impl.end());
 
  425  * @brief Iterator at the start of the map.
 
  429 auto CONCURRENTTOVALMAP::cbegin() const -> const_iterator
 
  436  * @brief Iterator at the end of the map.
 
  440 auto CONCURRENTTOVALMAP::cend() const -> const_iterator
 
  447  * @brief Test if a key is in the container.
 
  448  * @param key The key to test.
 
  452 bool CONCURRENTTOVALMAP::contains (const key_type key) const
 
  454   return get(key).valid();
 
  459  * @brief Return the number of times a given key is in the container.
 
  460  * @param key The key to test.
 
  462  * Returns either 0 or 1, depending on whether or not the key is in the map.
 
  466 auto CONCURRENTTOVALMAP::count (const key_type key) const -> size_type
 
  468   return contains (key) ? 1 : 0;
 
  473  * @brief Look up an element in the map.
 
  474  * @param key The element to find.
 
  476  * Returns either an iterator referencing the found element or end().
 
  480 auto CONCURRENTTOVALMAP::find (const key_type key) const -> const_iterator
 
  482   return const_iterator (this->get (key));
 
  487  * @brief Look up an element in the map.
 
  488  * @param key The element to find.
 
  490  * Returns either an iterator referencing the found element or end().
 
  492  * The mapped object must itself be thread-safe in order to make
 
  493  * any changes to it through the returned iterator.
 
  497 auto CONCURRENTTOVALMAP::find (const key_type key) -> iterator
 
  499   return iterator (this->get (key));
 
  504  * @brief Look up an element in the map.
 
  505  * @param key The element to find.
 
  507  * Returns the value associated with the key.
 
  508  * Throws @c std::out_of_range if the key does not exist in the map.
 
  511 auto CONCURRENTTOVALMAP::at (const key_type key) const -> const mapped_type&
 
  513   typename Impl_t::const_iterator it = this->get (key);
 
  515     throw std::out_of_range ("ConcurrentToValMap::at");
 
  517   return *mappedAsMapped (it.value());
 
  522  * @brief Look up an element in the map.
 
  523  * @param key The element to find.
 
  525  * Returns the value associated with the key.
 
  526  * Throws @c std::out_of_range if the key does not exist in the map.
 
  528  * This returns a non-const reference to the mapped object.
 
  529  * The mapped object must be thread-safe itself in order to safely
 
  530  * make any changes to it.
 
  533 auto CONCURRENTTOVALMAP::at (const key_type key) -> mapped_type&
 
  535   typename Impl_t::const_iterator it = this->get (key);
 
  537     throw std::out_of_range ("ConcurrentToValMap::at");
 
  539   return *mappedAsMapped (it.value());
 
  544  * @brief Return a range of iterators with entries matching @c key.
 
  545  * @param key The element to find.
 
  547  * As keys are unique in this container, this is either a single-element
 
  548  * range, or both iterators are equal to end().
 
  551 auto CONCURRENTTOVALMAP::equal_range (const key_type key) const
 
  552   -> std::pair<const_iterator, const_iterator>
 
  554   const_iterator i1 = find (key);
 
  555   const_iterator i2 = i1;
 
  559   return std::make_pair (i1, i2);
 
  564  * @brief Return a range of iterators with entries matching @c key.
 
  565  * @param key The element to find.
 
  567  * As keys are unique in this container, this is either a single-element
 
  568  * range, or both iterators are equal to end().
 
  570  * The mapped objects must themselves be thread-safe in order to make
 
  571  * any changes to them through the returned iterators.
 
  574 auto CONCURRENTTOVALMAP::equal_range (const key_type key)
 
  575   -> std::pair<iterator, iterator>
 
  577   iterator i1 = find (key);
 
  582   return std::make_pair (i1, i2);
 
  587  * @brief Add an element to the map.
 
  588  * @param key The key of the new item to add.
 
  589  * @param val The value of the new item to add.
 
  590  * @param ctx Execution context.
 
  592  * This will not overwrite an existing entry.
 
  593  * The first element in the returned pair is an iterator referencing
 
  594  * the added item.  The second is a flag that is true if a new element
 
  599 auto CONCURRENTTOVALMAP::emplace (key_type key,
 
  600                                   const mapped_type& val,
 
  602                                     /*= Updater_t::defaultContext()*/)
 
  603   -> std::pair<const_iterator, bool>
 
  605   return put (key, std::make_unique<mapped_type> (val), ctx);
 
  610  * @brief Add an element to the map.
 
  611  * @param key The key of the new item to add.
 
  612  * @param val The value of the new item to add.
 
  613  * @param ctx Execution context.
 
  615  * This will not overwrite an existing entry.
 
  616  * The first element in the returned pair is an iterator referencing
 
  617  * the added item.  The second is a flag that is true if a new element
 
  622 auto CONCURRENTTOVALMAP::emplace (key_type key,
 
  625                                     /*= Updater_t::defaultContext()*/)
 
  626   -> std::pair<const_iterator, bool>
 
  628   return put (key, std::make_unique<mapped_type> (std::move (val)), ctx);
 
  633  * @brief Add an element to the map.
 
  634  * @param key The key of the new item to add.
 
  635  * @param val The value of the new item to add.
 
  636  * @param ctx Execution context.
 
  638  * This will not overwrite an existing entry.
 
  639  * The first element in the returned pair is an iterator referencing
 
  640  * the added item.  The second is a flag that is true if a new element
 
  645 auto CONCURRENTTOVALMAP::emplace (key_type key,
 
  646                                   std::unique_ptr<mapped_type> val,
 
  648                                     /*= Updater_t::defaultContext()*/)
 
  649   -> std::pair<const_iterator, bool>
 
  651   return put (key, std::move (val), ctx);
 
  656  * @brief Add an element to the map.
 
  657  * @param p The item to add.
 
  658  *          Should be a pair where first is the string key
 
  659  *          and second is the integer value.
 
  660  * @param ctx Execution context.
 
  662  * This will not overwrite an existing entry.
 
  663  * The first element in the returned pair is an iterator referencing
 
  664  * the added item.  The second is a flag that is true if a new element
 
  668 template <class PAIR>
 
  670 auto CONCURRENTTOVALMAP::insert (const PAIR& p,
 
  671                                  const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  672   -> std::pair<const_iterator, bool>
 
  674   return emplace (p.first, p.second, ctx);
 
  679  * @brief Add an element to the map.
 
  680  * @param p The item to add.
 
  681  *          Should be a pair where first is the string key
 
  682  *          and second is the integer value.
 
  683  * @param ctx Execution context.
 
  685  * This will not overwrite an existing entry.
 
  686  * The first element in the returned pair is an iterator referencing
 
  687  * the added item.  The second is a flag that is true if a new element
 
  691 template <class PAIR>
 
  693 auto CONCURRENTTOVALMAP::insert (PAIR&& p,
 
  694                                  const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  695   -> std::pair<const_iterator, bool>
 
  697   return emplace (p.first, std::move (p.second), ctx);
 
  702  * @brief Insert a range of elements to the map.
 
  703  * @param first Start of the range.
 
  704  * @param last End of the range.
 
  705  * @param ctx Execution context.
 
  707  * The range should be a sequence of pairs where first is the string key
 
  708  *  and second is the integer value.
 
  711 template <class InputIterator>
 
  712 void CONCURRENTTOVALMAP::insert (InputIterator first, InputIterator last,
 
  713                                  const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  715   if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
 
  717     for (; first != last; ++first) {
 
  718       emplace (first->first, std::move (first->second), ctx);
 
  722     for (; first != last; ++first) {
 
  723       emplace (first->first, first->second, ctx);
 
  730  * @brief Increase the table capacity.
 
  731  * @param capacity The new table capacity.
 
  732  * @param ctx Execution context.
 
  734  * No action will be taken if @c capacity is smaller
 
  735  * than the current capacity.
 
  739 void CONCURRENTTOVALMAP::reserve (size_type capacity,
 
  741                                     /*= Updater_t::defaultContext()*/)
 
  743   return m_impl.reserve (capacity, ctx);
 
  748  * @brief Increase the table capacity.
 
  749  * @param capacity The new table capacity.
 
  751  * No action will be taken if @c capacity is smaller
 
  752  * than the current capacity.
 
  756 void CONCURRENTTOVALMAP::rehash (size_type capacity)
 
  758   return reserve (capacity);
 
  763  * @brief Called when this thread is no longer referencing anything
 
  764  *        from this container.
 
  765  * @param ctx Execution context.
 
  769 void CONCURRENTTOVALMAP::quiescent (const Context_t& ctx)
 
  771   return m_impl.quiescent (ctx);
 
  776  * @brief Swap this container with another.
 
  777  * @param other The container with which to swap.
 
  779  * This will also call swap on the Updater object; hence, the Updater
 
  780  * object must also support swap.
 
  782  * This operation is NOT thread-safe.  No other threads may be accessing
 
  783  * either container during this operation.
 
  786 void CONCURRENTTOVALMAP::swap (ConcurrentToValMap& other)
 
  788   m_impl.swap (other.m_impl);
 
  793  * @brief Access the Updater instance.
 
  796 auto CONCURRENTTOVALMAP::updater() -> Updater_t&
 
  798   return m_impl.updater();
 
  803  * @brief Convert an underlying key value to this type's key value.
 
  804  * @param val The underlying key value.
 
  808 auto CONCURRENTTOVALMAP::keyAsKey (val_t val) -> key_type
 
  810   return CxxUtils::detail::UIntConv<key_type>::uintToVal (val);
 
  815  * @brief Convert this type's key value to an underlying key value.
 
  820 auto CONCURRENTTOVALMAP::keyAsVal (key_type k) -> val_t
 
  822   return CxxUtils::detail::UIntConv<key_type>::valToUInt (k);
 
  827  * @brief Convert an underlying mapped value a pointer to
 
  828  *        this type's mapped value.
 
  829  * @param val The underlying mapped value.
 
  833 auto CONCURRENTTOVALMAP::mappedAsMapped (val_t val) -> mapped_type*
 
  835   return CxxUtils::detail::UIntConv<mapped_type*>::uintToVal (val);
 
  840  * @brief Convert this type's mapped value to an underlying mapped value.
 
  841  * @param val The mapped value.
 
  845 auto CONCURRENTTOVALMAP::mappedAsVal (mapped_type* val) -> val_t
 
  847   return CxxUtils::detail::UIntConv<mapped_type*>::valToUInt (val);
 
  852  * @brief Do a lookup in the table.
 
  853  * @param key The key to look up.
 
  855  * Returns an iterator of the underlying map pointing at the found
 
  859 auto CONCURRENTTOVALMAP::get (const key_type key) const
 
  860   -> typename Impl_t::const_iterator
 
  862   val_t kval = keyAsVal (key);
 
  863   size_t hash = m_impl.hasher() (kval);
 
  864   return m_impl.get (keyAsVal(key), hash);
 
  869  * @brief Insert an entry in the table.
 
  870  * @param key The key of the new item to add.
 
  871  * @param val The new mapped value to add.
 
  872  * @param ctx Execution context.
 
  874  * The first element in the returned pair is an iterator referencing
 
  875  * the added item.  The second is a flag that is true if a new element
 
  879 auto CONCURRENTTOVALMAP::put (const key_type key,
 
  880                               std::unique_ptr<mapped_type> val,
 
  881                               const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  882   -> std::pair<const_iterator, bool>
 
  884   val_t kval = keyAsVal (key);
 
  885   size_t hash = m_impl.hasher() (kval);
 
  886   auto [it, flag] = m_impl.put (kval, hash,
 
  887                                 mappedAsVal (val.get()),
 
  889   if (flag) val.release();
 
  890   return std::make_pair (const_iterator (it), flag);
 
  894 #undef T_CONCURRENTTOVALMAP
 
  895 #undef CONCURRENTTOVALMAP
 
  898 } // namespace CxxUtils