Mercurial > repos > rliterman > csp2
view CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/list.h @ 69:33d812a61356
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 17:55:14 -0400 |
parents | |
children |
line wrap: on
line source
// Copyright (c) 2021 Cloudflare, Inc. and contributors // Licensed under the MIT License: // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #pragma once #include "common.h" KJ_BEGIN_HEADER namespace kj { template <typename T> class ListLink; template <typename T, typename MaybeConstT, ListLink<T> T::*link> class ListIterator; namespace _ { // (private) KJ_NORETURN(void throwDoubleAdd()); KJ_NORETURN(void throwRemovedNotPresent()); KJ_NORETURN(void throwRemovedWrongList()); KJ_NORETURN(void throwDestroyedWhileInList()); } // namespace _ (private) template <typename T, ListLink<T> T::*link> class List { // A linked list that does no memory allocation. // // The list contains elements of type T that are allocated elsewhere. An existing object of type // T can be added to the list and removed again without doing any heap allocation. This is // achieved by requiring that T contains a field of type ListLink<T>. A pointer-to-member to // this field is the second parameter to the `List` template. // // kj::List is ideally suited to situations where an object wants to be able to "add itself" to // a list of objects waiting for a notification, with the ability to remove itself early if it // wants to stop waiting. With traditional STL containers, these operations would require memory // allocation. // // Example: // // struct Item { // ListLink<Waiter> link; // // ... other members ... // }; // // kj::List<Item, &Item::link> itemList; // // Item foo; // itemList.add(foo); // itemList.remove(foo); // // Note that you MUST manually remove an element from the list before destroying it. ListLinks // do not automatically unlink themselves because this could lead to subtle thread-safety bugs // if the List is guarded by a mutex, and that mutex is not currently locked. Normally, you should // have T's destructor remove it from any lists. You can use `link.isLinked()` to check if the // item is currently in a list. // // kj::List is a doubly-linked list in order to allow O(1) removal of any element given only a // reference to the element. However, it only supports forward iteration. // // When iterating over a kj::List, you can safely remove current element which the iterator // points to without breaking the iteration. However, removing any *other* element could // invalidate the iterator. public: List() = default; KJ_DISALLOW_COPY_AND_MOVE(List); bool empty() const { return head == nullptr; } size_t size() const { return listSize; } void add(T& element) { if ((element.*link).prev != nullptr) _::throwDoubleAdd(); *tail = element; (element.*link).prev = tail; tail = &((element.*link).next); ++listSize; } void addFront(T& element) { if ((element.*link).prev != nullptr) _::throwDoubleAdd(); (element.*link).next = head; (element.*link).prev = &head; KJ_IF_MAYBE(oldHead, head) { (oldHead->*link).prev = &(element.*link).next; } else { tail = &(element.*link).next; } head = element; ++listSize; } void remove(T& element) { if ((element.*link).prev == nullptr) _::throwRemovedNotPresent(); *((element.*link).prev) = (element.*link).next; KJ_IF_MAYBE(n, (element.*link).next) { (n->*link).prev = (element.*link).prev; } else { if (tail != &((element.*link).next)) _::throwRemovedWrongList(); tail = (element.*link).prev; } (element.*link).next = nullptr; (element.*link).prev = nullptr; --listSize; } typedef ListIterator<T, T, link> Iterator; typedef ListIterator<T, const T, link> ConstIterator; Iterator begin() { return Iterator(head); } Iterator end() { return Iterator(nullptr); } ConstIterator begin() const { return ConstIterator(head); } ConstIterator end() const { return ConstIterator(nullptr); } T& front() { return *begin(); } const T& front() const { return *begin(); } private: Maybe<T&> head; Maybe<T&>* tail = &head; size_t listSize = 0; }; template <typename T> class ListLink { public: ListLink(): next(nullptr), prev(nullptr) {} ~ListLink() noexcept { // Intentionally `noexcept` because we want to crash if a dangling pointer was left in a list. if (prev != nullptr) _::throwDestroyedWhileInList(); } KJ_DISALLOW_COPY_AND_MOVE(ListLink); bool isLinked() const { return prev != nullptr; } private: Maybe<T&> next; Maybe<T&>* prev; template <typename U, ListLink<U> U::*link> friend class List; template <typename U, typename MaybeConstU, ListLink<U> U::*link> friend class ListIterator; }; template <typename T, typename MaybeConstT, ListLink<T> T::*link> class ListIterator { public: ListIterator() = default; MaybeConstT& operator*() { KJ_IREQUIRE(current != nullptr, "tried to dereference end of list"); return *_::readMaybe(current); } const T& operator*() const { KJ_IREQUIRE(current != nullptr, "tried to dereference end of list"); return *_::readMaybe(current); } MaybeConstT* operator->() { KJ_IREQUIRE(current != nullptr, "tried to dereference end of list"); return _::readMaybe(current); } const T* operator->() const { KJ_IREQUIRE(current != nullptr, "tried to dereference end of list"); return _::readMaybe(current); } inline ListIterator& operator++() { current = next; next = current.map([](MaybeConstT& obj) -> kj::Maybe<MaybeConstT&> { return (obj.*link).next; }) .orDefault(nullptr); return *this; } inline ListIterator operator++(int) { ListIterator result = *this; ++*this; return result; } inline bool operator==(const ListIterator& other) const { return _::readMaybe(current) == _::readMaybe(other.current); } inline bool operator!=(const ListIterator& other) const { return _::readMaybe(current) != _::readMaybe(other.current); } private: Maybe<MaybeConstT&> current; Maybe<MaybeConstT&> next; // so that the current item can be removed from the list without invalidating the iterator explicit ListIterator(Maybe<MaybeConstT&> start) : current(start), next(start.map([](MaybeConstT& obj) -> kj::Maybe<MaybeConstT&> { return (obj.*link).next; }) .orDefault(nullptr)) {} friend class List<T, link>; }; } // namespace kj KJ_END_HEADER