LCOV - code coverage report
Current view: top level - lib/support - IntrusiveList.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 63 69 91.3 %
Date: 2024-02-15 08:20:41 Functions: 46 63 73.0 %

          Line data    Source code
       1             : /*
       2             :  *    Copyright (c) 2021 Project CHIP Authors
       3             :  *
       4             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       5             :  *    you may not use this file except in compliance with the License.
       6             :  *    You may obtain a copy of the License at
       7             :  *
       8             :  *        http://www.apache.org/licenses/LICENSE-2.0
       9             :  *
      10             :  *    Unless required by applicable law or agreed to in writing, software
      11             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      12             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13             :  *    See the License for the specific language governing permissions and
      14             :  *    limitations under the License.
      15             :  */
      16             : 
      17             : #pragma once
      18             : 
      19             : #include <iterator>
      20             : #include <utility>
      21             : 
      22             : #include <lib/support/CodeUtils.h>
      23             : 
      24             : namespace chip {
      25             : 
      26             : class IntrusiveListBase;
      27             : 
      28             : enum class IntrusiveMode
      29             : {
      30             :     // Strict mode features
      31             :     //
      32             :     //   Node constructor puts the hook in a well-known default state.
      33             :     //
      34             :     //   Node destructor checks if the hook is in the well-known default state.
      35             :     //   If not, an assertion is raised.
      36             :     //
      37             :     //   Every time an object is inserted in the intrusive container, the
      38             :     //   container checks if the hook is in the well-known default state. If
      39             :     //   not, an assertion is raised.
      40             :     //
      41             :     //   Every time an object is being erased from the intrusive container, the
      42             :     //   container puts the erased object in the well-known default state.
      43             :     //
      44             :     // Note: The strict mode works similar as safe mode for Boost.Intrusive
      45             :     //
      46             :     Strict,
      47             : 
      48             :     // Auto-unlink mode features
      49             :     //
      50             :     //   When the destructor of the node is called, the hook checks if the node
      51             :     //   is inserted in a container. If so, the hook removes the node from the
      52             :     //   container.
      53             :     //
      54             :     //   The hook has a member function called unlink() that can be used to
      55             :     //   unlink the node from the container at any time, without having any
      56             :     //   reference to the container, if the user wants to do so.
      57             :     //
      58             :     // This auto-unlink feature is useful in certain applications but it must
      59             :     // be used very carefully:
      60             :     //
      61             :     //   If several threads are using the same container the destructor of the
      62             :     //   auto-unlink hook will be called without any thread synchronization so
      63             :     //   removing the object is thread-unsafe. Container contents change
      64             :     //   silently without modifying the container directly. This can lead to
      65             :     //   surprising effects.
      66             :     //
      67             :     // These auto-unlink hooks have also strict-mode properties:
      68             :     //
      69             :     //   Node constructors put the hook in a well-known default state.
      70             :     //
      71             :     //   Every time an object is inserted in the intrusive container, the
      72             :     //   container checks if the hook is in the well-known default state. If
      73             :     //   not, an assertion is raised.
      74             :     //
      75             :     //   Every time an object is erased from an intrusive container, the
      76             :     //   container puts the erased object in the well-known default state.
      77             :     //
      78             :     // Note: The strict mode works similar as auto-unlink mode for
      79             :     // Boost.Intrusive
      80             :     //
      81             :     AutoUnlink,
      82             : };
      83             : 
      84             : class IntrusiveListNodePrivateBase
      85             : {
      86             : public:
      87          10 :     IntrusiveListNodePrivateBase() : mPrev(nullptr), mNext(nullptr) {}
      88      133881 :     ~IntrusiveListNodePrivateBase() { VerifyOrDie(!IsInList()); }
      89             : 
      90             :     // Note: The copy construct/assignment is not provided because the list node state is not copyable.
      91             :     //       The move construct/assignment is not provided because all modifications to the list shall go through the list object.
      92             :     IntrusiveListNodePrivateBase(const IntrusiveListNodePrivateBase &)             = delete;
      93             :     IntrusiveListNodePrivateBase & operator=(const IntrusiveListNodePrivateBase &) = delete;
      94             :     IntrusiveListNodePrivateBase(IntrusiveListNodePrivateBase &&)                  = delete;
      95             :     IntrusiveListNodePrivateBase & operator=(IntrusiveListNodePrivateBase &&)      = delete;
      96             : 
      97      268087 :     bool IsInList() const { return (mPrev != nullptr && mNext != nullptr); }
      98             : 
      99             : private:
     100             :     friend class IntrusiveListBase;
     101      134151 :     IntrusiveListNodePrivateBase(IntrusiveListNodePrivateBase * prev, IntrusiveListNodePrivateBase * next) :
     102      134151 :         mPrev(prev), mNext(next)
     103      134151 :     {}
     104             : 
     105             :     void TakePlaceOf(const IntrusiveListNodePrivateBase * that)
     106             :     {
     107             :         // prerequisite `that` is in a list
     108             :         // `this` will take place of the position of `that`.
     109             :         // `that` will be emptied by the caller after this function
     110             :         mPrev        = that->mPrev;
     111             :         mNext        = that->mNext;
     112             :         mPrev->mNext = this;
     113             :         mNext->mPrev = this;
     114             :     }
     115             : 
     116        8136 :     void Prepend(IntrusiveListNodePrivateBase * node)
     117             :     {
     118        8136 :         VerifyOrDie(IsInList());
     119        8136 :         VerifyOrDie(!node->IsInList());
     120        8136 :         node->mPrev  = mPrev;
     121        8136 :         node->mNext  = this;
     122        8136 :         mPrev->mNext = node;
     123        8136 :         mPrev        = node;
     124        8136 :     }
     125             : 
     126             :     void Append(IntrusiveListNodePrivateBase * node)
     127             :     {
     128             :         VerifyOrDie(IsInList());
     129             :         VerifyOrDie(!node->IsInList());
     130             :         node->mPrev  = this;
     131             :         node->mNext  = mNext;
     132             :         mNext->mPrev = node;
     133             :         mNext        = node;
     134             :     }
     135             : 
     136             : protected:
     137      133880 :     void Remove()
     138             :     {
     139      133880 :         VerifyOrDie(IsInList());
     140      133880 :         mPrev->mNext = mNext;
     141      133880 :         mNext->mPrev = mPrev;
     142      133880 :         mPrev        = nullptr;
     143      133880 :         mNext        = nullptr;
     144      133880 :     }
     145             : 
     146             : private:
     147             :     IntrusiveListNodePrivateBase * mPrev;
     148             :     IntrusiveListNodePrivateBase * mNext;
     149             : };
     150             : 
     151             : // Strict mode implementation
     152             : template <IntrusiveMode Mode = IntrusiveMode::Strict>
     153             : class IntrusiveListNodeBase : public IntrusiveListNodePrivateBase
     154             : {
     155             : };
     156             : 
     157             : // Partial specialization implementation for auto-unlink mode.
     158             : template <>
     159             : class IntrusiveListNodeBase<IntrusiveMode::AutoUnlink> : public IntrusiveListNodePrivateBase
     160             : {
     161             : public:
     162             :     ~IntrusiveListNodeBase() { Unlink(); }
     163             :     void Unlink()
     164             :     {
     165             :         if (IsInList())
     166             :             Remove();
     167             :     }
     168             : };
     169             : 
     170             : // Non-template part of IntrusiveList. It appears that for list structure, both mode can share same implementation.
     171             : class IntrusiveListBase
     172             : {
     173             : public:
     174             :     class ConstIteratorBase
     175             :     {
     176             :     private:
     177             :         friend class IntrusiveListBase;
     178       16258 :         ConstIteratorBase(const IntrusiveListNodePrivateBase * cur) : mCurrent(cur) {}
     179             : 
     180       36364 :         const IntrusiveListNodePrivateBase & operator*() { return *mCurrent; }
     181             : 
     182             :     public:
     183             :         using difference_type   = std::ptrdiff_t;
     184             :         using iterator_category = std::bidirectional_iterator_tag;
     185             : 
     186             :         ConstIteratorBase(const ConstIteratorBase &)             = default;
     187             :         ConstIteratorBase(ConstIteratorBase &&)                  = default;
     188             :         ConstIteratorBase & operator=(const ConstIteratorBase &) = default;
     189             :         ConstIteratorBase & operator=(ConstIteratorBase &&)      = default;
     190             : 
     191       36364 :         bool operator==(const ConstIteratorBase & that) const { return mCurrent == that.mCurrent; }
     192       36364 :         bool operator!=(const ConstIteratorBase & that) const { return !(*this == that); }
     193             : 
     194       28235 :         ConstIteratorBase & operator++()
     195             :         {
     196       28235 :             mCurrent = mCurrent->mNext;
     197       28235 :             return *this;
     198             :         }
     199             : 
     200             :         ConstIteratorBase operator++(int)
     201             :         {
     202             :             ConstIteratorBase res(mCurrent);
     203             :             mCurrent = mCurrent->mNext;
     204             :             return res;
     205             :         }
     206             : 
     207             :         ConstIteratorBase & operator--()
     208             :         {
     209             :             mCurrent = mCurrent->mPrev;
     210             :             return *this;
     211             :         }
     212             : 
     213             :         ConstIteratorBase operator--(int)
     214             :         {
     215             :             ConstIteratorBase res(mCurrent);
     216             :             mCurrent = mCurrent->mPrev;
     217             :             return res;
     218             :         }
     219             : 
     220             :     protected:
     221             :         const IntrusiveListNodePrivateBase * mCurrent;
     222             :     };
     223             : 
     224             :     class IteratorBase
     225             :     {
     226             :     private:
     227             :         friend class IntrusiveListBase;
     228         307 :         IteratorBase(IntrusiveListNodePrivateBase * cur) : mCurrent(cur) {}
     229             : 
     230             :         IntrusiveListNodePrivateBase & operator*() { return *mCurrent; }
     231             : 
     232             :     public:
     233             :         using difference_type   = std::ptrdiff_t;
     234             :         using iterator_category = std::bidirectional_iterator_tag;
     235             : 
     236             :         IteratorBase(const IteratorBase &)             = default;
     237             :         IteratorBase(IteratorBase &&)                  = default;
     238             :         IteratorBase & operator=(const IteratorBase &) = default;
     239             :         IteratorBase & operator=(IteratorBase &&)      = default;
     240             : 
     241         296 :         bool operator==(const IteratorBase & that) const { return mCurrent == that.mCurrent; }
     242         296 :         bool operator!=(const IteratorBase & that) const { return !(*this == that); }
     243             : 
     244         160 :         IteratorBase & operator++()
     245             :         {
     246         160 :             mCurrent = mCurrent->mNext;
     247         160 :             return *this;
     248             :         }
     249             : 
     250           0 :         IteratorBase operator++(int)
     251             :         {
     252           0 :             IteratorBase res(mCurrent);
     253           0 :             mCurrent = mCurrent->mNext;
     254           0 :             return res;
     255             :         }
     256             : 
     257             :         IteratorBase & operator--()
     258             :         {
     259             :             mCurrent = mCurrent->mPrev;
     260             :             return *this;
     261             :         }
     262             : 
     263             :         IteratorBase operator--(int)
     264             :         {
     265             :             IteratorBase res(mCurrent);
     266             :             mCurrent = mCurrent->mPrev;
     267             :             return res;
     268             :         }
     269             : 
     270             :     protected:
     271             :         IntrusiveListNodePrivateBase * mCurrent;
     272             :     };
     273             : 
     274      267421 :     bool Empty() const { return mNode.mNext == &mNode; }
     275             : 
     276           0 :     void Erase(IteratorBase pos) { pos.mCurrent->Remove(); }
     277             : 
     278             : protected:
     279             :     // The list is formed as a ring with mNode being the end.
     280             :     //
     281             :     // begin                                        end
     282             :     //   v                                           v
     283             :     // item(first) -> item -> ... -> item(last) -> mNode
     284             :     //   ^                                           |
     285             :     //    \------------------------------------------/
     286             :     //
     287      134151 :     IntrusiveListBase() : mNode(&mNode, &mNode) {}
     288      133769 :     ~IntrusiveListBase()
     289             :     {
     290      133769 :         VerifyOrDie(Empty());
     291             :         /* clear mNode such that the destructor checking mNode.IsInList doesn't fail */
     292      133769 :         mNode.Remove();
     293      133769 :     }
     294             : 
     295             :     IntrusiveListBase(const IntrusiveListBase &)             = delete;
     296             :     IntrusiveListBase & operator=(const IntrusiveListBase &) = delete;
     297             : 
     298             :     IntrusiveListBase(IntrusiveListBase && that) : mNode(&mNode, &mNode) { *this = std::move(that); }
     299             : 
     300             :     IntrusiveListBase & operator=(IntrusiveListBase && that)
     301             :     {
     302             :         VerifyOrDie(Empty());
     303             :         if (!that.Empty())
     304             :         {
     305             :             mNode.TakePlaceOf(&that.mNode);
     306             :             that.mNode.mNext = &that.mNode;
     307             :             that.mNode.mPrev = &that.mNode;
     308             :         }
     309             :         else
     310             :         {
     311             :             // Do nothing here if that is empty, because there is a prerequisite that `this` is empty.
     312             :         }
     313             :         return *this;
     314             :     }
     315             : 
     316        8129 :     ConstIteratorBase begin() const { return ConstIteratorBase(mNode.mNext); }
     317        8129 :     ConstIteratorBase end() const { return ConstIteratorBase(&mNode); }
     318         152 :     IteratorBase begin() { return IteratorBase(mNode.mNext); }
     319         201 :     IteratorBase end() { return IteratorBase(&mNode); }
     320             : 
     321             :     void PushFront(IntrusiveListNodePrivateBase * node) { mNode.Append(node); }
     322        8136 :     void PushBack(IntrusiveListNodePrivateBase * node) { mNode.Prepend(node); }
     323             : 
     324             :     void InsertBefore(IteratorBase pos, IntrusiveListNodePrivateBase * node) { pos.mCurrent->Prepend(node); }
     325             :     void InsertAfter(IteratorBase pos, IntrusiveListNodePrivateBase * node) { pos.mCurrent->Append(node); }
     326        6446 :     void Remove(IntrusiveListNodePrivateBase * node) { node->Remove(); }
     327             : 
     328             :     /// @brief Replace an original node in list with a new node.
     329             :     void Replace(IntrusiveListNodePrivateBase * original, IntrusiveListNodePrivateBase * replacement)
     330             :     {
     331             :         // VerifyOrDie(Contains(original)); This check is too heavy to do, but it shall hold
     332             :         VerifyOrDie(!replacement->IsInList());
     333             :         replacement->TakePlaceOf(original);
     334             :         original->mPrev = nullptr;
     335             :         original->mNext = nullptr;
     336             :     }
     337             : 
     338        8129 :     bool Contains(const IntrusiveListNodePrivateBase * node) const
     339             :     {
     340       36364 :         for (auto & iter : *this)
     341             :         {
     342       36364 :             if (&iter == node)
     343        8129 :                 return true;
     344             :         }
     345           0 :         return false;
     346             :     }
     347             : 
     348             : private:
     349             :     IntrusiveListNodePrivateBase mNode;
     350             : };
     351             : 
     352             : /// The hook converts between node object T and IntrusiveListNodeBase
     353             : ///
     354             : /// When using this hook, the node type (T) MUST inherit from IntrusiveListNodeBase.
     355             : ///
     356             : template <typename T, IntrusiveMode Mode>
     357             : class IntrusiveListBaseHook
     358             : {
     359             : public:
     360             :     static_assert(std::is_base_of<IntrusiveListNodeBase<Mode>, T>::value, "T must be derived from IntrusiveListNodeBase");
     361             : 
     362         175 :     static T * ToObject(IntrusiveListNodePrivateBase * node) { return static_cast<T *>(node); }
     363             :     static const T * ToObject(const IntrusiveListNodePrivateBase * node) { return static_cast<T *>(node); }
     364             : 
     365             :     static T * ToObject(IntrusiveListNodeBase<Mode> * node) { return static_cast<T *>(node); }
     366             :     static const T * ToObject(const IntrusiveListNodeBase<Mode> * node) { return static_cast<T *>(node); }
     367             : 
     368       16272 :     static IntrusiveListNodeBase<Mode> * ToNode(T * object) { return static_cast<IntrusiveListNodeBase<Mode> *>(object); }
     369        8129 :     static const IntrusiveListNodeBase<Mode> * ToNode(const T * object)
     370             :     {
     371        8129 :         return static_cast<const IntrusiveListNodeBase<Mode> *>(object);
     372             :     }
     373             : };
     374             : 
     375             : /// A double-linked list where the data is stored together with the previous/next pointers for cache efficiency / and compactness.
     376             : ///
     377             : /// The default hook (IntrusiveListBaseHook<T>) requires T to inherit from IntrusiveListNodeBase.
     378             : ///
     379             : /// Consumers must ensure that the IntrusiveListNodeBase object associated with
     380             : /// a node is removed from any list it might belong to before it is destroyed.
     381             : ///
     382             : /// Consumers must ensure that a list is empty before it is destroyed.
     383             : ///
     384             : /// A node may only belong to a single list. The code will assert (via VerifyOrDie) on this invariant.
     385             : ///
     386             : /// Example usage:
     387             : ///
     388             : ///     class ListNode : public IntrusiveListNodeBase  {};
     389             : ///     // ...
     390             : ///     ListNode a,b,c;
     391             : ///     IntrusiveList<ListNode> list;    // NOTE: node lifetime >= list lifetime
     392             : ///
     393             : ///     list.PushBack(&a);
     394             : ///     list.PushFront(&b);
     395             : ///     assert(list.Contains(&a)  && list.Contains(&b) && !list.Contains(&c));
     396             : ///     list.Remove(&a);
     397             : ///     list.Remove(&b);
     398             : template <typename T, IntrusiveMode Mode = IntrusiveMode::Strict, typename Hook = IntrusiveListBaseHook<T, Mode>>
     399             : class IntrusiveList : public IntrusiveListBase
     400             : {
     401             : public:
     402      135143 :     IntrusiveList() : IntrusiveListBase() {}
     403             : 
     404             :     IntrusiveList(IntrusiveList &&)             = default;
     405             :     IntrusiveList & operator=(IntrusiveList &&) = default;
     406             : 
     407             :     class ConstIterator : public IntrusiveListBase::ConstIteratorBase
     408             :     {
     409             :     public:
     410             :         using value_type = const T;
     411             :         using pointer    = const T *;
     412             :         using reference  = const T &;
     413             : 
     414             :         ConstIterator(IntrusiveListBase::ConstIteratorBase && base) : IntrusiveListBase::ConstIteratorBase(std::move(base)) {}
     415             :         const T * operator->() { return Hook::ToObject(mCurrent); }
     416             :         const T & operator*() { return *Hook::ToObject(mCurrent); }
     417             :     };
     418             : 
     419             :     class Iterator : public IntrusiveListBase::IteratorBase
     420             :     {
     421             :     public:
     422             :         using value_type = T;
     423             :         using pointer    = T *;
     424             :         using reference  = T &;
     425             : 
     426         573 :         Iterator(IntrusiveListBase::IteratorBase && base) : IntrusiveListBase::IteratorBase(std::move(base)) {}
     427          72 :         T * operator->() { return Hook::ToObject(mCurrent); }
     428         103 :         T & operator*() { return *Hook::ToObject(mCurrent); }
     429             :     };
     430             : 
     431             :     ConstIterator begin() const { return IntrusiveListBase::begin(); }
     432             :     ConstIterator end() const { return IntrusiveListBase::end(); }
     433         285 :     Iterator begin() { return IntrusiveListBase::begin(); }
     434         334 :     Iterator end() { return IntrusiveListBase::end(); }
     435             :     void PushFront(T * value) { IntrusiveListBase::PushFront(Hook::ToNode(value)); }
     436        8136 :     void PushBack(T * value) { IntrusiveListBase::PushBack(Hook::ToNode(value)); }
     437             :     void InsertBefore(Iterator pos, T * value) { IntrusiveListBase::InsertBefore(pos, Hook::ToNode(value)); }
     438             :     void InsertAfter(Iterator pos, T * value) { IntrusiveListBase::InsertAfter(pos, Hook::ToNode(value)); }
     439        8136 :     void Remove(T * value) { IntrusiveListBase::Remove(Hook::ToNode(value)); }
     440             :     void Replace(T * original, T * replacement) { IntrusiveListBase::Replace(Hook::ToNode(original), Hook::ToNode(replacement)); }
     441        8129 :     bool Contains(const T * value) const { return IntrusiveListBase::Contains(Hook::ToNode(value)); }
     442             : 
     443             :     void Clear()
     444             :     {
     445             :         while (begin() != end())
     446             :         {
     447             :             Remove(&(*begin()));
     448             :         }
     449             :     }
     450             : };
     451             : 
     452             : } // namespace chip

Generated by: LCOV version 1.14