This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 5706e3ba67 libswoc: Remove legacy IntrusiveHashMap (#10104)
5706e3ba67 is described below
commit 5706e3ba675844c5b6df5b51b93e478b87d591b0
Author: Alan M. Carroll <[email protected]>
AuthorDate: Thu Jul 27 14:18:31 2023 -0500
libswoc: Remove legacy IntrusiveHashMap (#10104)
---
.../internal-libraries/index.en.rst | 1 -
.../internal-libraries/intrusive-hash-map.en.rst | 213 ------
include/tscore/IntrusiveHashMap.h | 724 ---------------------
src/tscore/CMakeLists.txt | 1 -
src/tscore/Makefile.am | 1 -
src/tscore/unit_tests/test_IntrusiveHashMap.cc | 240 -------
6 files changed, 1180 deletions(-)
diff --git a/doc/developer-guide/internal-libraries/index.en.rst
b/doc/developer-guide/internal-libraries/index.en.rst
index 32dfb332b6..73ec59e528 100644
--- a/doc/developer-guide/internal-libraries/index.en.rst
+++ b/doc/developer-guide/internal-libraries/index.en.rst
@@ -35,6 +35,5 @@ development team.
MemSpan.en
TextView.en
buffer-writer.en
- intrusive-hash-map.en
intrusive-list.en
scalar.en
diff --git a/doc/developer-guide/internal-libraries/intrusive-hash-map.en.rst
b/doc/developer-guide/internal-libraries/intrusive-hash-map.en.rst
deleted file mode 100644
index 150e9a3594..0000000000
--- a/doc/developer-guide/internal-libraries/intrusive-hash-map.en.rst
+++ /dev/null
@@ -1,213 +0,0 @@
-.. Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file distributed
with this work for
- additional information regarding copyright ownership. The ASF licenses this
file to you under the
- Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
distributed under the License
- is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
- or implied. See the License for the specific language governing permissions
and limitations under
- the License.
-
-.. include:: ../../common.defs
-
-.. _lib-intrusive-hash-map:
-.. highlight:: cpp
-.. default-domain:: cpp
-
-IntrusiveHashMap
-****************
-
-:class:`IntrusiveHashMap` provides a "hashed" or "unordered" set, using
intrusive links. It provides a
-container for elements, each of which has a :arg:`key`. A hash function is
applied to a key to
-generate a :arg:`hash id` which is used to group the elements in to buckets
for fast lookup. This
-container is a mix of :code:`std::unordered_set` and
:code:`std::unordered_map`. There is no
-separation between elements and keys, but each element can contain non-key
data.
-
-Iteration over elements is provided and is constant time.
-
-In order to optimize lookup, the container can increase the number of buckets
used. This is called
-"expansion" and when it occurs is control by the "expansion policy" of the
container. The policy can
-be to be automatic or only done explicitly.
-
-Usage
-=====
-
-To use an :class:`IntrusiveHashMap` the element must provide support for the
container. This is done
-through an associated descriptor class which provides the operations needed to
manipulate the elements
-in the container.
-
-
-Details
-=======
-
-.. class:: template < typename H > IntrusiveHashMap
-
- :tparam H: Element operations.
-
- An unordered map using a hash function. The properties of the map are
determined by types and
- operations provided by the descriptor type :arg:`H`. The following types
are derived from :arg:`H`
- and defined in the container type.
-
- .. type:: value_type
-
- The type of elements in the container, deduced from the return types of
the link accessor methods
- in :arg:`H`.
-
- .. type:: key_type
-
- The type of the key used for hash computations. Deduced from the return
type of the key
- accessor. An instance of this type is never default constructed nor
modified, therefore it can
- be a reference if the key type is expensive to copy.
-
- .. type:: hash_id
-
- The type of the hash of a :type:`key_type`. Deduced from the return type
of the hash function.
- This must be a numeric type.
-
- :arg:`H`
- This describes the hash map, primarily via the operations required for
the map. The related types are deduced
- from the function return types. This is designed to be compatible with
:class:`IntrusiveDList`.
-
- .. function:: static key_type key_of(value_type * v)
-
- Key accessor - return the key of the element :arg:`v`.
-
- .. function:: static hash_id hash_of(key_type key)
-
- Hash function - compute the hash value of the :arg:`key`.
-
- .. function:: static bool equal(key_type lhs, key_type rhs)
-
- Key comparison - two keys are equal if this function returns
:code:`true`.
-
- .. function:: static IntrusiveHashMap::value_type * &
next_ptr(IntrusiveHashMap::value_type * v)
-
- Return a reference to the next element pointer embedded in the
element :arg:`v`.
-
- .. function:: static IntrusiveHashMap::value_type * &
prev_ptr(IntrusiveHashMap::value_type * v)
-
- Return a reference to the previous element pointer embedded in the
element :arg:`v`.
-
- .. type:: iterator
-
- An STL compliant iterator over elements in the container.
-
- .. type:: range
-
- An STL compliant half open range of elements, represented by a pair of
iterators.
-
- .. function:: IntrusiveHashMap & insert(value_type * v)
-
- Insert the element :arg:`v` into the container. If there are already
elements with the same
- key, :arg:`v` is inserted after these elements.
-
- There is no :code:`emplace` because :arg:`v` is put in the container,
not a copy of :arg:`v`.
- For the same reason :arg:`v` must be constructed before calling this
method, the container
- will never create an element instance.
-
- .. function:: iterator begin()
-
- Return an iterator to the first element in the container.
-
- .. function:: iterator end()
-
- Return an iterator to past the last element in the container.
-
- .. function:: iterator find(value_type * v)
-
- Search for :arg:`v` in the container. If found, return an iterator
referring to :arg:`v`. If not
- return the end iterator. This validates :arg:`v` is in the container.
-
- .. function:: range equal_range(key_type key)
-
- Find all elements with a key that is equal to :arg:`key`. The returned
value is a half open
- range starting with the first matching element to one past the last
matching element. All
- element in the range will have a key that is equal to :arg:`key`. If no
element has a matching
- key the range will be empty.
-
- .. function:: IntrusiveHashMap & erase(iterator spot)
-
- Remove the element referred to by :arg:`spot` from the container.
-
- .. function:: iterator iterator_for(value_type * v)
-
- Return an iterator for :arg:`v`. This is very fast, faster than
:func:`IntrusiveHashMap::find`
- but less safe because no validation done on :arg:`v`. If it not in the
container (either in no
- container or a different one) further iteration on the returned iterator
will go badly. It is
- useful inside range :code:`for` loops when it is guaranteed the element
is in the container.
-
- .. function:: template <typename F> IntrusiveHashMap & apply(F && f)
-
- :tparam F: A functional type with the signature :code:`void
(value_type*)`.
-
- This applies the function :arg:`f` to every element in the container in
such a way that
- modification of the element does not interfere with the iteration. The
most common use is to
- :code:`delete` the elements during cleanup. The common idiom ::
-
- for ( auto & elt : container) delete &elt;
-
- is problematic because the iteration links are in the deleted element
causing the computation
- of the next element to be a use after free. Using
:func:`IntrusiveHashMap::apply` enables safe
- cleanup. ::
-
- container.apply([](value_type & v) { delete & v; });
-
- Because the links are intrusive it is possible for other classes or the
element class to
- modify them. In such cases this method provides a safe way to invoke
such mechanisms.
-
-Design Notes
-============
-
-This is a refresh of an previously existing class, :code:`TSHahTable`. The
switch to C++ 11 and then
-C++ 17 made it possible to do much better in terms of the internal
implementation and API. The
-overall functionality is the roughly the same but with an easier API,
compatibility with
-:class:`IntrusiveDList`, improved compliance with STL container standards, and
better internal
-implementation.
-
-The biggest change is that elements are stored in a single global list rather
than per hash bucket.
-The buckets serve only as entry points in to the global list and to count the
number of elements
-per bucket. This simplifies the implementation of iteration, so that the old
:code:`Location` nested
-class can be removed. Elements with equal keys can be handled in the same way
as with STL
-containers, via iterator ranges, instead of a custom pseudo-iterator class.
-
-Notes on :func:`IntrusiveHashMap::apply`
-----------------------------------------
-
-This was added after some experience with use of the container. Initially it
was added to make
-cleaning up the container easier. Without it, cleanup looks like ::
-
- for ( auto spot = map.begin(), limit = map.end() ; spot != limit ; delete
&( * spot++)) {
- ; // empty
- }
-
-Instead one can do ::
-
- map.apply([](value_type& v) { delete &v; });
-
-The post increment operator guarantees that :arg:`spot` has been updated
before the current element is destroyed.
-However, it turns out to be handy in other map modifying operations. In the
unit tests there is
-this code
-
-.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveHashMap.cc
- :lines: 129-132
-
-This removes all elements that do not have the payload "dup". As another
design note,
-:func:`IntrusiveHashMap::iterator_for` here serves to bypass validation
checking on the target for
-:func:`IntrusiveHashMap::erase`, which is proper because
:func:`IntrusiveHashMap::apply` guarantees
-:arg:`thing` is in the map.
-
-Without :code:`apply` this is needed ::
-
- auto idx = map.begin();
- while (idx != map.end()) {
- auto x{idx++};
- if ("dup"sv != x->_payload) {
- map.erase(x);
- }
- }
-
-The latter is more verbose and more importantly less obvious, depending on a
subtle interaction with
-post increment.
diff --git a/include/tscore/IntrusiveHashMap.h
b/include/tscore/IntrusiveHashMap.h
deleted file mode 100644
index bbf90a77cc..0000000000
--- a/include/tscore/IntrusiveHashMap.h
+++ /dev/null
@@ -1,724 +0,0 @@
-/** @file
-
- Instrusive hash map.
-
- @section license License
-
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-#pragma once
-
-#include <vector>
-#include <array>
-#include <algorithm>
-#include "tscpp/util/IntrusiveDList.h"
-
-/** Intrusive Hash Table.
-
- Values stored in this container are not destroyed when the container is
destroyed or removed from the container.
- They must be released by the client.
-
- Duplicate keys are allowed. Clients must walk the list for multiple
entries.
- @see @c Location::operator++()
-
- By default the table automatically expands to limit the average chain
length. This can be tuned. If set
- to @c MANUAL then the table will expand @b only when explicitly requested
to do so by the client.
- @see @c ExpansionPolicy
- @see @c setExpansionPolicy()
- @see @c setExpansionLimit()
- @see @c expand()
-
- The hash table is configured by a descriptor class. This must contain the
following members
-
- - The static method <tt>key_type key_of(value_type *)</tt> which returns
the key for an instance of @c value_type.
-
- - The static method <tt>bool equal(key_type lhs, key_type rhs)</tt> which
checks if two instances of @c Key are the same.
-
- - The static method <tt>hash_id hash_of(key_type)</tt> which computes the
hash value of the key. @c ID must a numeric type.
-
- - The static method <tt>value_type *& next_ptr(value_type *)</tt> which
returns a reference to a forward pointer.
-
- - The static method <tt>value_type *& prev_ptr(value_type *)</tt> which
returns a reference to a backwards pointer.
-
- These are the required members, it is permitted to have other methods (if
the descriptor is used for other purposes)
- or to provide overloads of the methods. Note this is compatible with @c
IntrusiveDList.
-
- Several internal types are deduced from these arguments.
-
- @a Key is the return type of @a key_of and represents the key that
distinguishes instances of @a value_type. Two
- instances of @c value_type are considered the same if their respective @c
Key values are @c equal. @c Key is
- presumed cheap to copy. If the underlying key is not a simple type then @a
Key should be a constant pointer or a
- constant reference. The hash table will never attempt to modify a key.
-
- @a ID The numeric type that is the hash value for an instance of @a Key.
-
- Example for @c Http1ServerSession keyed by the origin server IP address.
-
- @code
- struct Descriptor {
- static sockaddr const* key_of(Http1ServerSession const* value) { return
&value->ip.sa }
- static bool equal(sockaddr const* lhs, sockaddr const* rhs) { return
ats_ip_eq(lhs, rhs); }
- static uint32_t hash_of(sockaddr const* key) { return ats_ip_hash(key); }
- static Http1ServerSession *& next_ptr(Http1ServerSession * ssn) { return
ssn->_next; }
- static Http1ServerSession *& prev_ptr(Http1ServerSession * ssn) { return
ssn->_prev; }
- };
- using Table = IntrusiveHashMap<Descriptor>;
- @endcode
-
- */
-template <typename H> class IntrusiveHashMap
-{
- using self_type = IntrusiveHashMap;
-
-public:
- /// Type of elements in the map.
- using value_type = typename std::remove_pointer<typename
std::remove_reference<decltype(H::next_ptr(nullptr))>::type>::type;
- /// Key type for the elements.
- using key_type = decltype(H::key_of(static_cast<value_type *>(nullptr)));
- /// The numeric hash ID computed from a key.
- using hash_id = decltype(H::hash_of(H::key_of(static_cast<value_type
*>(nullptr))));
-
- /// When the hash table is expanded.
- enum ExpansionPolicy {
- MANUAL, ///< Client must explicitly expand the table.
- AVERAGE, ///< Table expands if average chain length exceeds limit.
[default]
- MAXIMUM ///< Table expands if any chain length exceeds limit.
- };
-
-protected:
- /** List of elements.
- * All table elements are in this list. The buckets reference their starting
element in the list, or nothing if
- * no elements are in that bucket.
- */
- using List = ts::IntrusiveDList<H>;
-
- /// A bucket for the hash map.
- struct Bucket {
- /// Support for ts::IntrusiveDList<Bucket::Linkage>, definitions and link
storage.
- struct Linkage {
- static Bucket *&next_ptr(Bucket *b); ///< Access next pointer.
- static Bucket *&prev_ptr(Bucket *b); ///< Access prev pointer.
- Bucket *_prev{nullptr}; ///< Prev pointer.
- Bucket *_next{nullptr}; ///< Next pointer.
- } _link;
-
- value_type *_v{nullptr}; ///< First element in the bucket.
- size_t _count{0}; ///< Number of elements in the bucket.
-
- /** Marker for the chain having different keys.
-
- This is used to determine if expanding the hash map would be useful -
buckets that are not mixed
- will not be changed by an expansion.
- */
- bool _mixed_p{false};
-
- /// Compute the limit value for iteration in this bucket.
- /// This is the value of the next bucket, or @c nullptr if no next bucket.
- value_type *limit() const;
-
- /// Verify @a v is in this bucket.
- bool contains(value_type *v) const;
-
- void clear(); ///< Reset to initial state.
- };
-
-public:
- /// The default starting number of buckets.
- static size_t constexpr DEFAULT_BUCKET_COUNT = 7; ///< POOMA.
- /// The default expansion policy limit.
- static size_t constexpr DEFAULT_EXPANSION_LIMIT = 4; ///< Value from
previous version.
- /// Expansion policy if not specified in constructor.
- static ExpansionPolicy constexpr DEFAULT_EXPANSION_POLICY = AVERAGE;
-
- using iterator = typename List::iterator;
- using const_iterator = typename List::const_iterator;
-
- /// A range of elements in the map.
- /// It is a half open range, [first, last) in the usual STL style.
- /// @internal I tried @c std::pair as a base for this but was unable to get
STL container operations to work.
- struct range : public std::pair<iterator, iterator> {
- using super_type = std::pair<iterator, iterator>; ///< Super type.
- using super_type::super_type; ///< Use super type
constructors.
-
- // These methods enable treating the range as a view in to the hash map.
-
- /// Return @a first
- iterator const &begin() const;
- /// Return @a last
- iterator const &end() const;
- };
-
- /// A range of constant elements in the map.
- struct const_range : public std::pair<const_iterator, const_iterator> {
- using super_type = std::pair<const_iterator, const_iterator>; ///< Super
type.
-
- /// Allow implicit conversion of range to const_range.
- const_range(range const &r);
-
- using super_type::super_type; ///< Use super type constructors.
-
- // These methods enable treating the range as a view in to the hash map.
-
- /// Return @a first
- const_iterator const &begin() const;
- /// Return @a last
- const_iterator const &end() const;
- };
-
- /// Construct, starting with @n buckets.
- /// This doubles as the default constructor.
- IntrusiveHashMap(size_t n = DEFAULT_BUCKET_COUNT);
-
- /** Remove all values from the table.
-
- The values are not cleaned up. The values are not touched in this
method, therefore it is safe
- to destroy them first and then @c clear this table.
- */
- self_type &clear();
-
- iterator begin(); ///< First element.
- const_iterator begin() const; ///< First element.
- iterator end(); ///< Past last element.
- const_iterator end() const; ///< Past last element.
-
- /** Insert a value in to the table.
- The @a value must @b NOT already be in a table of this type.
- @note The value itself is put in the table, @b not a copy.
- */
- void insert(value_type *v);
-
- /** Find an element with a key equal to @a key.
-
- @return A element with a matching key, or the end iterator if not found.
- */
- const_iterator find(key_type key) const;
- iterator find(key_type key);
-
- /** Get an iterator for an existing value @a v.
-
- @return An iterator that references @a v, or the end iterator if @a v is
not in the table.
- */
- const_iterator find(value_type const *v) const;
- iterator find(value_type *v);
-
- /** Find the range of objects with keys equal to @a key.
-
- @return A iterator pair of [first, last) items with equal keys.
- */
- const_range equal_range(key_type key) const;
- range equal_range(key_type key);
-
- /** Get an @c iterator for the value @a v.
-
- This is a bit obscure but needed in certain cases. Because the interface
assumes iterators are always valid
- this avoid containment checks, which is useful if @a v is already known
to be in the container.
- */
- iterator iterator_for(value_type *v);
- const_iterator iterator_for(const value_type *v) const;
-
- /** Remove the value at @a loc from the table.
-
- @note This does @b not clean up the removed elements. Use carefully to
avoid leaks.
-
- @return An iterator the next value past @a loc. [STL compatibility]
- */
- iterator erase(iterator const &loc);
-
- /// Remove all elements in the @c range @a r.
- iterator erase(range const &r);
-
- /// Remove all elements in the range (start, limit]
- iterator erase(iterator const &start, iterator const &limit);
-
- /// Remove a @a value from the container.
- /// @a value is checked for being a member of the container.
- /// @return @c true if @a value was in the container and removed, @c false
if it was not in the container.
- bool erase(value_type *value);
-
- /** Apply @a F(value_type&) to every element in the hash map.
- *
- * This is similar to a range for loop except the iteration is done in a way
where destruction or alternation of
- * the element does @b not affect the iterator. Primarily this is useful for
@c delete to clean up the elements
- * but it can have other uses.
- *
- * @tparam F A functional object of the form <tt>void F(value_type&)</tt>
- * @param f The function to apply.
- * @return
- */
- template <typename F> self_type &apply(F &&f);
-
- /** Expand the hash if needed.
-
- Useful primarily when the expansion policy is set to @c MANUAL.
- */
- void expand();
-
- /// Number of elements in the map.
- size_t count() const;
-
- /// Number of buckets in the array.
- size_t bucket_count() const;
-
- /// Set the expansion policy to @a policy.
- self_type &set_expansion_policy(ExpansionPolicy policy);
-
- /// Get the current expansion policy
- ExpansionPolicy get_expansion_policy() const;
-
- /// Set the limit value for the expansion policy.
- self_type &set_expansion_limit(size_t n);
-
- /// Set the limit value for the expansion policy.
- size_t get_expansion_limit() const;
-
-protected:
- /// The type of storage for the buckets.
- using Table = std::vector<Bucket>;
-
- List _list; ///< Elements in the table.
- Table _table; ///< Array of buckets.
-
- /// List of non-empty buckets.
- ts::IntrusiveDList<typename Bucket::Linkage> _active_buckets;
-
- Bucket *bucket_for(key_type key);
-
- ExpansionPolicy _expansion_policy{DEFAULT_EXPANSION_POLICY}; ///< When to
expand the table.
- size_t _expansion_limit{DEFAULT_EXPANSION_LIMIT}; ///< Limit
value for expansion.
-
- // noncopyable
- IntrusiveHashMap(const IntrusiveHashMap &) = delete;
- IntrusiveHashMap &operator=(const IntrusiveHashMap &) = delete;
-
- // Hash table size prime list.
- static constexpr std::array<size_t, 29> PRIME = {
- {1, 3, 7, 13, 31, 61, 127, 251, 509, 1021,
- 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573,
- 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689,
268435399, 536870909}
- };
-};
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::Bucket::Linkage::next_ptr(Bucket *b) -> Bucket *&
-{
- return b->_link._next;
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::Bucket::Linkage::prev_ptr(Bucket *b) -> Bucket *&
-{
- return b->_link._prev;
-}
-
-// This is designed so that if the bucket is empty, then @c nullptr is
returned, which will immediately terminate
-// a search loop on an empty bucket because that will start with a nullptr
candidate, matching the limit.
-template <typename H>
-auto
-IntrusiveHashMap<H>::Bucket::limit() const -> value_type *
-{
- Bucket *n{_link._next};
- return n ? n->_v : nullptr;
-};
-
-template <typename H>
-void
-IntrusiveHashMap<H>::Bucket::clear()
-{
- _v = nullptr;
- _count = 0;
- _mixed_p = false;
- // These can be left set during an expansion, when the bucket did have
elements before but not
- // after. Therefore make sure they are cleared.
- _link._next = _link._prev = nullptr;
-}
-
-template <typename H>
-bool
-IntrusiveHashMap<H>::Bucket::contains(value_type *v) const
-{
- value_type *x = _v;
- value_type *limit = this->limit();
- while (x != limit && x != v) {
- x = H::next_ptr(x);
- }
- return x == v;
-}
-
-// ---------------------
-template <typename H>
-auto
-IntrusiveHashMap<H>::range::begin() const -> iterator const &
-{
- return super_type::first;
-}
-template <typename H>
-auto
-IntrusiveHashMap<H>::range::end() const -> iterator const &
-{
- return super_type::second;
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::const_range::begin() const -> const_iterator const &
-{
- return super_type::first;
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::const_range::end() const -> const_iterator const &
-{
- return super_type::second;
-}
-
-// ---------------------
-
-template <typename H> IntrusiveHashMap<H>::IntrusiveHashMap(size_t n)
-{
- if (n) {
- _table.resize(*std::lower_bound(PRIME.begin(), PRIME.end(), n));
- }
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::bucket_for(key_type key) -> Bucket *
-{
- return &_table[H::hash_of(key) % _table.size()];
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::begin() -> iterator
-{
- return _list.begin();
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::begin() const -> const_iterator
-{
- return _list.begin();
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::end() -> iterator
-{
- return _list.end();
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::end() const -> const_iterator
-{
- return _list.end();
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::clear() -> self_type &
-{
- for (auto &b : _table) {
- b.clear();
- }
- // Clear container data.
- _list.clear();
- _active_buckets.clear();
- return *this;
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::find(key_type key) -> iterator
-{
- Bucket *b = this->bucket_for(key);
- value_type *v = b->_v;
- value_type *limit = b->limit();
- while (v != limit && !H::equal(key, H::key_of(v))) {
- v = H::next_ptr(v);
- }
- return v == limit ? _list.end() : _list.iterator_for(v);
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::find(key_type key) const -> const_iterator
-{
- return const_cast<self_type *>(this)->find(key);
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::equal_range(key_type key) -> range
-{
- iterator first{this->find(key)};
- iterator last{first};
- iterator limit{this->end()};
-
- while (last != limit && H::equal(key, H::key_of(&*last))) {
- ++last;
- }
-
- return range{first, last};
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::equal_range(key_type key) const -> const_range
-{
- return const_cast<self_type *>(this)->equal_range(key);
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::iterator_for(const value_type *v) const -> const_iterator
-{
- return _list.iterator_for(v);
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::iterator_for(value_type *v) -> iterator
-{
- return _list.iterator_for(v);
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::find(value_type *v) -> iterator
-{
- Bucket *b = this->bucket_for(H::key_of(v));
- return b->contains(v) ? _list.iterator_for(v) : this->end();
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::find(value_type const *v) const -> const_iterator
-{
- return const_cast<self_type *>(this)->find(const_cast<value_type *>(v));
-}
-
-template <typename H>
-void
-IntrusiveHashMap<H>::insert(value_type *v)
-{
- auto key = H::key_of(v);
- Bucket *bucket = this->bucket_for(key);
- value_type *spot = bucket->_v;
- bool mixed_p = false; // Found a different key in the bucket.
-
- if (nullptr == spot) { // currently empty bucket, set it and add to active
list.
- _list.append(v);
- bucket->_v = v;
- _active_buckets.append(bucket);
- } else {
- value_type *limit = bucket->limit();
-
- // First search the bucket to see if the key is already in it.
- while (spot != limit && !H::equal(key, H::key_of(spot))) {
- spot = H::next_ptr(spot);
- }
- if (spot != bucket->_v) {
- mixed_p = true; // found some other key, it's going to be mixed.
- }
- _list.insert_before(spot, v);
- if (spot == bucket->_v) { // added before the bucket start, update the
start.
- bucket->_v = v;
- }
- bucket->_mixed_p = mixed_p;
- }
- ++bucket->_count;
-
- // auto expand if appropriate.
- if ((AVERAGE == _expansion_policy && (_list.count() / _table.size()) >
_expansion_limit) ||
- (MAXIMUM == _expansion_policy && bucket->_count > _expansion_limit &&
bucket->_mixed_p)) {
- this->expand();
- }
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::erase(iterator const &loc) -> iterator
-{
- value_type *v = loc;
- iterator zret = ++(this->iterator_for(v)); // get around no
const_iterator -> iterator.
- Bucket *b = this->bucket_for(H::key_of(v));
- value_type *nv = H::next_ptr(v);
- value_type *limit = b->limit();
- if (b->_v == v) { // removed first element in bucket, update bucket
- if (limit == nv) { // that was also the only element, deactivate bucket
- _active_buckets.erase(b);
- b->clear();
- } else {
- b->_v = nv;
- --b->_count;
- }
- }
- _list.erase(loc);
- return zret;
-}
-
-template <typename H>
-bool
-IntrusiveHashMap<H>::erase(value_type *v)
-{
- auto loc = this->iterator_for(v);
- if (loc != this->end()) {
- this->erase(loc);
- return true;
- }
- return false;
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::erase(iterator const &start, iterator const &limit) ->
iterator
-{
- auto spot{start};
- Bucket *bucket{this->bucket_for(spot)};
- while (spot != limit) {
- auto target = bucket;
- bucket = bucket->_link._next; // bump now to avoid forward
iteration problems in case of bucket removal.
- value_type *v_limit = bucket ? bucket->_v : nullptr;
- while (spot != v_limit && spot != limit) {
- --(target->_count);
- ++spot;
- }
- if (target->_count == 0) {
- _active_buckets.erase(target);
- }
- }
- _list.erase(start, limit);
- return _list.iterator_for(limit); // convert from const_iterator back to
iterator
-};
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::erase(range const &r) -> iterator
-{
- return this->erase(r.first, r.second);
-}
-
-namespace detail
-{
-// Make @c apply more convenient by allowing the function to take a reference
type or pointer type to the container
-// elements. The pointer type is the base, plus a shim to convert from a
reference type functor to a pointer pointer
-// type. The complex return type definition forces only one, but not both, to
be valid for a particular functor. This
-// also must be done via free functions and not method overloads because the
compiler forces a match up of method
-// definitions and declarations before any template instantiation.
-
-template <typename H, typename F>
-auto
-IntrusiveHashMapApply(IntrusiveHashMap<H> &map, F &&f)
- -> decltype(f(*static_cast<typename IntrusiveHashMap<H>::value_type
*>(nullptr)), map)
-{
- return map.apply([&f](typename IntrusiveHashMap<H>::value_type *v) { return
f(*v); });
-}
-
-template <typename H, typename F>
-auto
-IntrusiveHashMapApply(IntrusiveHashMap<H> &map, F &&f)
- -> decltype(f(static_cast<typename IntrusiveHashMap<H>::value_type
*>(nullptr)), map)
-{
- auto spot{map.begin()};
- auto limit{map.end()};
- while (spot != limit) {
- f(spot++); // post increment means @a spot is updated before @a f is
applied.
- }
- return map;
-}
-} // namespace detail
-
-template <typename H>
-template <typename F>
-auto
-IntrusiveHashMap<H>::apply(F &&f) -> self_type &
-{
- return detail::IntrusiveHashMapApply(*this, f);
-};
-
-template <typename H>
-void
-IntrusiveHashMap<H>::expand()
-{
- ExpansionPolicy org_expansion_policy = _expansion_policy; // save for
restore.
- value_type *old = _list.head(); // save for
repopulating.
- auto old_size = _table.size();
-
- // Reset to empty state.
- this->clear();
- _table.resize(*std::lower_bound(PRIME.begin(), PRIME.end(), old_size + 1));
-
- _expansion_policy = MANUAL; // disable any auto expand while we're expanding.
- while (old) {
- value_type *v = old;
- old = H::next_ptr(old);
- this->insert(v);
- }
- // stashed array gets cleaned up when @a tmp goes out of scope.
- _expansion_policy = org_expansion_policy; // reset to original value.
-}
-
-template <typename H>
-size_t
-IntrusiveHashMap<H>::count() const
-{
- return _list.count();
-}
-
-template <typename H>
-size_t
-IntrusiveHashMap<H>::bucket_count() const
-{
- return _table.size();
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::set_expansion_policy(ExpansionPolicy policy) -> self_type
&
-{
- _expansion_policy = policy;
- return *this;
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::get_expansion_policy() const -> ExpansionPolicy
-{
- return _expansion_policy;
-}
-
-template <typename H>
-auto
-IntrusiveHashMap<H>::set_expansion_limit(size_t n) -> self_type &
-{
- _expansion_limit = n;
- return *this;
-}
-
-template <typename H>
-size_t
-IntrusiveHashMap<H>::get_expansion_limit() const
-{
- return _expansion_limit;
-}
-/*
----------------------------------------------------------------------------------------------
*/
diff --git a/src/tscore/CMakeLists.txt b/src/tscore/CMakeLists.txt
index 24772dcda4..d1e25c7dcd 100644
--- a/src/tscore/CMakeLists.txt
+++ b/src/tscore/CMakeLists.txt
@@ -144,7 +144,6 @@ add_executable(test_tscore
unit_tests/test_HKDF.cc
unit_tests/test_Histogram.cc
unit_tests/test_History.cc
- unit_tests/test_IntrusiveHashMap.cc
unit_tests/test_IntrusivePtr.cc
unit_tests/test_List.cc
unit_tests/test_MMH.cc
diff --git a/src/tscore/Makefile.am b/src/tscore/Makefile.am
index 1a49ca5d07..707be7af7e 100644
--- a/src/tscore/Makefile.am
+++ b/src/tscore/Makefile.am
@@ -162,7 +162,6 @@ test_tscore_SOURCES = \
unit_tests/test_History.cc \
unit_tests/test_ink_inet.cc \
unit_tests/test_ink_memory.cc \
- unit_tests/test_IntrusiveHashMap.cc \
unit_tests/test_IntrusivePtr.cc \
unit_tests/test_layout.cc \
unit_tests/test_List.cc \
diff --git a/src/tscore/unit_tests/test_IntrusiveHashMap.cc
b/src/tscore/unit_tests/test_IntrusiveHashMap.cc
deleted file mode 100644
index 6cda72128d..0000000000
--- a/src/tscore/unit_tests/test_IntrusiveHashMap.cc
+++ /dev/null
@@ -1,240 +0,0 @@
-/** @file
-
- IntrusiveHashMap unit tests.
-
- @section license License
-
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-#include <iostream>
-#include <string_view>
-#include <string>
-#include <bitset>
-#include <random>
-#include <tscore/IntrusiveHashMap.h>
-#include <tscpp/util/ts_bw_format.h>
-#include "catch.hpp"
-
-// -------------
-// --- TESTS ---
-// -------------
-
-using namespace std::literals;
-
-namespace
-{
-struct Thing {
- std::string _payload;
- int _n{0};
-
- Thing(std::string_view text) : _payload(text) {}
- Thing(std::string_view text, int x) : _payload(text), _n(x) {}
-
- Thing *_next{nullptr};
- Thing *_prev{nullptr};
-};
-
-struct ThingMapDescriptor {
- static Thing *&
- next_ptr(Thing *thing)
- {
- return thing->_next;
- }
- static Thing *&
- prev_ptr(Thing *thing)
- {
- return thing->_prev;
- }
- static std::string_view
- key_of(Thing *thing)
- {
- return thing->_payload;
- }
- static constexpr std::hash<std::string_view> hasher{};
- static auto
- hash_of(std::string_view s) -> decltype(hasher(s))
- {
- return hasher(s);
- }
- static bool
- equal(std::string_view const &lhs, std::string_view const &rhs)
- {
- return lhs == rhs;
- }
-};
-
-using Map = IntrusiveHashMap<ThingMapDescriptor>;
-
-} // namespace
-
-TEST_CASE("IntrusiveHashMap", "[libts][IntrusiveHashMap]")
-{
- Map map;
- map.insert(new Thing("bob"));
- REQUIRE(map.count() == 1);
- map.insert(new Thing("dave"));
- map.insert(new Thing("persia"));
- REQUIRE(map.count() == 3);
- // Need to be bit careful cleaning up, since the link pointers are in the
objects and deleting
- // the object makes it unsafe to use an iterator referencing that object.
For a full cleanup,
- // the best option is to first delete everything, then clean up the map.
- map.apply([](Thing *thing) { delete thing; });
- map.clear();
- REQUIRE(map.count() == 0);
-
- size_t nb = map.bucket_count();
- std::bitset<64> marks;
- for (unsigned int i = 1; i <= 63; ++i) {
- std::string name;
- swoc::bwprint(name, "{} squared is {}", i, i * i);
- Thing *thing = new Thing(name);
- thing->_n = i;
- map.insert(thing);
- REQUIRE(map.count() == i);
- REQUIRE(map.find(name) != map.end());
- }
- REQUIRE(map.count() == 63);
- REQUIRE(map.bucket_count() > nb);
- for (auto &thing : map) {
- REQUIRE(0 == marks[thing._n]);
- marks[thing._n] = true;
- }
- marks[0] = true;
- REQUIRE(marks.all());
- map.insert(new Thing("dup"sv, 79));
- map.insert(new Thing("dup"sv, 80));
- map.insert(new Thing("dup"sv, 81));
-
- auto r = map.equal_range("dup"sv);
- REQUIRE(r.first != r.second);
- REQUIRE(r.first->_payload == "dup"sv);
- REQUIRE(r.first->_n == 81);
-
- Map::iterator idx;
-
- // Erase all the non-"dup" and see if the range is still correct.
- map.apply([&map](Thing *thing) {
- if (thing->_payload != "dup"sv) {
- map.erase(map.iterator_for(thing));
- delete thing;
- }
- });
- r = map.equal_range("dup"sv);
- REQUIRE(r.first != r.second);
- idx = r.first;
- REQUIRE(idx->_payload == "dup"sv);
- REQUIRE(idx->_n == 81);
- REQUIRE((++idx)->_payload == "dup"sv);
- REQUIRE(idx->_n != r.first->_n);
- REQUIRE(idx->_n == 79);
- REQUIRE((++idx)->_payload == "dup"sv);
- REQUIRE(idx->_n != r.first->_n);
- REQUIRE(idx->_n == 80);
- REQUIRE(++idx == map.end());
- // Verify only the "dup" items are left.
- for (auto &&elt : map) {
- REQUIRE(elt._payload == "dup"sv);
- }
- // clean up the last bits.
- map.apply([](Thing *thing) { delete thing; });
-};
-
-// Some more involved tests.
-TEST_CASE("IntrusiveHashMapManyStrings", "[IntrusiveHashMap]")
-{
- std::vector<std::string> strings;
-
- std::uniform_int_distribution<short> char_gen{'a', 'z'};
- std::uniform_int_distribution<short> length_gen{20, 40};
- std::minstd_rand randu;
- constexpr int N = 1009;
-
- Map ihm;
-
- strings.reserve(N);
- for (int i = 0; i < N; ++i) {
- auto len = length_gen(randu);
- std::string s;
- s.reserve(len);
- for (decltype(len) j = 0; j < len; ++j) {
- s += char_gen(randu);
- }
- strings.push_back(s);
- }
-
- // Fill the IntrusiveHashMap
- for (int i = 0; i < N; ++i) {
- ihm.insert(new Thing{strings[i], i});
- }
-
- REQUIRE(ihm.count() == N);
-
- // Do some lookups - just require the whole loop, don't artificially inflate
the test count.
- bool miss_p = false;
- for (int j = 0, idx = 17; j < N; ++j, idx = (idx + 17) % N) {
- if (auto spot = ihm.find(strings[idx]); spot == ihm.end() || spot->_n !=
idx) {
- miss_p = true;
- }
- }
- REQUIRE(miss_p == false);
-
- // Let's try some duplicates when there's a lot of data in the map.
- miss_p = false;
- for (int idx = 23; idx < N; idx += 23) {
- ihm.insert(new Thing(strings[idx], 2000 + idx));
- }
- for (int idx = 23; idx < N; idx += 23) {
- auto spot = ihm.find(strings[idx]);
- if (spot == ihm.end() || spot->_n != 2000 + idx || ++spot == ihm.end() ||
spot->_n != idx) {
- miss_p = true;
- }
- }
- REQUIRE(miss_p == false);
-
- // Do a different stepping, special cases the intersection with the previous
stepping.
- miss_p = false;
- for (int idx = 31; idx < N; idx += 31) {
- ihm.insert(new Thing(strings[idx], 3000 + idx));
- }
- for (int idx = 31; idx < N; idx += 31) {
- auto spot = ihm.find(strings[idx]);
- if (spot == ihm.end() || spot->_n != 3000 + idx || ++spot == ihm.end() ||
(idx != (23 * 31) && spot->_n != idx) ||
- (idx == (23 * 31) && spot->_n != 2000 + idx)) {
- miss_p = true;
- }
- }
- REQUIRE(miss_p == false);
-
- // Check for misses.
- miss_p = false;
- for (int i = 0; i < 99; ++i) {
- char s[41];
- auto len = length_gen(randu);
- for (decltype(len) j = 0; j < len; ++j) {
- s[j] = char_gen(randu);
- }
- std::string_view name(s, len);
- auto spot = ihm.find(name);
- if (spot != ihm.end() && name != spot->_payload) {
- miss_p = true;
- }
- }
- REQUIRE(miss_p == false);
-
- ihm.apply([](Thing *thing) { delete thing; });
-};