96 :
public detail::ListSignalMixin<SortedList<T, Source>, T> {
97 friend detail::ListSignalMixin<SortedList<T, Source>, T>;
112 struct ComparatorTag {};
113 struct ComparatorState {
114 using Scan = bool (*)(ComparatorState&,
const std::vector<std::shared_ptr<T>>&,
115 const std::vector<std::size_t>&, std::optional<std::size_t>);
117 void* target =
nullptr;
119 bool operator()(
const T& a,
const T& b)
const {
return scalar(a, b); }
122 template<
typename Fn>
123 static bool scan_order(Fn& comparator,
const std::vector<std::shared_ptr<T>>& items,
124 const std::vector<std::size_t>& sources,
125 std::optional<std::size_t> skip) {
126 const auto size = items.size();
127 const auto skipped = skip.value_or(
size);
128 const std::size_t first = skipped == 0 ? 1 : 0;
129 if (first >=
size) {
return true; }
130 const T* previous = items[first].get();
131 auto previous_source = sources[first];
132 for (
auto d = first + 1; d <
size; ++d) {
133 if (d == skipped) {
continue; }
134 const T* current = items[d].get();
135 const auto current_source = sources[d];
136 const bool reversed = current_source < previous_source;
137 const T* lhs = reversed ? previous : current;
138 const T* rhs = reversed ? current : previous;
139 if (
static_cast<bool>(std::invoke(comparator, *lhs, *rhs)) != reversed) {
return false; }
141 previous_source = current_source;
146 static std::shared_ptr<ComparatorState> make_comparator(
Comparator comparator) {
147 auto result = std::make_shared<ComparatorState>();
148 result->scalar = std::move(comparator);
149 result->scan = [](ComparatorState& self,
const auto& items,
const auto& sources,
auto skip) {
150 return scan_order(self.scalar, items, sources, skip);
155 template<
typename Fn>
156 static std::shared_ptr<ComparatorState> make_comparator(Fn&& comparator) {
157 using Stored = std::decay_t<Fn>;
159 static_assert(
sizeof(Stored) <= 32);
160 static_assert(
alignof(Stored) <=
alignof(std::max_align_t));
161 struct OwnedComparator {
162 ComparatorState state;
166 explicit OwnedComparator(Fn&& fn) : callable(std::forward<Fn>(fn)) {}
168 auto owned = std::make_shared<OwnedComparator>(std::forward<Fn>(comparator));
169 auto& result = owned->state;
170 result.target = &owned->callable;
171 result.scalar = [target = &owned->callable](
const T& a,
const T& b) {
172 return static_cast<bool>(std::invoke(*target, a, b));
174 result.scan = [](ComparatorState& self,
const auto& items,
const auto& sources,
auto skip) {
175 return scan_order(*
static_cast<Stored*
>(self.target), items, sources, skip);
177 return std::shared_ptr<ComparatorState>{owned, &owned->state};
182 :
SortedList(
std::move(source), make_comparator(
std::move(comparator)), ComparatorTag{}) {}
184 template<
typename Fn>
185 requires (!std::is_same_v<std::decay_t<Fn>,
Comparator> &&
186 !std::is_pointer_v<std::decay_t<Fn>> &&
187 std::is_copy_constructible_v<std::decay_t<Fn>> &&
188 std::is_invocable_r_v<bool, std::decay_t<Fn>&,
const T&,
const T&>)
190 :
SortedList(
std::move(source), make_comparator(
std::forward<Fn>(comparator)), ComparatorTag{}) {}
193 SortedList(std::shared_ptr<Source> source, std::shared_ptr<ComparatorState> comparator, ComparatorTag )
194 : source_(
std::move(source)),
196 state_(
std::make_shared<SharedState>())
198 state_->comparator = std::move(comparator);
206 std::unique_lock lk(state_->m);
207 state_->source_items = source_->snapshot();
208 rebuild_from_snapshot_unlocked_(*state_, state_->source_items);
212 std::weak_ptr<SharedState> weak_state = state_;
213 std::weak_ptr<Signal> weak_signal = signal_;
214 std::weak_ptr<Source> weak_source{source_};
215 source_sub_ = source_->observe(
216 [weak_state, weak_signal, weak_source](
const ListChange<T>& ch) {
217 auto st = weak_state.lock();
218 auto sig = weak_signal.lock();
219 auto src = weak_source.lock();
220 if (!st || !sig || !src)
return;
221 st->updates.submit([st, sig, ch] { dispatch_source_change_(*st, *sig, ch); });
232 [[nodiscard]] std::size_t
size()
const {
233 std::shared_lock lk(state_->m);
234 return state_->items.size();
237 [[nodiscard]]
bool empty()
const {
return size() == 0; }
239 [[nodiscard]] std::shared_ptr<T>
at(std::size_t derived_index)
const {
240 std::shared_lock lk(state_->m);
241 return state_->items.at(derived_index);
244 [[nodiscard]] std::vector<std::shared_ptr<T>>
snapshot()
const {
245 std::shared_lock lk(state_->m);
246 return state_->items;
249 [[nodiscard]] std::optional<std::size_t>
251 std::shared_lock lk(state_->m);
252 if (derived_index >= state_->derived_to_source.size())
return std::nullopt;
253 return state_->derived_to_source[derived_index];
263 replace_comparator(make_comparator(std::move(new_comparator)));
266 template<
typename Fn>
267 requires (!std::is_same_v<std::decay_t<Fn>,
Comparator> &&
268 !std::is_pointer_v<std::decay_t<Fn>> &&
269 std::is_copy_constructible_v<std::decay_t<Fn>> &&
270 std::is_invocable_r_v<bool, std::decay_t<Fn>&,
const T&,
const T&>)
272 replace_comparator(make_comparator(std::forward<Fn>(comparator)));
276 void replace_comparator(std::shared_ptr<ComparatorState> comparator) {
278 auto signal = signal_;
279 state->updates.submit([state, signal, comparator = std::move(comparator)] {
281 work.comparator = comparator;
282 rebuild_from_snapshot_unlocked_(work, state->source_items);
284 commit(*state, work);
285 state->scratch.reset();
286 signal->emit(std::move(reset));
292 mutable std::shared_mutex m;
293 std::shared_ptr<ComparatorState> comparator;
294 detail::ListUpdateQueue updates;
295 std::vector<std::shared_ptr<T>> source_items;
296 bool needs_rebuild =
false;
297 std::unique_ptr<SharedState> scratch;
299 std::vector<std::size_t> source_to_derived;
301 std::vector<std::size_t> derived_to_source;
303 std::vector<std::shared_ptr<T>> items;
306 std::shared_ptr<Source> source_;
307 std::shared_ptr<Signal> signal_;
308 std::shared_ptr<SharedState> state_;
309 Subscription source_sub_;
314 static void rebuild_from_snapshot_unlocked_(
316 std::vector<std::shared_ptr<T>> snap)
318 std::vector<std::size_t> idx(snap.size());
319 for (std::size_t i = 0; i < snap.size(); ++i) idx[i] = i;
320 std::stable_sort(idx.begin(), idx.end(),
321 [&](std::size_t a, std::size_t b) {
322 return (*st.comparator)(*snap[a], *snap[b]);
325 st.source_to_derived.assign(snap.size(), 0);
326 st.derived_to_source.assign(idx.size(), 0);
328 st.items.reserve(idx.size());
329 for (std::size_t d = 0; d < idx.size(); ++d) {
330 const std::size_t s = idx[d];
331 st.derived_to_source[d] = s;
332 st.source_to_derived[s] = d;
333 st.items.push_back(snap[s]);
338 struct EmissionBuffer {
339 std::vector<ListChange<T>> events;
340 void emit(ListChange<T> event) { events.push_back(std::move(event)); }
341 void emit_batch(std::vector<ListChange<T>>
batch) {
342 events.reserve(events.size() +
batch.size());
343 for (
auto& event :
batch) { events.push_back(std::move(event)); }
347 static void commit(SharedState& st, SharedState& work) {
348 std::unique_lock lock(st.m);
349 st.comparator.swap(work.comparator);
350 st.source_to_derived.swap(work.source_to_derived);
351 st.derived_to_source.swap(work.derived_to_source);
352 st.items.swap(work.items);
353 st.needs_rebuild =
false;
360 template<
typename Value>
361 static void reserve_append(std::vector<Value>& values) {
362 if (values.size() != values.capacity()) {
return; }
363 const auto capacity = values.capacity();
364 const auto maximum = values.max_size();
365 if (capacity == maximum) {
throw std::length_error(
"SortedList capacity exceeded"); }
367 if (capacity == 0) { next = 1; }
368 else if (capacity <= maximum / 2) { next = capacity * 2; }
369 values.reserve(next);
372 static void dispatch_source_change_(SharedState& st,
374 const ListChange<T>& ch) {
375 detail::replay_list_change(st.source_items, ch);
376 const bool rebuild = st.needs_rebuild;
377 st.needs_rebuild =
true;
379 ordered_unlocked_(st)) {
380 const auto position = binary_search_insert_(st, *ch.item, ch.index);
381 std::vector<ListChange<T>> events;
384 std::unique_lock lock(st.m);
388 reserve_append(st.items);
389 reserve_append(st.source_to_derived);
390 reserve_append(st.derived_to_source);
391 st.items.insert(st.items.begin() +
static_cast<std::ptrdiff_t
>(position), ch.item);
392 st.derived_to_source.insert(
393 st.derived_to_source.begin() +
static_cast<std::ptrdiff_t
>(position), ch.index);
394 st.source_to_derived.push_back(position);
395 renumber_s2d_(st, position);
396 st.needs_rebuild =
false;
401 sig.emit_batch(std::move(events));
405 auto scratch = std::make_unique<SharedState>();
406 scratch->comparator = st.comparator;
407 scratch->source_to_derived = st.source_to_derived;
408 scratch->derived_to_source = st.derived_to_source;
409 scratch->items = st.items;
410 st.scratch = std::move(scratch);
412 auto& work = *st.scratch;
413 EmissionBuffer buffered;
415 rebuild_from_snapshot_unlocked_(work, st.source_items);
434 for (
const auto& event : buffered.events) {
435 detail::replay_list_change(work.items, event);
437 work.source_to_derived = st.source_to_derived;
438 work.derived_to_source = st.derived_to_source;
439 work.comparator = st.comparator;
443 sig.emit_batch(std::move(buffered.events));
468 static std::size_t binary_search_insert_(
469 const SharedState& st,
const T& needle, std::size_t needle_src_i,
470 std::optional<std::size_t> skip_derived_idx = std::nullopt)
472 const auto& cmp = *st.comparator;
473 const auto& items = st.items;
474 const auto& d2s = st.derived_to_source;
480 auto real = [&](std::size_t compressed) -> std::size_t {
481 if (skip_derived_idx && compressed >= *skip_derived_idx)
482 return compressed + 1;
485 const std::size_t n_eff = items.size()
486 - (skip_derived_idx ? 1u : 0u);
489 std::size_t hi = n_eff;
491 const std::size_t mid = lo + (hi - lo) / 2;
492 const std::size_t r = real(mid);
493 const auto& mid_item = *items[r];
494 if (cmp(needle, mid_item)) {
496 }
else if (cmp(mid_item, needle)) {
501 if (needle_src_i < d2s[r]) hi = mid;
508 static void handle_insert_(SharedState& st, EmissionBuffer& sig,
509 const ListChange<T>& ch) {
510 std::unique_lock lk(st.m);
511 const std::size_t src_idx = ch.index;
515 if (src_idx != st.items.size()) {
516 for (
auto& s : st.derived_to_source) {
517 if (s >= src_idx) ++s;
520 st.source_to_derived.insert(st.source_to_derived.begin()
521 +
static_cast<std::ptrdiff_t
>(src_idx),
526 auto shared = ch.item;
528 const bool ordered = ordered_unlocked_(st);
529 const std::size_t d_idx = ordered ? binary_search_insert_(st, *shared, src_idx) : st.items.
size();
531 st.derived_to_source.insert(st.derived_to_source.begin()
532 +
static_cast<std::ptrdiff_t
>(d_idx),
534 st.items.insert(st.items.begin()
535 +
static_cast<std::ptrdiff_t
>(d_idx),
540 renumber_s2d_(st, d_idx);
543 auto moves = reorder_unlocked_(st);
544 moves.insert(moves.begin(), {ListChangeKind::Insert, d_idx, ch.item, 0});
546 sig.emit_batch(std::move(moves));
553 static void handle_remove_(SharedState& st, EmissionBuffer& sig,
554 const ListChange<T>& ch) {
555 std::unique_lock lk(st.m);
556 const std::size_t src_idx = ch.index;
557 if (src_idx >= st.source_to_derived.size())
return;
559 const std::size_t d_idx = st.source_to_derived[src_idx];
560 auto removed = st.items[d_idx];
562 st.items.erase(st.items.begin() +
static_cast<std::ptrdiff_t
>(d_idx));
563 st.derived_to_source.erase(
564 st.derived_to_source.begin() +
static_cast<std::ptrdiff_t
>(d_idx));
565 st.source_to_derived.erase(
566 st.source_to_derived.begin() +
static_cast<std::ptrdiff_t
>(src_idx));
569 for (
auto& s : st.derived_to_source) {
570 if (s > src_idx) --s;
574 if (!ordered_unlocked_(st)) {
575 auto moves = reorder_unlocked_(st);
576 moves.insert(moves.begin(), {ListChangeKind::Remove, d_idx, removed, 0});
578 sig.emit_batch(std::move(moves));
585 static void handle_replace_(SharedState& st, EmissionBuffer& sig,
586 const ListChange<T>& ch) {
587 handle_slot_changed_(st, sig, ch,
593 static void handle_item_changed_(SharedState& st, EmissionBuffer& sig,
594 const ListChange<T>& ch) {
595 handle_slot_changed_(st, sig, ch,
604 static bool ordered_unlocked_(
const SharedState& st,
605 std::optional<std::size_t> skip = std::nullopt) {
606 return st.comparator->scan(*st.comparator, st.items, st.derived_to_source, skip);
611 static void move_row_unlocked_(SharedState& st, std::size_t from, std::size_t to)
noexcept {
612 auto item = std::move(st.items[from]);
613 const auto source_index = st.derived_to_source[from];
614 const auto first =
static_cast<std::ptrdiff_t
>(std::min(from, to));
615 const auto last =
static_cast<std::ptrdiff_t
>(std::max(from, to));
617 std::move(st.items.begin() + first + 1, st.items.begin() + last + 1, st.items.begin() + first);
618 std::move(st.derived_to_source.begin() + first + 1, st.derived_to_source.begin() + last + 1,
619 st.derived_to_source.begin() + first);
621 std::move_backward(st.items.begin() + first, st.items.begin() + last, st.items.begin() + last + 1);
622 std::move_backward(st.derived_to_source.begin() + first, st.derived_to_source.begin() + last,
623 st.derived_to_source.begin() + last + 1);
625 st.items[to] = std::move(item);
626 st.derived_to_source[to] = source_index;
627 for (
auto d = std::min(from, to); d <= std::max(from, to); ++d)
628 st.source_to_derived[st.derived_to_source[d]] = d;
634 static std::vector<ListChange<T>> reorder_unlocked_(SharedState& st) {
635 auto before = [&](std::size_t a, std::size_t b) {
636 const auto& lhs = *st.items[st.source_to_derived[a]];
637 const auto& rhs = *st.items[st.source_to_derived[b]];
638 if ((*st.comparator)(lhs, rhs))
return true;
639 if ((*st.comparator)(rhs, lhs))
return false;
642 std::vector<ListChange<T>> events;
643 if (std::is_sorted(st.derived_to_source.begin(), st.derived_to_source.end(), before))
return events;
644 auto target = st.derived_to_source;
645 std::sort(target.begin(), target.end(), before);
646 events.reserve(target.size());
650 std::size_t first = 0, last = target.size();
651 while (first < last && target[first] == st.derived_to_source[first]) ++first;
652 while (last > first && target[last - 1] == st.derived_to_source[last - 1]) --last;
653 const bool reverse = st.source_to_derived[target[first]] - first <
654 last - 1 - st.source_to_derived[target[last - 1]];
655 auto place = [&](std::size_t to) {
656 const auto from = st.source_to_derived[target[to]];
657 if (from == to)
return;
658 const auto item = st.items[from];
660 move_row_unlocked_(st, from, to);
662 if (reverse) {
for (
auto i = last; i-- > first;) place(i); }
663 else {
for (
auto i = first; i < last; ++i) place(i); }
677 static void handle_slot_changed_(SharedState& st, EmissionBuffer& sig,
678 const ListChange<T>& ch,
679 bool new_ptr_from_src,
681 bool cross_slot_use_move) {
682 std::unique_lock lk(st.m);
683 const std::size_t src_idx = ch.index;
684 if (src_idx >= st.source_to_derived.size())
return;
686 const std::size_t d_old = st.source_to_derived[src_idx];
689 auto previous = st.items[d_old];
690 auto fresh = new_ptr_from_src ? ch.item : previous;
691 if (new_ptr_from_src) st.items[d_old] = fresh;
692 if (!ordered_unlocked_(st, d_old)) {
693 auto moves = reorder_unlocked_(st);
694 if (new_ptr_from_src) {
696 moves.insert(moves.begin(), {ListChangeKind::Replace, d_old, fresh, 0});
701 sig.emit_batch(std::move(moves));
710 const std::size_t p_small = binary_search_insert_(
711 st, *fresh, src_idx, d_old);
713 if (p_small == d_old) {
716 sig.emit(ListChange<T>{same_slot_kind, d_old, fresh, 0});
722 const std::size_t d_insert = p_small;
723 move_row_unlocked_(st, d_old, d_insert);
726 if (cross_slot_use_move) {
738 static void handle_move_(SharedState& st, EmissionBuffer& sig,
739 const ListChange<T>& ch) {
740 std::unique_lock lk(st.m);
741 const std::size_t from = ch.from_index;
742 const std::size_t to = ch.index;
743 if (from == to)
return;
744 if (from >= st.source_to_derived.size())
return;
745 if (to >= st.source_to_derived.size())
return;
751 const std::size_t moved_d = st.source_to_derived[from];
754 for (
auto& s : st.derived_to_source) {
755 if (s > from && s <= to) --s;
758 for (
auto& s : st.derived_to_source) {
759 if (s >= to && s < from) ++s;
762 st.derived_to_source[moved_d] = to;
766 if (!ordered_unlocked_(st)) {
767 auto moves = reorder_unlocked_(st);
769 sig.emit_batch(std::move(moves));
773 static void handle_reset_(SharedState& st, EmissionBuffer& sig,
774 const ListChange<T>& ch) {
777 std::unique_lock lk(st.m);
778 rebuild_from_snapshot_unlocked_(st, *ch.snapshot);
781 sig.emit(std::move(reset));
787 static void renumber_s2d_(SharedState& st, std::size_t first = 0) {
788 for (std::size_t d = first; d < st.derived_to_source.size(); ++d) {
789 st.source_to_derived[st.derived_to_source[d]] = d;