util/IntrusiveSortedList: new class

This commit is contained in:
Max Kellermann 2022-11-13 08:51:18 +01:00
parent 85ab89a08b
commit 51769c40d8
4 changed files with 114 additions and 11 deletions

View File

@ -54,15 +54,7 @@ TimerList::~TimerList() noexcept
void
TimerList::Insert(FineTimerEvent &t) noexcept
{
#ifdef NO_BOOST
auto i = std::find_if(timers.begin(), timers.end(), [due = t.GetDue()](const auto &other){
return other.GetDue() >= due;
});
timers.insert(i, t);
#else
timers.insert(t);
#endif
}
Event::Duration

View File

@ -34,9 +34,10 @@
#include "Chrono.hxx"
#include "event/Features.h"
#include "util/IntrusiveList.hxx"
#ifndef NO_BOOST
#ifdef NO_BOOST
#include "util/IntrusiveSortedList.hxx"
#else
#include <boost/intrusive/set.hpp>
#endif
@ -55,7 +56,7 @@ class TimerList final {
/* when building without Boost, then this is just a sorted
doubly-linked list - this doesn't scale well, but is good
enough for most programs */
IntrusiveList<FineTimerEvent> timers;
IntrusiveSortedList<FineTimerEvent, Compare> timers;
#else
boost::intrusive::multiset<FineTimerEvent,
boost::intrusive::base_hook<boost::intrusive::set_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>>,

View File

@ -0,0 +1,65 @@
/*
* Copyright 2020-2022 Max Kellermann <max.kellermann@gmail.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 "IntrusiveList.hxx"
/**
* A variant of #IntrusiveList which is sorted automatically. There
* are obvious scalability problems with this approach, so use with
* care.
*/
template<typename T, typename Compare=typename T::Compare,
typename HookTraits=IntrusiveListBaseHookTraits<T>,
bool constant_time_size=false>
class IntrusiveSortedList
: public IntrusiveList<T, HookTraits, constant_time_size>
{
using Base = IntrusiveList<T, HookTraits, constant_time_size>;
[[no_unique_address]]
Compare compare;
public:
constexpr IntrusiveSortedList() noexcept = default;
IntrusiveSortedList(IntrusiveSortedList &&src) noexcept = default;
using typename Base::reference;
using Base::begin;
using Base::end;
void insert(reference item) noexcept {
auto position = std::find_if(begin(), end(), [this, &item](const auto &other){
return !compare(other, item);
});
Base::insert(position, item);
}
};

View File

@ -260,3 +260,48 @@ TEST(IntrusiveList, Sort)
ASSERT_EQ(&*std::next(list.begin(), 1), &items[2]);
ASSERT_EQ(&*std::next(list.begin(), 2), &items[4]);
}
#include "util/IntrusiveSortedList.hxx"
TEST(IntrusiveSortedList, Basic)
{
using Item = CharItem<IntrusiveHookMode::NORMAL>;
struct Compare {
constexpr bool operator()(const Item &a, const Item &b) noexcept {
return a.ch < b.ch;
}
};
Item items[]{'z', 'a', 'b', 'q', 'b', 'c', 't', 'm', 'y'};
IntrusiveSortedList<Item, Compare> list;
ASSERT_EQ(ToString(list, list.begin(), 2), "__");
list.insert(items[0]);
ASSERT_EQ(ToString(list, list.begin(), 3), "z_z");
list.insert(items[1]);
ASSERT_EQ(ToString(list, list.begin(), 4), "az_a");
list.insert(items[2]);
ASSERT_EQ(ToString(list, list.begin(), 5), "abz_a");
list.insert(items[3]);
ASSERT_EQ(ToString(list, list.begin(), 6), "abqz_a");
list.insert(items[4]);
ASSERT_EQ(ToString(list, list.begin(), 7), "abbqz_a");
list.insert(items[5]);
ASSERT_EQ(ToString(list, list.begin(), 8), "abbcqz_a");
list.insert(items[6]);
ASSERT_EQ(ToString(list, list.begin(), 9), "abbcqtz_a");
list.insert(items[7]);
ASSERT_EQ(ToString(list, list.begin(), 10), "abbcmqtz_a");
list.insert(items[8]);
ASSERT_EQ(ToString(list, list.begin(), 11), "abbcmqtyz_a");
}