util/IntrusiveForwardList: add option cache_last
This commit is contained in:
parent
64b0587e78
commit
f448bfd3f3
@ -8,6 +8,7 @@
|
|||||||
#include "Concepts.hxx"
|
#include "Concepts.hxx"
|
||||||
#include "MemberPointer.hxx"
|
#include "MemberPointer.hxx"
|
||||||
#include "OptionalCounter.hxx"
|
#include "OptionalCounter.hxx"
|
||||||
|
#include "OptionalField.hxx"
|
||||||
#include "ShallowCopy.hxx"
|
#include "ShallowCopy.hxx"
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@ -16,6 +17,12 @@
|
|||||||
|
|
||||||
struct IntrusiveForwardListOptions {
|
struct IntrusiveForwardListOptions {
|
||||||
bool constant_time_size = false;
|
bool constant_time_size = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache a pointer to the last item? This makes back() and
|
||||||
|
* push_back() run in constant time.
|
||||||
|
*/
|
||||||
|
bool cache_last = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IntrusiveForwardListNode {
|
struct IntrusiveForwardListNode {
|
||||||
@ -103,6 +110,9 @@ class IntrusiveForwardList {
|
|||||||
|
|
||||||
IntrusiveForwardListNode head{nullptr};
|
IntrusiveForwardListNode head{nullptr};
|
||||||
|
|
||||||
|
[[no_unique_address]]
|
||||||
|
OptionalField<IntrusiveForwardListNode *, options.cache_last> last_cache{&head};
|
||||||
|
|
||||||
[[no_unique_address]]
|
[[no_unique_address]]
|
||||||
OptionalCounter<constant_time_size> counter;
|
OptionalCounter<constant_time_size> counter;
|
||||||
|
|
||||||
@ -144,6 +154,7 @@ public:
|
|||||||
:head{std::exchange(src.head.next, nullptr)}
|
:head{std::exchange(src.head.next, nullptr)}
|
||||||
{
|
{
|
||||||
using std::swap;
|
using std::swap;
|
||||||
|
swap(last_cache, src.last_cache);
|
||||||
swap(counter, src.counter);
|
swap(counter, src.counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,11 +163,13 @@ public:
|
|||||||
{
|
{
|
||||||
// shallow copies mess with the counter
|
// shallow copies mess with the counter
|
||||||
static_assert(!options.constant_time_size);
|
static_assert(!options.constant_time_size);
|
||||||
|
static_assert(!options.cache_last);
|
||||||
}
|
}
|
||||||
|
|
||||||
IntrusiveForwardList &operator=(IntrusiveForwardList &&src) noexcept {
|
IntrusiveForwardList &operator=(IntrusiveForwardList &&src) noexcept {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(head, src.head);
|
swap(head, src.head);
|
||||||
|
swap(last_cache, src.last_cache);
|
||||||
swap(counter, src.counter);
|
swap(counter, src.counter);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -174,6 +187,7 @@ public:
|
|||||||
|
|
||||||
void clear() noexcept {
|
void clear() noexcept {
|
||||||
head = {};
|
head = {};
|
||||||
|
last_cache = {};
|
||||||
counter.reset();
|
counter.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +197,8 @@ public:
|
|||||||
pop_front();
|
pop_front();
|
||||||
disposer(item);
|
disposer(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
last_cache = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const_reference front() const noexcept {
|
const_reference front() const noexcept {
|
||||||
@ -196,6 +212,11 @@ public:
|
|||||||
reference pop_front() noexcept {
|
reference pop_front() noexcept {
|
||||||
auto &i = front();
|
auto &i = front();
|
||||||
head.next = head.next->next;
|
head.next = head.next->next;
|
||||||
|
|
||||||
|
if constexpr (options.cache_last)
|
||||||
|
if (head.next == nullptr)
|
||||||
|
last_cache.value = &head;
|
||||||
|
|
||||||
--counter;
|
--counter;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -205,6 +226,16 @@ public:
|
|||||||
disposer(&i);
|
disposer(&i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const_reference back() const noexcept
|
||||||
|
requires(options.cache_last) {
|
||||||
|
return *Cast(last_cache.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference back() noexcept
|
||||||
|
requires(options.cache_last) {
|
||||||
|
return *Cast(last_cache.value);
|
||||||
|
}
|
||||||
|
|
||||||
class const_iterator;
|
class const_iterator;
|
||||||
|
|
||||||
class iterator final {
|
class iterator final {
|
||||||
@ -265,6 +296,11 @@ public:
|
|||||||
return {nullptr};
|
return {nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr iterator last() noexcept
|
||||||
|
requires(options.cache_last) {
|
||||||
|
return {last_cache.value};
|
||||||
|
}
|
||||||
|
|
||||||
class const_iterator final {
|
class const_iterator final {
|
||||||
friend IntrusiveForwardList;
|
friend IntrusiveForwardList;
|
||||||
|
|
||||||
@ -317,15 +353,35 @@ public:
|
|||||||
return {head.next};
|
return {head.next};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator last() const noexcept
|
||||||
|
requires(options.cache_last) {
|
||||||
|
return {last_cache.value};
|
||||||
|
}
|
||||||
|
|
||||||
void push_front(reference t) noexcept {
|
void push_front(reference t) noexcept {
|
||||||
auto &new_node = ToNode(t);
|
auto &new_node = ToNode(t);
|
||||||
|
|
||||||
|
if constexpr (options.cache_last)
|
||||||
|
if (empty())
|
||||||
|
last_cache.value = &new_node;
|
||||||
|
|
||||||
new_node.next = head.next;
|
new_node.next = head.next;
|
||||||
head.next = &new_node;
|
head.next = &new_node;
|
||||||
++counter;
|
++counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void push_back(reference t) noexcept
|
||||||
|
requires(options.cache_last) {
|
||||||
|
auto &new_node = ToNode(t);
|
||||||
|
new_node.next = nullptr;
|
||||||
|
last_cache.value->next = &new_node;
|
||||||
|
last_cache.value = &new_node;
|
||||||
|
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
|
||||||
static iterator insert_after(iterator pos, reference t) noexcept
|
static iterator insert_after(iterator pos, reference t) noexcept
|
||||||
requires(!constant_time_size) {
|
requires(!constant_time_size && !options.cache_last) {
|
||||||
/* if we have no counter, then this method is allowed
|
/* if we have no counter, then this method is allowed
|
||||||
to be static */
|
to be static */
|
||||||
|
|
||||||
@ -336,9 +392,15 @@ public:
|
|||||||
return &new_node;
|
return &new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator insert_after(iterator pos, reference t) noexcept {
|
iterator insert_after(iterator pos, reference t) noexcept
|
||||||
|
requires(constant_time_size || options.cache_last) {
|
||||||
auto &pos_node = *pos.cursor;
|
auto &pos_node = *pos.cursor;
|
||||||
auto &new_node = ToNode(t);
|
auto &new_node = ToNode(t);
|
||||||
|
|
||||||
|
if constexpr (options.cache_last)
|
||||||
|
if (pos_node.next == nullptr)
|
||||||
|
last_cache.value = &new_node;
|
||||||
|
|
||||||
new_node.next = pos_node.next;
|
new_node.next = pos_node.next;
|
||||||
pos_node.next = &new_node;
|
pos_node.next = &new_node;
|
||||||
++counter;
|
++counter;
|
||||||
@ -347,6 +409,11 @@ public:
|
|||||||
|
|
||||||
void erase_after(iterator pos) noexcept {
|
void erase_after(iterator pos) noexcept {
|
||||||
pos.cursor->next = pos.cursor->next->next;
|
pos.cursor->next = pos.cursor->next->next;
|
||||||
|
|
||||||
|
if constexpr (options.cache_last)
|
||||||
|
if (pos.cursor->next == nullptr)
|
||||||
|
last_cache.value = pos.cursor;
|
||||||
|
|
||||||
--counter;
|
--counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ struct CharItem final : IntrusiveForwardListHook {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static std::string
|
static std::string
|
||||||
ToString(const IntrusiveForwardList<CharItem> &list) noexcept
|
ToString(const auto &list) noexcept
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
for (const auto &i : list)
|
for (const auto &i : list)
|
||||||
@ -50,3 +50,89 @@ TEST(IntrusiveForwardList, Basic)
|
|||||||
list.reverse();
|
list.reverse();
|
||||||
ASSERT_EQ(ToString(list), "cb");
|
ASSERT_EQ(ToString(list), "cb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(IntrusiveForwardList, CacheLast)
|
||||||
|
{
|
||||||
|
using Item = CharItem;
|
||||||
|
|
||||||
|
Item items[]{'a', 'b', 'c'};
|
||||||
|
|
||||||
|
using List =IntrusiveForwardList<
|
||||||
|
CharItem, IntrusiveForwardListBaseHookTraits<CharItem>,
|
||||||
|
IntrusiveForwardListOptions{.cache_last = true}>;
|
||||||
|
List list;
|
||||||
|
ASSERT_EQ(ToString(list), "");
|
||||||
|
list.reverse();
|
||||||
|
ASSERT_EQ(ToString(list), "");
|
||||||
|
|
||||||
|
for (auto &i : items)
|
||||||
|
list.push_front(i);
|
||||||
|
|
||||||
|
ASSERT_EQ(ToString(list), "cba");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
|
||||||
|
list.erase_after(list.begin());
|
||||||
|
ASSERT_EQ(ToString(list), "ca");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
|
||||||
|
list.reverse();
|
||||||
|
ASSERT_EQ(ToString(list), "ac");
|
||||||
|
ASSERT_EQ(&list.back(), &items[2]);
|
||||||
|
|
||||||
|
list.erase_after(list.begin());
|
||||||
|
ASSERT_EQ(ToString(list), "a");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
|
||||||
|
list.reverse();
|
||||||
|
ASSERT_EQ(ToString(list), "a");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
|
||||||
|
list.pop_front();
|
||||||
|
ASSERT_EQ(ToString(list), "");
|
||||||
|
|
||||||
|
list.insert_after(list.before_begin(), items[0]);
|
||||||
|
ASSERT_EQ(ToString(list), "a");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
|
||||||
|
list.insert_after(list.before_begin(), items[1]);
|
||||||
|
ASSERT_EQ(ToString(list), "ba");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
|
||||||
|
list.pop_front();
|
||||||
|
ASSERT_EQ(ToString(list), "a");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
|
||||||
|
list.pop_front();
|
||||||
|
ASSERT_EQ(ToString(list), "");
|
||||||
|
|
||||||
|
for (auto &i : items)
|
||||||
|
list.push_back(i);
|
||||||
|
|
||||||
|
ASSERT_EQ(ToString(list), "abc");
|
||||||
|
ASSERT_EQ(&list.back(), &items[2]);
|
||||||
|
|
||||||
|
/* move constructor */
|
||||||
|
auto list2 = std::move(list);
|
||||||
|
ASSERT_EQ(ToString(list2), "abc");
|
||||||
|
ASSERT_EQ(&list2.back(), &items[2]);
|
||||||
|
ASSERT_EQ(ToString(list), "");
|
||||||
|
|
||||||
|
/* move operator */
|
||||||
|
list = std::move(list2);
|
||||||
|
ASSERT_EQ(ToString(list), "abc");
|
||||||
|
ASSERT_EQ(&list.back(), &items[2]);
|
||||||
|
ASSERT_EQ(ToString(list2), "");
|
||||||
|
|
||||||
|
list.erase_after(list.begin());
|
||||||
|
ASSERT_EQ(ToString(list), "ac");
|
||||||
|
ASSERT_EQ(&list.back(), &items[2]);
|
||||||
|
|
||||||
|
list.erase_after(list.before_begin());
|
||||||
|
ASSERT_EQ(ToString(list), "c");
|
||||||
|
ASSERT_EQ(&list.back(), &items[2]);
|
||||||
|
|
||||||
|
// insert_after()
|
||||||
|
list.insert_after(list.begin(), items[0]);
|
||||||
|
ASSERT_EQ(ToString(list), "ca");
|
||||||
|
ASSERT_EQ(&list.back(), &items[0]);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user