util/IntrusiveList: add option "constant_time_size"
This commit is contained in:
parent
e437cc4faf
commit
8a68d085b4
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include "Cast.hxx"
|
#include "Cast.hxx"
|
||||||
#include "MemberPointer.hxx"
|
#include "MemberPointer.hxx"
|
||||||
|
#include "OptionalCounter.hxx"
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@ -46,7 +47,7 @@ struct IntrusiveListNode {
|
|||||||
class IntrusiveListHook {
|
class IntrusiveListHook {
|
||||||
template<typename T> friend struct IntrusiveListBaseHookTraits;
|
template<typename T> friend struct IntrusiveListBaseHookTraits;
|
||||||
template<auto member> friend struct IntrusiveListMemberHookTraits;
|
template<auto member> friend struct IntrusiveListMemberHookTraits;
|
||||||
template<typename T, typename HookTraits> friend class IntrusiveList;
|
template<typename T, typename HookTraits, bool> friend class IntrusiveList;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IntrusiveListNode siblings;
|
IntrusiveListNode siblings;
|
||||||
@ -128,6 +129,10 @@ struct IntrusiveListBaseHookTraits {
|
|||||||
template<typename U>
|
template<typename U>
|
||||||
using Hook = typename IntrusiveListHookDetection<U>::type;
|
using Hook = typename IntrusiveListHookDetection<U>::type;
|
||||||
|
|
||||||
|
static constexpr bool IsAutoUnlink() noexcept {
|
||||||
|
return std::is_base_of_v<AutoUnlinkIntrusiveListHook, T>;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
|
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
|
||||||
auto *hook = &Hook<T>::Cast(*node);
|
auto *hook = &Hook<T>::Cast(*node);
|
||||||
return static_cast<T *>(hook);
|
return static_cast<T *>(hook);
|
||||||
@ -156,6 +161,10 @@ struct IntrusiveListMemberHookTraits {
|
|||||||
using _Hook = MemberPointerType<decltype(member)>;
|
using _Hook = MemberPointerType<decltype(member)>;
|
||||||
using Hook = typename IntrusiveListHookDetection<_Hook>::type;
|
using Hook = typename IntrusiveListHookDetection<_Hook>::type;
|
||||||
|
|
||||||
|
static constexpr bool IsAutoUnlink() noexcept {
|
||||||
|
return std::is_base_of_v<AutoUnlinkIntrusiveListHook, _Hook>;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
|
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
|
||||||
auto &hook = Hook::Cast(*node);
|
auto &hook = Hook::Cast(*node);
|
||||||
return &ContainerCast(hook, member);
|
return &ContainerCast(hook, member);
|
||||||
@ -175,13 +184,22 @@ struct IntrusiveListMemberHookTraits {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename HookTraits=IntrusiveListBaseHookTraits<T>>
|
/**
|
||||||
|
* @param constant_time_size make size() constant-time by caching the
|
||||||
|
* number of items in a field?
|
||||||
|
*/
|
||||||
|
template<typename T,
|
||||||
|
typename HookTraits=IntrusiveListBaseHookTraits<T>,
|
||||||
|
bool constant_time_size=false>
|
||||||
class IntrusiveList {
|
class IntrusiveList {
|
||||||
template<typename U>
|
template<typename U>
|
||||||
using Hook = typename IntrusiveListHookDetection<U>::type;
|
using Hook = typename IntrusiveListHookDetection<U>::type;
|
||||||
|
|
||||||
IntrusiveListNode head{&head, &head};
|
IntrusiveListNode head{&head, &head};
|
||||||
|
|
||||||
|
[[no_unique_address]]
|
||||||
|
OptionalCounter<constant_time_size> counter;
|
||||||
|
|
||||||
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
|
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
|
||||||
return HookTraits::Cast(node);
|
return HookTraits::Cast(node);
|
||||||
}
|
}
|
||||||
@ -221,6 +239,9 @@ public:
|
|||||||
|
|
||||||
src.head.next = &src.head;
|
src.head.next = &src.head;
|
||||||
src.head.prev = &src.head;
|
src.head.prev = &src.head;
|
||||||
|
|
||||||
|
using std::swap;
|
||||||
|
swap(counter, src.counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
~IntrusiveList() noexcept {
|
~IntrusiveList() noexcept {
|
||||||
@ -251,6 +272,8 @@ public:
|
|||||||
b.head.next->prev = &b.head;
|
b.head.next->prev = &b.head;
|
||||||
b.head.prev->next = &b.head;
|
b.head.prev->next = &b.head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
swap(a.counter, b.counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool empty() const noexcept {
|
constexpr bool empty() const noexcept {
|
||||||
@ -258,6 +281,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_type size() const noexcept {
|
constexpr size_type size() const noexcept {
|
||||||
|
if constexpr (constant_time_size)
|
||||||
|
return counter;
|
||||||
|
else
|
||||||
return std::distance(begin(), end());
|
return std::distance(begin(), end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,8 +294,10 @@ public:
|
|||||||
is_linked() method will not work */
|
is_linked() method will not work */
|
||||||
while (!empty())
|
while (!empty())
|
||||||
pop_front();
|
pop_front();
|
||||||
} else
|
} else {
|
||||||
head = {&head, &head};
|
head = {&head, &head};
|
||||||
|
counter.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename D>
|
template<typename D>
|
||||||
@ -291,6 +319,7 @@ public:
|
|||||||
|
|
||||||
if (pred(*i)) {
|
if (pred(*i)) {
|
||||||
ToHook(*i).unlink();
|
ToHook(*i).unlink();
|
||||||
|
--counter;
|
||||||
dispose(i);
|
dispose(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,12 +335,14 @@ public:
|
|||||||
|
|
||||||
void pop_front() noexcept {
|
void pop_front() noexcept {
|
||||||
ToHook(front()).unlink();
|
ToHook(front()).unlink();
|
||||||
|
--counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename D>
|
template<typename D>
|
||||||
void pop_front_and_dispose(D &&disposer) noexcept {
|
void pop_front_and_dispose(D &&disposer) noexcept {
|
||||||
auto &i = front();
|
auto &i = front();
|
||||||
ToHook(i).unlink();
|
ToHook(i).unlink();
|
||||||
|
--counter;
|
||||||
disposer(&i);
|
disposer(&i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,6 +352,7 @@ public:
|
|||||||
|
|
||||||
void pop_back() noexcept {
|
void pop_back() noexcept {
|
||||||
ToHook(back()).unlink();
|
ToHook(back()).unlink();
|
||||||
|
--counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
class const_iterator;
|
class const_iterator;
|
||||||
@ -434,6 +466,7 @@ public:
|
|||||||
iterator erase(iterator i) noexcept {
|
iterator erase(iterator i) noexcept {
|
||||||
auto result = std::next(i);
|
auto result = std::next(i);
|
||||||
ToHook(*i).unlink();
|
ToHook(*i).unlink();
|
||||||
|
--counter;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,6 +486,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void insert(iterator p, T &t) noexcept {
|
void insert(iterator p, T &t) noexcept {
|
||||||
|
static_assert(!constant_time_size ||
|
||||||
|
!HookTraits::IsAutoUnlink(),
|
||||||
|
"Can't use auto-unlink hooks with constant_time_size");
|
||||||
|
|
||||||
auto &existing_node = ToNode(*p);
|
auto &existing_node = ToNode(*p);
|
||||||
auto &new_node = ToNode(t);
|
auto &new_node = ToNode(t);
|
||||||
|
|
||||||
@ -460,5 +497,7 @@ public:
|
|||||||
new_node.prev = existing_node.prev;
|
new_node.prev = existing_node.prev;
|
||||||
existing_node.prev = &new_node;
|
existing_node.prev = &new_node;
|
||||||
new_node.next = &existing_node;
|
new_node.next = &existing_node;
|
||||||
|
|
||||||
|
++counter;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
74
src/util/OptionalCounter.hxx
Normal file
74
src/util/OptionalCounter.hxx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 CM4all GmbH
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* author: Max Kellermann <mk@cm4all.com>
|
||||||
|
*
|
||||||
|
* 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 <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
template<bool enable> class OptionalCounter;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class OptionalCounter<false>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr void reset() noexcept {}
|
||||||
|
constexpr auto &operator++() noexcept { return *this; }
|
||||||
|
constexpr auto &operator--() noexcept { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class OptionalCounter<true>
|
||||||
|
{
|
||||||
|
std::size_t value = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr operator std::size_t() const noexcept {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void reset() noexcept {
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto &operator++() noexcept {
|
||||||
|
++value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto &operator--() noexcept {
|
||||||
|
assert(value > 0);
|
||||||
|
|
||||||
|
--value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user