diff --git a/src/util/IntrusiveForwardList.hxx b/src/util/IntrusiveForwardList.hxx new file mode 100644 index 000000000..7a3c5cec7 --- /dev/null +++ b/src/util/IntrusiveForwardList.hxx @@ -0,0 +1,359 @@ +/* + * Copyright 2020-2022 CM4all GmbH + * All rights reserved. + * + * author: Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Cast.hxx" +#include "MemberPointer.hxx" +#include "OptionalCounter.hxx" +#include "ShallowCopy.hxx" + +#include +#include +#include + +struct IntrusiveForwardListNode { + IntrusiveForwardListNode *next = nullptr; +}; + +struct IntrusiveForwardListHook { + IntrusiveForwardListNode siblings; + + static constexpr auto &Cast(IntrusiveForwardListNode &node) noexcept { + return ContainerCast(node, &IntrusiveForwardListHook::siblings); + } + + static constexpr const auto &Cast(const IntrusiveForwardListNode &node) noexcept { + return ContainerCast(node, &IntrusiveForwardListHook::siblings); + } +}; + +/** + * For classes which embed #IntrusiveForwardListHook as base class. + */ +template +struct IntrusiveForwardListBaseHookTraits { + static constexpr T *Cast(IntrusiveForwardListNode *node) noexcept { + static_assert(std::is_base_of_v); + auto *hook = &IntrusiveForwardListHook::Cast(*node); + return static_cast(hook); + } + + static constexpr const T *Cast(const IntrusiveForwardListNode *node) noexcept { + static_assert(std::is_base_of_v); + const auto *hook = &IntrusiveForwardListHook::Cast(*node); + return static_cast(hook); + } + + static constexpr IntrusiveForwardListHook &ToHook(T &t) noexcept { + static_assert(std::is_base_of_v); + return t; + } + + static constexpr const IntrusiveForwardListHook &ToHook(const T &t) noexcept { + static_assert(std::is_base_of_v); + return t; + } +}; + +/** + * For classes which embed #IntrusiveForwardListHook as member. + */ +template +struct IntrusiveForwardListMemberHookTraits { + using T = MemberPointerContainerType; + using Hook = IntrusiveForwardListHook; + + static_assert(std::is_same_v, Hook>); + + static constexpr T *Cast(IntrusiveForwardListNode *node) noexcept { + auto &hook = Hook::Cast(*node); + return &ContainerCast(hook, member); + } + + static constexpr const T *Cast(const IntrusiveForwardListNode *node) noexcept { + const auto &hook = Hook::Cast(*node); + return &ContainerCast(hook, member); + } + + static constexpr auto &ToHook(T &t) noexcept { + return t.*member; + } + + static constexpr const auto &ToHook(const T &t) noexcept { + return t.*member; + } +}; + +/** + * @param constant_time_size make size() constant-time by caching the + * number of items in a field? + */ +template, + bool constant_time_size=false> +class IntrusiveForwardList { + IntrusiveForwardListNode head; + + [[no_unique_address]] + OptionalCounter counter; + + static constexpr T *Cast(IntrusiveForwardListNode *node) noexcept { + return HookTraits::Cast(node); + } + + static constexpr const T *Cast(const IntrusiveForwardListNode *node) noexcept { + return HookTraits::Cast(node); + } + + static constexpr IntrusiveForwardListHook &ToHook(T &t) noexcept { + return HookTraits::ToHook(t); + } + + static constexpr const IntrusiveForwardListHook &ToHook(const T &t) noexcept { + return HookTraits::ToHook(t); + } + + static constexpr IntrusiveForwardListNode &ToNode(T &t) noexcept { + return ToHook(t).siblings; + } + + static constexpr const IntrusiveForwardListNode &ToNode(const T &t) noexcept { + return ToHook(t).siblings; + } + +public: + using size_type = std::size_t; + + IntrusiveForwardList() = default; + + IntrusiveForwardList(IntrusiveForwardList &&src) noexcept + :head{std::exchange(src.head.next, nullptr)} + { + using std::swap; + swap(counter, src.counter); + } + + constexpr IntrusiveForwardList(ShallowCopy, const IntrusiveForwardList &src) noexcept + :head(src.head) + { + // shallow copies mess with the counter + static_assert(!constant_time_size); + } + + IntrusiveForwardList &operator=(IntrusiveForwardList &&src) noexcept { + using std::swap; + swap(head, src.head); + swap(counter, counter); + return *this; + } + + constexpr bool empty() const noexcept { + return head.next == nullptr; + } + + constexpr size_type size() const noexcept { + if constexpr (constant_time_size) + return counter; + else + return std::distance(begin(), end()); + } + + void clear() noexcept { + head = {}; + counter.reset(); + } + + template + void clear_and_dispose(D &&disposer) noexcept { + while (!empty()) { + auto *item = &front(); + pop_front(); + disposer(item); + } + } + + const T &front() const noexcept { + return *Cast(head.next); + } + + T &front() noexcept { + return *Cast(head.next); + } + + void pop_front() noexcept { + head.next = head.next->next; + --counter; + } + + class const_iterator; + + class iterator final { + friend IntrusiveForwardList; + friend const_iterator; + + IntrusiveForwardListNode *cursor; + + constexpr iterator(IntrusiveForwardListNode *_cursor) noexcept + :cursor(_cursor) {} + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + + iterator() = default; + + constexpr bool operator==(const iterator &other) const noexcept { + return cursor == other.cursor; + } + + constexpr bool operator!=(const iterator &other) const noexcept { + return !(*this == other); + } + + constexpr T &operator*() const noexcept { + return *Cast(cursor); + } + + constexpr T *operator->() const noexcept { + return Cast(cursor); + } + + iterator &operator++() noexcept { + cursor = cursor->next; + return *this; + } + }; + + constexpr iterator before_begin() noexcept { + return {&head}; + } + + constexpr iterator begin() noexcept { + return {head.next}; + } + + static constexpr iterator end() noexcept { + return {nullptr}; + } + + class const_iterator final { + friend IntrusiveForwardList; + + const IntrusiveForwardListNode *cursor; + + constexpr const_iterator(const IntrusiveForwardListNode *_cursor) noexcept + :cursor(_cursor) {} + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = const T; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + + const_iterator() = default; + + const_iterator(iterator src) noexcept + :cursor(src.cursor) {} + + constexpr bool operator==(const const_iterator &other) const noexcept { + return cursor == other.cursor; + } + + constexpr bool operator!=(const const_iterator &other) const noexcept { + return !(*this == other); + } + + constexpr const T &operator*() const noexcept { + return *Cast(cursor); + } + + constexpr const T *operator->() const noexcept { + return Cast(cursor); + } + + const_iterator &operator++() noexcept { + cursor = cursor->next; + return *this; + } + }; + + constexpr const_iterator begin() const noexcept { + return {head.next}; + } + + void push_front(T &t) noexcept { + auto &new_node = ToNode(t); + new_node.next = head.next; + head.next = &new_node; + ++counter; + } + + static iterator insert_after(iterator pos, T &t) noexcept { + // no counter update in this static method + static_assert(!constant_time_size); + + auto &pos_node = *pos.cursor; + auto &new_node = ToNode(t); + new_node.next = pos_node.next; + pos_node.next = &new_node; + return &new_node; + } + + void erase_after(iterator pos) noexcept { + pos.cursor->next = pos.cursor->next->next; + --counter; + } + + void reverse() noexcept { + if (empty()) + return; + + /* the first item will be the last, and will stay + there; during the loop, it will divide the list + between "old order" (right of it) and "new + (reversed) order" (left of it) */ + const auto middle = begin(); + + while (std::next(middle) != end()) { + /* remove the item after the "middle", and + move it to the front */ + auto i = std::next(middle); + erase_after(middle); + push_front(*i); + } + } +}; diff --git a/src/util/ShallowCopy.hxx b/src/util/ShallowCopy.hxx new file mode 100644 index 000000000..571d6d82b --- /dev/null +++ b/src/util/ShallowCopy.hxx @@ -0,0 +1,43 @@ +/* + * Copyright 2007-2022 CM4all GmbH + * All rights reserved. + * + * author: Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SHALLOW_COPY_HXX +#define SHALLOW_COPY_HXX + +/** + * A tag for overloading copying constructors, telling them to make + * shallow copies of source data (e.g. copy pointers instead of + * duplicating the referenced objects). + */ +struct ShallowCopy {}; + +#endif