Mercurial > repos > rliterman > csp2
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/list.h Tue Mar 18 17:55:14 2025 -0400 @@ -0,0 +1,227 @@ +// 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