71 :
public detail::ListSignalMixin<DistinctList<T, Key, Source>, T> {
72 friend detail::ListSignalMixin<DistinctList<T, Key, Source>, T>;
78 using Signal = detail::ListSignal<T>;
84 KeyOf key_of = default_key_of_())
85 : source_(
std::move(source)),
87 state_(
std::make_shared<SharedState>())
89 state_->key_of = std::move(key_of);
92 std::weak_ptr<SharedState> weak_state = state_;
93 std::weak_ptr<Signal> weak_signal = signal_;
94 std::weak_ptr<Source> weak_source{source_};
95 source_sub_ = source_->observe(
96 [weak_state, weak_signal, weak_source](
const ListChange<T>& ch) {
97 auto st = weak_state.lock();
98 auto sig = weak_signal.lock();
99 auto src = weak_source.lock();
100 if (!st || !sig || !src)
return;
101 handle_source_change_(*st, *sig, *src, ch);
111 [[nodiscard]] std::size_t
size()
const {
112 std::shared_lock lk(state_->m);
113 return state_->visible_slots.size();
116 [[nodiscard]]
bool empty()
const {
return size() == 0; }
118 [[nodiscard]] std::shared_ptr<T>
at(std::size_t derived_pos)
const {
119 std::shared_lock lk(state_->m);
120 const auto sid = state_->visible_slots.at(derived_pos);
121 return state_->slots.at(sid).rep;
124 [[nodiscard]] std::vector<std::shared_ptr<T>>
snapshot()
const {
125 std::shared_lock lk(state_->m);
126 std::vector<std::shared_ptr<T>> out;
127 out.reserve(state_->visible_slots.size());
128 for (
auto sid : state_->visible_slots) {
129 out.push_back(state_->slots.at(sid).rep);
135 using SlotId = std::uint64_t;
145 std::shared_ptr<T> rep;
148 std::vector<std::shared_ptr<T>> dups;
152 mutable std::shared_mutex m;
158 std::unordered_map<SlotId, Slot> slots;
163 std::vector<SlotId> visible_slots;
168 std::unordered_map<Key, SlotId> key_to_slot;
172 std::unordered_map<const T*, SlotId> item_to_slot;
178 std::unordered_map<const T*, Key> item_key;
180 SlotId next_slot_id{1};
181 std::vector<std::shared_ptr<T>> source_items;
184 std::shared_ptr<Source> source_;
185 std::shared_ptr<Signal> signal_;
186 std::shared_ptr<SharedState> state_;
187 Subscription source_sub_;
189 static KeyOf default_key_of_() {
190 return [](
const T& v) -> Key {
191 if constexpr (std::is_same_v<Key, T>) {
194 static_assert(std::is_same_v<Key, T>,
195 "DistinctList<T, Key>: default key extractor needs Key == T. "
196 "Provide a custom KeyOf for differing Key.");
206 void rebuild_initial_() {
207 rebuild_(*state_, source_->snapshot());
210 static void rebuild_(SharedState& st, std::vector<std::shared_ptr<T>> snap) {
211 std::unique_lock lk(st.m);
213 st.visible_slots.clear();
214 st.key_to_slot.clear();
216 st.item_to_slot.clear();
218 st.source_items = std::move(snap);
219 st.slots.reserve(st.source_items.size());
220 st.visible_slots.reserve(st.source_items.size());
221 st.key_to_slot.reserve(st.source_items.size());
222 st.item_key.reserve(st.source_items.size());
223 st.item_to_slot.reserve(st.source_items.size());
224 for (
const auto& sp_ : st.source_items) {
225 const Key k = st.key_of(*sp_);
226 st.item_key[sp_.get()] = k;
227 auto it = st.key_to_slot.find(k);
228 if (it == st.key_to_slot.end()) {
229 const SlotId sid = st.next_slot_id++;
230 st.key_to_slot.emplace(k, sid);
231 st.slots.emplace(sid, Slot{k, sp_, {}});
232 st.visible_slots.push_back(sid);
233 st.item_to_slot[sp_.get()] = sid;
235 st.slots[it->second].dups.push_back(sp_);
236 st.item_to_slot[sp_.get()] = it->second;
254 static std::size_t derived_pos_for_new_rep_(SharedState& st,
255 std::size_t source_idx) {
259 std::unordered_map<SlotId, bool> counted;
260 std::size_t count = 0;
261 const std::size_t bound =
262 std::min<std::size_t>(source_idx, st.source_items.size());
263 for (std::size_t i = 0; i < bound; ++i) {
264 auto sp_ = st.source_items[i];
265 auto it = st.item_to_slot.find(sp_.get());
266 if (it == st.item_to_slot.end())
continue;
267 const SlotId sid = it->second;
271 auto sl_it = st.slots.find(sid);
272 if (sl_it == st.slots.end())
continue;
273 if (sl_it->second.rep.get() == sp_.get() && counted.emplace(sid,
true).second) ++count;
280 static std::size_t derived_pos_of_slot_(
const SharedState& st, SlotId sid) {
281 auto it = std::lower_bound(st.visible_slots.begin(),
282 st.visible_slots.end(), sid);
283 if (it == st.visible_slots.end() || *it != sid) {
284 return st.visible_slots.size();
286 return static_cast<std::size_t
>(it - st.visible_slots.begin());
296 static SlotId allocate_ordered_slot_id_(SharedState& st,
297 std::size_t derived_pos) {
298 const auto sz = st.visible_slots.size();
299 if (derived_pos == sz) {
301 const SlotId sid = st.next_slot_id;
302 st.next_slot_id += kSlotIdStep;
305 if (derived_pos == 0) {
308 const SlotId right = st.visible_slots.front();
313 return rebalance_and_insert_(st, derived_pos);
316 const SlotId left = st.visible_slots[derived_pos - 1];
317 const SlotId right = st.visible_slots[derived_pos];
318 if (right - left >= 2) {
319 return left + (right - left) / 2;
321 return rebalance_and_insert_(st, derived_pos);
330 static SlotId rebalance_and_insert_(SharedState& st,
331 std::size_t derived_pos) {
332 const std::size_t sz = st.visible_slots.size();
333 std::vector<SlotId> old_ids = st.visible_slots;
334 std::unordered_map<SlotId, Slot> new_slots;
335 new_slots.reserve(st.slots.size());
336 SlotId cursor = kSlotIdStep;
338 std::unordered_map<SlotId, SlotId> remap;
340 for (std::size_t i = 0; i < sz; ++i) {
341 if (i == derived_pos) cursor += kSlotIdStep;
342 const SlotId old_id = old_ids[i];
343 const SlotId new_id = cursor;
344 remap[old_id] = new_id;
345 cursor += kSlotIdStep;
351 for (
auto& [old_id, slot] : st.slots) {
352 auto rm_it = remap.find(old_id);
353 if (rm_it == remap.end()) {
354 new_slots.emplace(old_id, std::move(slot));
356 new_slots.emplace(rm_it->second, std::move(slot));
359 st.slots = std::move(new_slots);
361 for (
auto& [k, sid] : st.key_to_slot) {
362 auto rm_it = remap.find(sid);
363 if (rm_it != remap.end()) sid = rm_it->second;
366 for (
auto& [p, sid] : st.item_to_slot) {
367 auto rm_it = remap.find(sid);
368 if (rm_it != remap.end()) sid = rm_it->second;
371 for (std::size_t i = 0; i < sz; ++i) {
372 st.visible_slots[i] = remap[old_ids[i]];
374 st.next_slot_id = cursor + kSlotIdStep;
377 if (derived_pos == 0) {
378 return st.visible_slots.empty() ? cursor / 2
379 : st.visible_slots.front() / 2;
381 const SlotId left = st.visible_slots[derived_pos - 1];
382 const SlotId right = derived_pos < sz ? st.visible_slots[derived_pos]
383 : cursor + kSlotIdStep;
384 return left + (right - left) / 2;
387 static constexpr SlotId kSlotIdStep = 1024;
389 static void handle_source_change_(SharedState& st,
Signal& sig, Source&, ListChange<T> ch) {
390 std::shared_ptr<T> previous;
392 std::unique_lock lk(st.m);
393 auto& items = st.source_items;
394 const auto pos =
static_cast<std::ptrdiff_t
>(ch.index);
398 previous = items.at(ch.index);
399 items.erase(items.begin() + pos);
402 previous = items.at(ch.index);
403 items[ch.index] = ch.item;
406 auto moved = items.at(ch.from_index);
407 items.erase(items.begin() +
static_cast<std::ptrdiff_t
>(ch.from_index));
408 items.insert(items.begin() + pos, std::move(moved));
418 handle_remove_(st, sig, ch);
422 handle_remove_(st, sig, removed);
423 handle_insert_(st, sig, ch, ch.item);
428 rebuild_(st, *ch.snapshot);
429 sig.emit(reset_event_(st));
435 static void handle_insert_(SharedState& st,
Signal& sig,
436 const ListChange<T>& ch,
437 std::shared_ptr<T> sp_) {
438 const Key k = st.key_of(*sp_);
440 std::optional<std::size_t> emit_at;
443 std::unique_lock lk(st.m);
444 st.item_key[sp_.get()] = k;
445 auto it = st.key_to_slot.find(k);
446 if (it == st.key_to_slot.end()) {
452 std::size_t derived_pos;
453 if (ch.index >= st.source_items.size() - 1) {
454 derived_pos = st.visible_slots.size();
455 }
else if (ch.index == 0) {
459 derived_pos_for_new_rep_(st, ch.index);
462 allocate_ordered_slot_id_(st, derived_pos);
463 st.key_to_slot.emplace(k, sid);
464 st.slots.emplace(sid, Slot{k, sp_, {}});
465 st.item_to_slot[sp_.get()] = sid;
468 st.visible_slots.insert(
469 st.visible_slots.begin()
470 +
static_cast<std::ptrdiff_t
>(derived_pos),
472 emit_at = derived_pos;
476 st.slots[it->second].dups.push_back(sp_);
477 st.item_to_slot[sp_.get()] = it->second;
480 if (emit_at.has_value()) {
487 static void handle_remove_(SharedState& st,
Signal& sig,
488 const ListChange<T>& ch) {
489 if (ch.item ==
nullptr)
return;
490 std::optional<std::size_t> emit_remove_at;
491 std::shared_ptr<T> removed_sp;
492 std::shared_ptr<T> promoted_sp;
493 std::optional<std::size_t> emit_replace_at;
495 std::unique_lock lk(st.m);
496 auto its_it = st.item_to_slot.find(ch.item.get());
497 if (its_it == st.item_to_slot.end())
return;
498 const SlotId sid = its_it->second;
499 const bool survives = std::any_of(st.source_items.begin(), st.source_items.end(),
500 [&](
const auto& item) { return item.get() == ch.item.get(); });
502 st.item_to_slot.erase(its_it);
503 st.item_key.erase(ch.item.get());
506 auto sl_it = st.slots.find(sid);
507 if (sl_it == st.slots.end())
return;
508 Slot& slot = sl_it->second;
510 if (slot.rep.get() != ch.item.get()) {
513 for (
auto bi = slot.dups.begin(); bi != slot.dups.end(); ++bi) {
514 if (bi->get() == ch.item.get()) { slot.dups.erase(bi);
break; }
520 removed_sp = slot.rep;
521 if (!slot.dups.empty()) {
523 promoted_sp = std::move(slot.dups.front());
524 slot.dups.erase(slot.dups.begin());
525 slot.rep = promoted_sp;
526 emit_replace_at = derived_pos_of_slot_(st, sid);
529 const std::size_t derived_pos = derived_pos_of_slot_(st, sid);
530 if (derived_pos < st.visible_slots.size()) {
531 st.visible_slots.erase(
532 st.visible_slots.begin()
533 +
static_cast<std::ptrdiff_t
>(derived_pos));
535 st.key_to_slot.erase(slot.key);
536 st.slots.erase(sl_it);
537 emit_remove_at = derived_pos;
540 if (emit_remove_at.has_value()) {
544 if (emit_replace_at.has_value()) {
550 static ListChange<T> reset_event_(
const SharedState& st) {
551 std::shared_lock lock(st.m);
552 std::vector<std::shared_ptr<T>> items;
553 items.reserve(st.visible_slots.size());
554 for (
const auto sid : st.visible_slots) items.push_back(st.slots.at(sid).rep);
558 static void handle_item_changed_(SharedState& st,
Signal& sig,
559 const ListChange<T>& ch) {
560 if (!ch.item)
return;
561 std::shared_ptr<T> item;
562 std::vector<std::size_t> occurrences;
563 std::optional<std::size_t> visible;
566 std::shared_lock lk(st.m);
567 auto found = st.item_key.find(ch.item.get());
568 if (found == st.item_key.end())
return;
569 key_changed = found->second != st.key_of(*ch.item);
570 const auto sid = st.item_to_slot.at(ch.item.get());
571 const auto& slot = st.slots.at(sid);
572 if (slot.rep.get() == ch.item.get()) visible = derived_pos_of_slot_(st, sid);
573 for (std::size_t i = 0; i < st.source_items.size(); ++i) {
574 if (st.source_items[i].get() == ch.item.get()) {
575 item = st.source_items[i];
576 if (key_changed) occurrences.push_back(i);
587 for (
const auto index : occurrences) {
591 std::unique_lock lk(st.m);
592 st.item_key.erase(item.get());
593 st.item_to_slot.erase(item.get());
595 for (
const auto index : occurrences) {