diff --git a/doc/developer-guide/internal-libraries/MemArena.en.rst b/doc/developer-guide/internal-libraries/MemArena.en.rst index ea5bc2f319d..47f0f3f00e1 100644 --- a/doc/developer-guide/internal-libraries/MemArena.en.rst +++ b/doc/developer-guide/internal-libraries/MemArena.en.rst @@ -76,14 +76,14 @@ to not be an issue, or there must be a provision for some sort of garbage collec Generally |MemArena| is not as useful for classes that allocate their own internal memory (such as :code:`std::string` or :code:`std::vector`), which includes most container classes. One -container class that can be easily used is :class:`IntrusiveDList` because the links are in the +container class that can be easily used is :code:`swoc::IntrusiveDList` because the links are in the instance and therefore also in the arena. Objects created in the arena must not have :code:`delete` called on them as this will corrupt memory, usually leading to an immediate crash. The memory for the instance will be released when the arena is destroyed. The destructor can be called if needed but in general if a destructor is needed it is probably not a class that should be constructed in the arena. Looking at -:class:`IntrusiveDList` again for an example, if this is used to link objects in the arena, there is +:code`:`swoc::IntrusiveDList` again for an example, if this is used to link objects in the arena, there is no need for a destructor to clean up the links - all of the objects will be de-allocated when the arena is destroyed. Whether this kind of situation can be arranged with reasonable effort is a good heuristic on whether |MemArena| is an appropriate choice. diff --git a/doc/developer-guide/internal-libraries/index.en.rst b/doc/developer-guide/internal-libraries/index.en.rst index 73ec59e528b..45f808280d1 100644 --- a/doc/developer-guide/internal-libraries/index.en.rst +++ b/doc/developer-guide/internal-libraries/index.en.rst @@ -35,5 +35,4 @@ development team. MemSpan.en TextView.en buffer-writer.en - intrusive-list.en scalar.en diff --git a/doc/developer-guide/internal-libraries/intrusive-list.en.rst b/doc/developer-guide/internal-libraries/intrusive-list.en.rst deleted file mode 100644 index 75ab3b1d013..00000000000 --- a/doc/developer-guide/internal-libraries/intrusive-list.en.rst +++ /dev/null @@ -1,234 +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-list: -.. highlight:: cpp -.. default-domain:: cpp - -IntrusiveDList -************** - -:class:`IntrusiveDList` is a class that provides a double linked list using pointers embedded in the -object. :class:`IntrusiveDList` also acts as a queue. No memory management is done - objects can be -added to and removed from the list but the allocation and deallocation of the objects must be -handled outside the class. This class supports an STL compliant bidirectional iteration. The -iterators automatically convert to pointer as in normal use of this class the contained elements -will be referenced by pointers. - -Definition -========== - -.. class:: template < typename L > IntrusiveDList - - A double linked list / queue based on links inside the objects. The element type, :code:`T`, is - deduced from the return type of the link accessor methods in :arg:`L`. - - :tparam L: List item descriptor - - The descriptor, :arg:`L`, is a type that provides the operations on list elements required by - the container. - - .. type:: value_type - - The type of elements in the container, deduced from the return types of the link accessor methods - in :arg:`L`. - - :arg:`L` - .. function:: static value_type * & next_ptr(value_type * elt) - - Return a reference to the next element pointer embedded in the element :arg:`elt`. - - .. function:: static value_type * & prev_ptr(value_type * elt) - - Return a reference to the previous element pointer embedded in the element :arg:`elt`. - - .. type:: iterator - - An STL compliant bidirectional iterator on elements in the list. :type:`iterator` has a user - defined conversion to :code:`value_type *` for convenience in use. - - .. type:: const_iterator - - An STL compliant bidirectional constant iterator on elements in the list. :type:`const_iterator` has a user - defined conversion to :code:`const value_type *` for convenience in use. - - .. function:: value_type * head() - - Return a pointer to the head element in the list. This may be :code:`nullptr` if the list is empty. - - .. function:: value_type * tail() - - Return a pointer to the tail element in the list. This may be :code:`nullptr` if the list is empty. - - .. function:: IntrusiveDList & clear() - - Remove all elements from the list. This only removes, no deallocation nor destruction is performed. - - .. function:: size_t count() const - - Return the number of elements in the list. - - .. function:: IntrusiveDList & append(value_type * elt) - - Append :arg:`elt` to the list. - - .. function:: IntrusiveDList & prepend(value_type * elt) - - Prepend :arg:`elt` to the list. - - .. function:: value_type * take_head() - - Remove the head element and return a pointer to it. May be :code:`nullptr` if the list is empty. - - .. function:: value_type * take_tail() - - Remove the tail element and return a pointer to it. May be :code:`nullptr` if the list is empty. - - .. function:: iterator erase(const iterator & loc) - - Remove the element at :arg:`loc`. Return the element after :arg:`loc`. - - .. function:: iterator erase(const iterator & start, const iterator & limit) - - Remove the elements in the half open range from and including :arg:`start` - to but not including :arg:`limit`. - - .. function:: iterator iterator_for(value_type * value) - - Return an :type:`iterator` that refers to :arg:`value`. :arg:`value` is checked for being in a - list but there is no guarantee it is in this list. If :arg:`value` is not in a list then the - end iterator is returned. - - .. function:: const_iterator iterator_for(const value_type * value) - - Return a :type:`const_iterator` that refers to :arg:`value`. :arg:`value` is checked for being - in a list but there is no guarantee it is in this list. If :arg:`value` is not in a list then - the end iterator is returned. - -Usage -===== - -An instance of :class:`IntrusiveDList` acts as a container for items, maintaining a doubly linked -list / queue of the objects and tracking the number of objects in the container. There are methods -for appending, prepending, and inserting (both before and after a specific element already in the -list). Some care must be taken because it is too expensive to check for an element already being in -the list or in another list. The internal links are set to :code:`nullptr`, therefore one simple check -for being in a list is if either internal link is not :code:`nullptr`. This requires initializing the -internal links to :code:`nullptr`. - -Examples -======== - -In this example the goal is to have a list of :code:`Message` objects. First the class is declared -along with the internal linkage support. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 38-63 - -The struct :code:`Linkage` is used both to provide the descriptor to :class:`IntrusiveDList` and to -contain the link pointers. This isn't necessary - the links could have been direct members -and the implementation of the link accessor methods adjusted. Because the links are intended to be -used only by a specific container class (:code:`Container`) the struct is made protected. - -The implementation of the link accessor methods. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 65-74 - -A method to check if the message is in a list. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 76-80 - -The container class for the messages could be implemented as - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 82-99 - -The :code:`debug` method takes a format string (:arg:`fmt`) and an arbitrary set of arguments, formats -the arguments in to the string, and adds the new message to the list. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 122-131 - -The :code:`print` method demonstrates the use of the range :code:`for` loop on a list. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 142-148 - -The maximum severity level can also be computed even more easily using :code:`std::max_element`. -This find the element with the maximum severity and returns that severity, or :code:`LVL_DEBUG` if -no element is found (which happens if the list is empty). - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 134-140 - -Other methods for the various severity levels would be implemented in a similar fashion. Because the -intrusive list does not do memory management, the container must clean that up itself, as in the -:code:`clear` method. A bit of care must be exercised because the links are in the elements, and -these links are used for iteration therefore using an iterator that references a deleted object is -risky. One approach, illustrated here, is to use :func:`IntrusiveDList::take_head` to remove the -element before destroying it. Another option is to allocation the elements in a :class:`MemArena` to -avoid the need for any explicit cleanup. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 106-114 - -In some cases the elements of the list are subclasses and the links are declared in a super class -and are therefore of the super class type. For instance, in the unit test a class :code:`Thing` is -defined for testing. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 159 - -Later on, to validate use on a subclass, :code:`PrivateThing` is defined as a subclass of -:code:`Thing`. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 181 - -However, the link members :code:`_next` and :code:`_prev` are of type :code:`Thing*` but the -descriptor for a list of :code:`PrivateThing` must have link accessors that return -:code:`PrivateThing *&`. To make this easier a conversion template function is provided, -:code:`ts::ptr_ref_cast` that converts a member of type :code:`T*` to a reference to a pointer -to :code:`X`, e.g. :code:`X*&`. This is used in the setup for testing :code:`PrivateThing`. - -.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc - :lines: 190-199 - -While this can be done directly with :code:`reinterpret_cast<>`, use of :code:`ts::ptr_cast` avoids -typographic errors and warnings about type punning caused by :code:`-fstrict-aliasing`. - -Design Notes -============ - -The historic goal of this class is to replace the :code:`DLL` list support. The benefits of this are - -* Remove dependency on the C preprocessor. - -* Provide greater flexibility in the internal link members. Because of the use of the descriptor - and its static methods, the links can be anywhere in the object, including in nested structures - or super classes. The links are declared like normal members and do not require specific macros. - -* Provide STL compliant iteration. This makes the class easier to use in general and particularly - in the case of range :code:`for` loops. - -* Track the number of items in the list. - -* Provide queue support, which is of such low marginal expense there is, IMHO, no point in - providing a separate class for it. - - - diff --git a/include/tscpp/util/IntrusiveDList.h b/include/tscpp/util/IntrusiveDList.h deleted file mode 100644 index 4b00dc30f7f..00000000000 --- a/include/tscpp/util/IntrusiveDList.h +++ /dev/null @@ -1,890 +0,0 @@ -/** @file - - Intrusive double linked list container. - - This provides support for a doubly linked list container. Items in the list must provide links - inside the class and accessor functions for those links. - - @note This is a header only library. - - @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 - -/// Clang doesn't like just declaring the tag struct we need so we have to include the file. -#include -#include - -namespace ts -{ -/** Intrusive doubly linked list container. - - This holds items in a doubly linked list using links in the items. Items are placed in the list - by changing the pointers. An item can be in only one list for a set of links, but an item can - contain multiple sets of links. This requires different specializations of this template because - link access is part of the type specification. Memory for items is not managed by this class - - instances must be allocated and released elsewhere. In particular removing an item from the list - does not destruct or free the item. - - Access to the links is described by a linkage class which is required to contain the following - members: - - - The static method @c next_ptr which returns a reference to the pointer to the next item. - - - The static method @c prev_ptr which returns a reference to the pointer to the previous item. - - The pointer methods take a single argument of @c Item* and must return a reference to a pointer - instance. This type is deduced from the methods and is not explicitly specified. It must be - cheaply copyable and stateless. - - It is the responsibility of the item class to initialize the link pointers. When an item is - removed from the list the link pointers are set to @c nullptr. - - An example declaration would be - - @code - // Item in the list. - struct Thing { - Thing* _next {nullptr}; - Thing* _prev {nullptr}; - Data _payload; - - // Linkage descriptor. - struct Linkage { - static Thing*& next_ptr(Thing* Thing) { return Thing->_next; } - static Thing*& prev_ptr(Thing* Thing) { return Thing->_prev; } - }; - }; - - using ThingList = ts::IntrusiveDList; - @endcode - - Item access is done by using either STL style iteration, or direct access to the member - pointers. A client can have its own mechanism for getting an element to start, or use the @c - head and/or @c tail methods to get the first and last elements in the list respectively. Note if - the list is empty then @c nullptr will be returned. There are simple and fast conversions - between item pointers and iterators. - - */ -template class IntrusiveDList -{ - friend class iterator; - -public: - using self_type = IntrusiveDList; ///< Self reference type. - /// The list item type. - using value_type = typename std::remove_pointer::type>::type; - - /// Const iterator. - class const_iterator - { - using self_type = const_iterator; ///< Self reference type. - friend class IntrusiveDList; - - public: - using list_type = IntrusiveDList; ///< Container type. - using value_type = const typename list_type::value_type; /// Import for API compliance. - // STL algorithm compliance. - using iterator_category = std::bidirectional_iterator_tag; - using pointer = value_type *; - using reference = value_type &; - using difference_type = int; - - /// Default constructor. - const_iterator(); - - /// Pre-increment. - /// Move to the next element in the list. - /// @return The iterator. - self_type &operator++(); - - /// Pre-decrement. - /// Move to the previous element in the list. - /// @return The iterator. - self_type &operator--(); - - /// Post-increment. - /// Move to the next element in the list. - /// @return The iterator value before the increment. - self_type operator++(int); - - /// Post-decrement. - /// Move to the previous element in the list. - /// @return The iterator value before the decrement. - self_type operator--(int); - - /// Dereference. - /// @return A reference to the referent. - value_type &operator*() const; - - /// Dereference. - /// @return A pointer to the referent. - value_type *operator->() const; - - /// Convenience conversion to pointer type - /// Because of how this list is normally used, being able to pass an iterator as a pointer is quite convenient. - /// If the iterator isn't valid, it converts to @c nullptr. - operator value_type *() const; - - /// Equality - bool operator==(self_type const &that) const; - - /// Inequality - bool operator!=(self_type const &that) const; - - protected: - // These are stored non-const to make implementing @c iterator easier. This class provides the required @c const - // protection. - list_type *_list{nullptr}; ///< Needed to decrement from @c end() position. - typename list_type::value_type *_v{nullptr}; ///< Referenced element. - - /// Internal constructor for containers. - const_iterator(const list_type *list, value_type *v); - }; - - /// Iterator for the list. - class iterator : public const_iterator - { - using self_type = iterator; ///< Self reference type. - using super_type = const_iterator; ///< Super class type. - - friend class IntrusiveDList; - - public: - using list_type = IntrusiveDList; /// Must hoist this for direct use. - using value_type = typename list_type::value_type; /// Import for API compliance. - // STL algorithm compliance. - using iterator_category = std::bidirectional_iterator_tag; - using pointer = value_type *; - using reference = value_type &; - - /// Default constructor. - iterator(); - - /// Pre-increment. - /// Move to the next element in the list. - /// @return The iterator. - self_type &operator++(); - - /// Pre-decrement. - /// Move to the previous element in the list. - /// @return The iterator. - self_type &operator--(); - - /// Post-increment. - /// Move to the next element in the list. - /// @return The iterator value before the increment. - self_type operator++(int); - - /// Post-decrement. - /// Move to the previous element in the list. - /// @return The iterator value before the decrement. - self_type operator--(int); - - /// Dereference. - /// @return A reference to the referent. - value_type &operator*() const; - - /// Dereference. - /// @return A pointer to the referent. - value_type *operator->() const; - - /// Convenience conversion to pointer type - /// Because of how this list is normally used, being able to pass an iterator as a pointer is quite convenient. - /// If the iterator isn't valid, it converts to @c nullptr. - operator value_type *() const; - - protected: - /// Internal constructor for containers. - iterator(list_type *list, value_type *v); - }; - - /// Construct to empty list. - IntrusiveDList() = default; - - /// Move list to @a this and leave @a that empty. - IntrusiveDList(self_type &&that); - - /// No copy assignment because items can't be in two lists and can't copy items. - self_type &operator=(const self_type &that) = delete; - /// Move @a that to @a this. - self_type &operator=(self_type &&that); - - /// Empty check. - /// @return @c true if the list is empty. - bool empty() const; - - /// Presence check (linear time). - /// @return @c true if @a v is in the list, @c false if not. - bool contains(const value_type *v) const; - - /// Add @a elt as the first element in the list. - /// @return This container. - self_type &prepend(value_type *v); - - /// Add @elt as the last element in the list. - /// @return This container. - self_type &append(value_type *v); - - /// Remove the first element of the list. - /// @return A pointer to the removed item, or @c nullptr if the list was empty. - value_type *take_head(); - - /// Remove the last element of the list. - /// @return A pointer to the removed item, or @c nullptr if the list was empty. - value_type *take_tail(); - - /// Insert a new element @a elt after @a target. - /// The caller is responsible for ensuring @a target is in this list and @a elt is not in a list. - /// @return This list. - self_type &insert_after(value_type *target, value_type *v); - - /// Insert a new element @a v before @a target. - /// The caller is responsible for ensuring @a target is in this list and @a elt is not in a list. - /// @return This list. - self_type &insert_before(value_type *target, value_type *v); - - /// Insert a new element @a elt after @a target. - /// If @a target is the end iterator, @a v is appended to the list. - /// @return This list. - self_type &insert_after(iterator const &target, value_type *v); - - /// Insert a new element @a v before @a target. - /// If @a target is the end iterator, @a v is appended to the list. - /// @return This list. - self_type &insert_before(iterator const &target, value_type *v); - - /// Take @a v out of this list. - /// @return The element after @a v. - value_type *erase(value_type *v); - - /// Take the element at @a loc out of this list. - /// @return Iterator for the next element. - iterator erase(const iterator &loc); - - /// Take elements out of the list. - /// Remove elements start with @a start up to but not including @a limit. - /// @return @a limit - iterator erase(const iterator &start, const iterator &limit); - - /// Remove all elements. - /// @note @b No memory management is done! - /// @return This container. - self_type &clear(); - - /// @return Number of elements in the list. - size_t count() const; - - /// Get an iterator to the first element. - iterator begin(); - - /// Get an iterator to the first element. - const_iterator begin() const; - - /// Get an iterator past the last element. - iterator end(); - - /// Get an iterator past the last element. - const_iterator end() const; - - /** Get an iterator for the item @a v. - * - * It is the responsibility of the caller that @a v is in the list. The purpose is to make - * iteration starting at a specific element easier (i.e. all of the link manipulation and checking - * is done by the iterator). - * - * @return An @c iterator that refers to @a v. - */ - iterator iterator_for(value_type *v); - const_iterator iterator_for(const value_type *v) const; - - /// Get the first element. - value_type *head(); - const value_type *head() const; - - /// Get the last element. - value_type *tail(); - const value_type *tail() const; - - /** Apply a functor to every element in the list. - * This iterates over the list correctly even if the functor destroys or removes elements. - */ - template self_type &apply(F &&f); - -protected: - value_type *_head{nullptr}; ///< First element in list. - value_type *_tail{nullptr}; ///< Last element in list. - size_t _count{0}; ///< # of elements in list. -}; - -/** Utility class to provide intrusive links. - * - * @tparam T Class to link. - * - * The normal use is to declare this as a member to provide the links and the linkage functions. - * @code - * class Thing { - * // blah blah - * Thing* _next{nullptr}; - * Thing* _prev{nullptr}; - * using Linkage = ts::IntrusiveLinkage; - * }; - * using ThingList = ts::IntrusiveDList; - * @endcode - * The template will default to the names '_next' and '_prev' therefore in the example it could - * have been done as - * @code - * using Linkage = ts::IntrusiveLinkage; - * @endcode - */ -template struct IntrusiveLinkage { - static T *&next_ptr(T *thing); ///< Retrieve reference to next pointer. - static T *&prev_ptr(T *thing); ///< Retrieve reference to previous pointer. -}; - -template -T *& -IntrusiveLinkage::next_ptr(T *thing) -{ - return thing->*NEXT; -} -template -T *& -IntrusiveLinkage::prev_ptr(T *thing) -{ - return thing->*PREV; -} - -/** Utility cast to change the underlying type of a pointer reference. - * - * @tparam T The resulting pointer reference type. - * @tparam P The starting pointer reference type. - * @param p A reference to pointer to @a P. - * @return A reference to the same pointer memory of type @c T*&. - * - * This changes a reference to a pointer to @a P to a reference to a pointer to @a T. This is useful - * for intrusive links that are inherited. For instance - * - * @code - * class Thing { Thing* _next; ... } - * class BetterThing : public Thing { ... }; - * @endcode - * - * To make @c BetterThing work with an intrusive container without making new link members, - * - * @code - * static BetterThing*& next_ptr(BetterThing* bt) { - * return ts::ptr_ref_cast(_next); - * } - * @endcode - * - * This is both convenient and gets around aliasing warnings from the compiler that can arise from - * using @c reinterpret_cast. - */ -template -T *& -ptr_ref_cast(P *&p) -{ - union { - P **_p; - T **_t; - } u{&p}; - return *(u._t); -}; - -// --- Implementation --- - -template ts::IntrusiveDList::const_iterator::const_iterator() {} - -template -ts::IntrusiveDList::const_iterator::const_iterator(const list_type *list, value_type *v) - : _list(const_cast(list)), _v(const_cast(v)) -{ -} - -template ts::IntrusiveDList::iterator::iterator() {} - -template ts::IntrusiveDList::iterator::iterator(IntrusiveDList *list, value_type *v) : super_type(list, v) {} - -template -auto -ts::IntrusiveDList::const_iterator::operator++() -> self_type & -{ - _v = L::next_ptr(_v); - return *this; -} - -template -auto -ts::IntrusiveDList::iterator::operator++() -> self_type & -{ - this->super_type::operator++(); - return *this; -} - -template -auto -ts::IntrusiveDList::const_iterator::operator++(int) -> self_type -{ - self_type tmp(*this); - ++*this; - return tmp; -} - -template -auto -ts::IntrusiveDList::iterator::operator++(int) -> self_type -{ - self_type tmp(*this); - ++*this; - return tmp; -} - -template -auto -ts::IntrusiveDList::const_iterator::operator--() -> self_type & -{ - if (_v) { - _v = L::prev_ptr(_v); - } else if (_list) { - _v = _list->_tail; - } - return *this; -} - -template -auto -ts::IntrusiveDList::iterator::operator--() -> self_type & -{ - this->super_type::operator--(); - return *this; -} - -template -auto -ts::IntrusiveDList::const_iterator::operator--(int) -> self_type -{ - self_type tmp(*this); - --*this; - return tmp; -} - -template -auto -ts::IntrusiveDList::iterator::operator--(int) -> self_type -{ - self_type tmp(*this); - --*this; - return tmp; -} - -template -auto -ts::IntrusiveDList::const_iterator::operator->() const -> value_type * -{ - return _v; -} - -template -auto -ts::IntrusiveDList::iterator::operator->() const -> value_type * -{ - return super_type::_v; -} - -template ts::IntrusiveDList::const_iterator::operator value_type *() const -{ - return _v; -} - -template -auto -ts::IntrusiveDList::const_iterator::operator*() const -> value_type & -{ - return *_v; -} - -template -auto -ts::IntrusiveDList::iterator::operator*() const -> value_type & -{ - return *super_type::_v; -} - -template ts::IntrusiveDList::iterator::operator value_type *() const -{ - return super_type::_v; -} - -/// --- Main class - -template -ts::IntrusiveDList::IntrusiveDList(self_type &&that) : _head(that._head), _tail(that._tail), _count(that._count) -{ - that.clear(); -} - -template -bool -ts::IntrusiveDList::empty() const -{ - return _head == nullptr; -} - -template -bool -ts::IntrusiveDList::contains(const value_type *v) const -{ - for (auto thing = _head; thing; thing = L::next_ptr(thing)) { - if (thing == v) - return true; - } - return false; -} - -template -bool -ts::IntrusiveDList::const_iterator::operator==(self_type const &that) const -{ - return this->_v == that._v; -} - -template -bool -ts::IntrusiveDList::const_iterator::operator!=(self_type const &that) const -{ - return this->_v != that._v; -} - -template -auto -ts::IntrusiveDList::prepend(value_type *v) -> self_type & -{ - L::prev_ptr(v) = nullptr; - if (nullptr != (L::next_ptr(v) = _head)) { - L::prev_ptr(_head) = v; - } else { - _tail = v; // transition empty -> non-empty - } - _head = v; - ++_count; - return *this; -} - -template -auto -ts::IntrusiveDList::append(value_type *v) -> self_type & -{ - L::next_ptr(v) = nullptr; - if (nullptr != (L::prev_ptr(v) = _tail)) { - L::next_ptr(_tail) = v; - } else { - _head = v; // transition empty -> non-empty - } - _tail = v; - ++_count; - return *this; -} - -template -auto -ts::IntrusiveDList::take_head() -> value_type * -{ - value_type *zret = _head; - if (_head) { - if (nullptr == (_head = L::next_ptr(_head))) { - _tail = nullptr; // transition non-empty -> empty - } else { - L::prev_ptr(_head) = nullptr; - } - L::next_ptr(zret) = L::prev_ptr(zret) = nullptr; - --_count; - } - return zret; -} - -template -auto -ts::IntrusiveDList::take_tail() -> value_type * -{ - value_type *zret = _tail; - if (_tail) { - if (nullptr == (_tail = L::prev_ptr(_tail))) { - _head = nullptr; // transition non-empty -> empty - } else { - L::next_ptr(_tail) = nullptr; - } - L::next_ptr(zret) = L::prev_ptr(zret) = nullptr; - --_count; - } - return zret; -} - -template -auto -ts::IntrusiveDList::insert_after(value_type *target, value_type *v) -> self_type & -{ - if (target) { - if (nullptr != (L::next_ptr(v) = L::next_ptr(target))) { - L::prev_ptr(L::next_ptr(v)) = v; - } else if (_tail == target) { - _tail = v; - } - L::prev_ptr(v) = target; - L::next_ptr(target) = v; - - ++_count; - } else { - this->append(v); - } - return *this; -} - -template -auto -ts::IntrusiveDList::insert_after(iterator const &target, value_type *v) -> self_type & -{ - return this->insert_after(target._v, v); -} - -template -auto -ts::IntrusiveDList::insert_before(value_type *target, value_type *v) -> self_type & -{ - if (target) { - if (nullptr != (L::prev_ptr(v) = L::prev_ptr(target))) { - L::next_ptr(L::prev_ptr(v)) = v; - } else if (target == _head) { - _head = v; - } - L::next_ptr(v) = target; - L::prev_ptr(target) = v; - - ++_count; - } else { - this->append(v); - } - return *this; -} - -template -auto -ts::IntrusiveDList::insert_before(iterator const &target, value_type *v) -> self_type & -{ - return this->insert_before(target._v, v); -} - -template -auto -ts::IntrusiveDList::erase(value_type *v) -> value_type * -{ - value_type *zret{nullptr}; - - if (L::prev_ptr(v)) { - L::next_ptr(L::prev_ptr(v)) = L::next_ptr(v); - } - if (L::next_ptr(v)) { - zret = L::next_ptr(v); - L::prev_ptr(L::next_ptr(v)) = L::prev_ptr(v); - } - if (v == _head) { - _head = L::next_ptr(v); - } - if (v == _tail) { - _tail = L::prev_ptr(v); - } - L::prev_ptr(v) = L::next_ptr(v) = nullptr; - --_count; - - return zret; -} - -template -auto -ts::IntrusiveDList::erase(const iterator &loc) -> iterator -{ - return this->iterator_for(this->erase(loc._v)); -}; - -template -auto -ts::IntrusiveDList::erase(const iterator &first, const iterator &limit) -> iterator -{ - value_type *spot = first; - value_type *prev{L::prev_ptr(spot)}; - if (prev) { - L::next_ptr(prev) = limit; - } - if (spot == _head) { - _head = limit; - } - // tail is only updated if @a limit is @a end (e.g., @c nullptr). - if (nullptr == limit) { - _tail = prev; - } else { - L::prev_ptr(limit) = prev; - } - // Clear links in removed elements. - while (spot != limit) { - value_type *target{spot}; - spot = L::next_ptr(spot); - L::prev_ptr(target) = L::next_ptr(target) = nullptr; - }; - - return {limit._v, this}; -}; - -template -auto -ts::IntrusiveDList::operator=(self_type &&that) -> self_type & -{ - if (this != &that) { - this->_head = that._head; - this->_tail = that._tail; - this->_count = that._count; - that.clear(); - } - return *this; -} - -template -size_t -ts::IntrusiveDList::count() const -{ - return _count; -}; - -template -auto -ts::IntrusiveDList::begin() const -> const_iterator -{ - return const_iterator{this, _head}; -}; - -template -auto -ts::IntrusiveDList::begin() -> iterator -{ - return iterator{this, _head}; -}; - -template -auto -ts::IntrusiveDList::end() const -> const_iterator -{ - return const_iterator{this, nullptr}; -}; - -template -auto -ts::IntrusiveDList::end() -> iterator -{ - return iterator{this, nullptr}; -}; - -template -auto -ts::IntrusiveDList::iterator_for(value_type *v) -> iterator -{ - return iterator{this, v}; -}; - -template -auto -ts::IntrusiveDList::iterator_for(const value_type *v) const -> const_iterator -{ - return const_iterator{this, v}; -}; - -template -auto -ts::IntrusiveDList::tail() -> value_type * -{ - return _tail; -} - -template -auto -ts::IntrusiveDList::tail() const -> const value_type * -{ - return _tail; -} - -template -auto -ts::IntrusiveDList::head() -> value_type * -{ - return _head; -} - -template -auto -ts::IntrusiveDList::head() const -> const value_type * -{ - return _head; -} - -template -auto -ts::IntrusiveDList::clear() -> self_type & -{ - _head = _tail = nullptr; - _count = 0; - return *this; -}; - -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 - auto - Intrusive_DList_Apply(ts::IntrusiveDList &list, F &&f) - -> decltype(f(*static_cast::value_type *>(nullptr)), list) - { - return list.apply([&f](typename ts::IntrusiveDList::value_type *v) { return f(*v); }); - } - - template - auto - Intrusive_DList_Apply(ts::IntrusiveDList &list, F &&f) - -> decltype(f(static_cast::value_type *>(nullptr)), list) - { - auto spot{list.begin()}; - auto limit{list.end()}; - while (spot != limit) { - f(spot++); // post increment means @a spot is updated before @a f is applied. - } - return list; - } -} // namespace detail - -template -template -auto -ts::IntrusiveDList::apply(F &&f) -> self_type & -{ - return detail::Intrusive_DList_Apply(*this, f); -}; - -} // namespace ts diff --git a/include/tscpp/util/Makefile.am b/include/tscpp/util/Makefile.am index fcd31eb3f21..cdecb3ac6fa 100644 --- a/include/tscpp/util/Makefile.am +++ b/include/tscpp/util/Makefile.am @@ -23,7 +23,6 @@ library_include_HEADERS = \ ts_bw_format.h \ ts_diag_levels.h \ ts_ip.h \ - IntrusiveDList.h \ LocalBuffer.h \ PostScript.h \ Strerror.h \ diff --git a/proxy/http/remap/PluginDso.h b/proxy/http/remap/PluginDso.h index 694ac7fded4..257a5407d91 100644 --- a/proxy/http/remap/PluginDso.h +++ b/proxy/http/remap/PluginDso.h @@ -37,6 +37,8 @@ #include #include +#include "swoc/IntrusiveDList.h" + #include "ts/apidefs.h" #include "ts/remap.h" @@ -44,7 +46,6 @@ namespace fs = swoc::file; #include "tscore/Ptr.h" #include "I_EventSystem.h" -#include "tscpp/util/IntrusiveDList.h" #include "Plugin.h" @@ -85,8 +86,8 @@ class PluginDso : public PluginThreadContext using self_type = PluginDso; ///< Self reference type. self_type *_next = nullptr; self_type *_prev = nullptr; - using Linkage = ts::IntrusiveLinkage; - using PluginList = ts::IntrusiveDList; + using Linkage = swoc::IntrusiveLinkage; + using PluginList = swoc::IntrusiveDList; /* Methods to be called when processing a list of plugins, to be overloaded by the remap or the global plugins correspondingly */ virtual void indicatePreReload() = 0; diff --git a/proxy/http/remap/PluginFactory.h b/proxy/http/remap/PluginFactory.h index 644fb154485..e0109d6f708 100644 --- a/proxy/http/remap/PluginFactory.h +++ b/proxy/http/remap/PluginFactory.h @@ -25,12 +25,13 @@ #include +#include "swoc/IntrusiveDList.h" + #include "tscore/Ptr.h" #include "PluginDso.h" #include "RemapPluginInfo.h" #include "tscore/Ptr.h" -#include "tscpp/util/IntrusiveDList.h" #include "tscore/ink_uuid.h" #include "ts/apidefs.h" @@ -58,7 +59,7 @@ class RemapPluginInst using self_type = RemapPluginInst; ///< Self reference type. self_type *_next = nullptr; self_type *_prev = nullptr; - using Linkage = ts::IntrusiveLinkage; + using Linkage = swoc::IntrusiveLinkage; /* Plugin instance = the plugin info + the data returned by the init callback */ RemapPluginInfo &_plugin; @@ -87,7 +88,7 @@ class RemapPluginInst */ class PluginFactory { - using PluginInstList = ts::IntrusiveDList; + using PluginInstList = swoc::IntrusiveDList; public: PluginFactory(); diff --git a/proxy/http3/QPACK.h b/proxy/http3/QPACK.h index 23f0f024858..b255fcf95c5 100644 --- a/proxy/http3/QPACK.h +++ b/proxy/http3/QPACK.h @@ -25,11 +25,12 @@ #include +#include "swoc/IntrusiveDList.h" + #include "I_EventSystem.h" #include "I_Event.h" #include "I_IOBuffer.h" #include "tscore/Arena.h" -#include "tscpp/util/IntrusiveDList.h" #include "MIME.h" #include "HTTP.h" #include "QUICApplication.h" @@ -259,7 +260,7 @@ class QPACK : public QUICApplication bool _invalid = false; - ts::IntrusiveDList _blocked_list; + swoc::IntrusiveDList _blocked_list; bool _add_to_blocked_list(DecodeRequest *decode_request); uint16_t _largest_known_received_index = 0; diff --git a/src/tscpp/util/CMakeLists.txt b/src/tscpp/util/CMakeLists.txt index bb0290a07cd..ac0d9c63e12 100644 --- a/src/tscpp/util/CMakeLists.txt +++ b/src/tscpp/util/CMakeLists.txt @@ -32,7 +32,6 @@ target_link_libraries(tscpputil install(TARGETS tscpputil) add_executable(test_tscpputil - unit_tests/test_IntrusiveDList.cc unit_tests/test_LocalBuffer.cc unit_tests/test_PostScript.cc unit_tests/test_Strerror.cc diff --git a/src/tscpp/util/unit_tests/test_IntrusiveDList.cc b/src/tscpp/util/unit_tests/test_IntrusiveDList.cc deleted file mode 100644 index b7f79d8d3c5..00000000000 --- a/src/tscpp/util/unit_tests/test_IntrusiveDList.cc +++ /dev/null @@ -1,295 +0,0 @@ -/** @file - - IntrusiveDList 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 -#include -#include -#include - -#include "tscpp/util/IntrusiveDList.h" -#include "swoc/bwf_base.h" - -#include "catch.hpp" - -using ts::IntrusiveDList; - -// -------------------- -// Code for documentation - placed here to guarantee the examples at least compile. -// First so that additional tests do not require updating the documentation source links. - -class Message -{ - using self_type = Message; ///< Self reference type. - -public: - // Message severity level. - enum Severity { LVL_DEBUG, LVL_INFO, LVL_WARN, LVL_ERROR }; - -protected: - std::string _text; // Text of the message. - Severity _severity{LVL_DEBUG}; - int _indent{0}; // indentation level for display. - - // Intrusive list support. - struct Linkage { - static self_type *&next_ptr(self_type *); // Link accessor. - static self_type *&prev_ptr(self_type *); // Link accessor. - - self_type *_next{nullptr}; // Forward link. - self_type *_prev{nullptr}; // Backward link. - } _link; - - bool is_in_list() const; - - friend class Container; -}; - -auto -Message::Linkage::next_ptr(self_type *that) -> self_type *& -{ - return that->_link._next; -} -auto -Message::Linkage::prev_ptr(self_type *that) -> self_type *& -{ - return that->_link._prev; -} - -bool -Message::is_in_list() const -{ - return _link._next || _link._prev; -} - -class Container -{ - using self_type = Container; - using MessageList = ts::IntrusiveDList; - -public: - ~Container(); - - template self_type &debug(std::string_view fmt, Args &&...args); - - size_t count() const; - self_type &clear(); - Message::Severity max_severity() const; - void print() const; - -protected: - MessageList _msgs; -}; - -Container::~Container() -{ - this->clear(); // clean up memory. -} - -auto -Container::clear() -> self_type & -{ - Message *msg; - while (nullptr != (msg = _msgs.take_head())) { - delete msg; - } - _msgs.clear(); - return *this; -} - -size_t -Container::count() const -{ - return _msgs.count(); -} - -template -auto -Container::debug(std::string_view fmt, Args &&...args) -> self_type & -{ - Message *msg = new Message; - swoc::bwprint_v(msg->_text, fmt, std::forward_as_tuple(args...)); - msg->_severity = Message::LVL_DEBUG; - _msgs.append(msg); - return *this; -} - -Message::Severity -Container::max_severity() const -{ - auto spot = std::max_element(_msgs.begin(), _msgs.end(), - [](Message const &lhs, Message const &rhs) { return lhs._severity < rhs._severity; }); - return spot == _msgs.end() ? Message::Severity::LVL_DEBUG : spot->_severity; -} - -void -Container::print() const -{ - for (auto &&elt : _msgs) { - std::cout << static_cast(elt._severity) << ": " << elt._text << std::endl; - } -} - -TEST_CASE("IntrusiveDList Example", "[libtscpputil][IntrusiveDList]") -{ - Container container; - - container.debug("This is message {}", 1); - REQUIRE(container.count() == 1); - // Destructor is checked for non-crashing as container goes out of scope. -} - -struct Thing { - Thing *_next{nullptr}; - Thing *_prev{nullptr}; - std::string _payload; - - Thing(std::string_view text) : _payload(text) {} - - struct Linkage { - static Thing *& - next_ptr(Thing *t) - { - return t->_next; - } - static Thing *& - prev_ptr(Thing *t) - { - return t->_prev; - } - }; -}; - -// Just for you, @maskit ! Demonstrating non-public links and subclassing. -class PrivateThing : protected Thing -{ - using self_type = PrivateThing; - using super_type = Thing; - -public: - PrivateThing(std::string_view text) : super_type(text) {} - - struct Linkage { - static self_type *& - next_ptr(self_type *t) - { - return ts::ptr_ref_cast(t->_next); - } - static self_type *& - prev_ptr(self_type *t) - { - return ts::ptr_ref_cast(t->_prev); - } - }; - - std::string const & - payload() const - { - return _payload; - } -}; - -// End of documentation example code. -// If any lines above here are changed, the documentation must be updated. -// -------------------- - -using ThingList = ts::IntrusiveDList; -using PrivateThingList = ts::IntrusiveDList; - -TEST_CASE("IntrusiveDList", "[libtscpputil][IntrusiveDList]") -{ - ThingList list; - int n; - - REQUIRE(list.count() == 0); - REQUIRE(list.head() == nullptr); - REQUIRE(list.tail() == nullptr); - REQUIRE(list.begin() == list.end()); - REQUIRE(list.empty()); - - n = 0; - for ([[maybe_unused]] auto &thing : list) - ++n; - REQUIRE(n == 0); - // Check const iteration (mostly compile checks here). - for ([[maybe_unused]] auto &thing : static_cast(list)) - ++n; - REQUIRE(n == 0); - - list.append(new Thing("one")); - REQUIRE(list.begin() != list.end()); - REQUIRE(list.tail() == list.head()); - - list.prepend(new Thing("two")); - REQUIRE(list.count() == 2); - REQUIRE(list.head()->_payload == "two"); - REQUIRE(list.tail()->_payload == "one"); - list.prepend(list.take_tail()); - REQUIRE(list.head()->_payload == "one"); - REQUIRE(list.tail()->_payload == "two"); - list.insert_after(list.head(), new Thing("middle")); - list.insert_before(list.tail(), new Thing("muddle")); - REQUIRE(list.count() == 4); - auto spot = list.begin(); - REQUIRE((*spot++)._payload == "one"); - REQUIRE((*spot++)._payload == "middle"); - REQUIRE((*spot++)._payload == "muddle"); - REQUIRE((*spot++)._payload == "two"); - REQUIRE(spot == list.end()); - - Thing *thing = list.take_head(); - REQUIRE(thing->_payload == "one"); - REQUIRE(list.count() == 3); - REQUIRE(list.head() != nullptr); - REQUIRE(list.head()->_payload == "middle"); - - list.prepend(thing); - list.erase(list.head()); - REQUIRE(list.count() == 3); - REQUIRE(list.head() != nullptr); - REQUIRE(list.head()->_payload == "middle"); - list.prepend(thing); - - thing = list.take_tail(); - REQUIRE(thing->_payload == "two"); - REQUIRE(list.count() == 3); - REQUIRE(list.tail() != nullptr); - REQUIRE(list.tail()->_payload == "muddle"); - - list.append(thing); - list.erase(list.tail()); - REQUIRE(list.count() == 3); - REQUIRE(list.tail() != nullptr); - REQUIRE(list.tail()->_payload == "muddle"); - REQUIRE(list.head()->_payload == "one"); - - list.insert_before(list.end(), new Thing("trailer")); - REQUIRE(list.count() == 4); - REQUIRE(list.tail()->_payload == "trailer"); - - PrivateThingList priv_list; - for (std::size_t i = 1; i <= 23; ++i) { - std::string name; - swoc::bwprint(name, "Item {}", i); - priv_list.append(new PrivateThing(name)); - REQUIRE(priv_list.count() == i); - } - REQUIRE(priv_list.head()->payload() == "Item 1"); - REQUIRE(priv_list.tail()->payload() == "Item 23"); -}