// Copyright (c) 2024 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef QUICHE_COMMON_QUICHE_INTRUSIVE_LIST_H_ #define QUICHE_COMMON_QUICHE_INTRUSIVE_LIST_H_ // A QuicheIntrusiveList<> is a doubly-linked list where the link pointers are // embedded in the elements. They are circularly linked making insertion and // removal into a known position constant time and branch-free operations. // // Usage is similar to an STL list<> where feasible, but there are important // differences. First and foremost, the elements must derive from the // QuicheIntrusiveLink<> base class: // // struct Foo : public QuicheIntrusiveLink<Foo> { // // ... // } // // QuicheIntrusiveList<Foo> l; // l.push_back(new Foo); // l.push_front(new Foo); // l.erase(&l.front()); // l.erase(&l.back()); // // Intrusive lists are primarily useful when you would have considered embedding // link pointers in your class directly for space or performance reasons. An // QuicheIntrusiveLink<> is the size of 2 pointers, usually 16 bytes on 64-bit // systems. Intrusive lists do not perform memory allocation (unlike the STL // list<> class) and thus may use less memory than list<>. In particular, if the // list elements are pointers to objects, using a list<> would perform an extra // memory allocation for each list node structure, while an // QuicheIntrusiveList<> would not. // // Note that QuicheIntrusiveLink is exempt from the C++ style guide's // limitations on multiple inheritance, so it's fine to inherit from both // QuicheIntrusiveLink and a base class, even if the base class is not a pure // interface. // // Because the list pointers are embedded in the objects stored in an // QuicheIntrusiveList<>, erasing an item from a list is constant time. Consider // the following: // // map<string,Foo> foo_map; // list<Foo*> foo_list; // // foo_list.push_back(&foo_map["bar"]); // foo_list.erase(&foo_map["bar"]); // Compile error! // // The problem here is that a Foo* doesn't know where on foo_list it resides, // so removal requires iteration over the list. Various tricks can be performed // to overcome this. For example, a foo_list::iterator can be stored inside of // the Foo object. But at that point you'd be better off using an // QuicheIntrusiveList<>: // // map<string,Foo> foo_map; // QuicheIntrusiveList<Foo> foo_list; // // foo_list.push_back(&foo_map["bar"]); // foo_list.erase(&foo_map["bar"]); // Yeah! // // Note that QuicheIntrusiveLists come with a few limitations. The primary // limitation is that the QuicheIntrusiveLink<> base class is not copyable or // assignable. The result is that STL algorithms which mutate the order of // iterators, such as reverse() and unique(), will not work by default with // QuicheIntrusiveLists. In order to allow these algorithms to work you'll need // to define swap() and/or operator= for your class. // // Another limitation is that the QuicheIntrusiveList<> structure itself is not // copyable or assignable since an item/link combination can only exist on one // QuicheIntrusiveList<> at a time. This limitation is a result of the link // pointers for an item being intrusive in the item itself. For example, the // following will not compile: // // FooList a; // FooList b(a); // no copy constructor // b = a; // no assignment operator // // The similar STL code does work since the link pointers are external to the // item: // // list<int*> a; // a.push_back(new int); // list<int*> b(a); // QUICHE_CHECK(a.front() == b.front()); // // Note that QuicheIntrusiveList::size() runs in O(N) time. #include <stddef.h> #include <cstddef> #include <iterator> #include "quiche/common/platform/api/quiche_export.h" namespace quiche { template <typename T, typename ListID> class QuicheIntrusiveList; template <typename T, typename ListID = void> class QUICHE_EXPORT QuicheIntrusiveLink { … }; template <typename T, typename ListID = void> class QUICHE_EXPORT QuicheIntrusiveList { … }; } // namespace quiche #endif // QUICHE_COMMON_QUICHE_INTRUSIVE_LIST_H_