diff --git a/src/event/TimerList.cxx b/src/event/TimerList.cxx index 5110b4970..300e8fbf8 100644 --- a/src/event/TimerList.cxx +++ b/src/event/TimerList.cxx @@ -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 diff --git a/src/event/TimerList.hxx b/src/event/TimerList.hxx index 120ea9e08..a0708add1 100644 --- a/src/event/TimerList.hxx +++ b/src/event/TimerList.hxx @@ -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 #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 timers; + IntrusiveSortedList timers; #else boost::intrusive::multiset>>, diff --git a/src/util/IntrusiveSortedList.hxx b/src/util/IntrusiveSortedList.hxx new file mode 100644 index 000000000..27e654384 --- /dev/null +++ b/src/util/IntrusiveSortedList.hxx @@ -0,0 +1,65 @@ +/* + * Copyright 2020-2022 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 "IntrusiveList.hxx" + +/** + * A variant of #IntrusiveList which is sorted automatically. There + * are obvious scalability problems with this approach, so use with + * care. + */ +template, + bool constant_time_size=false> +class IntrusiveSortedList + : public IntrusiveList +{ + using Base = IntrusiveList; + + [[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); + } +}; diff --git a/test/util/TestIntrusiveList.cxx b/test/util/TestIntrusiveList.cxx index a553c1979..dbe940bb9 100644 --- a/test/util/TestIntrusiveList.cxx +++ b/test/util/TestIntrusiveList.cxx @@ -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; + + 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 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"); +}