1use std::{
2 any::{Any, TypeId},
3 cell::{Cell, RefCell},
4 hash::{Hash, Hasher},
5 rc::{Rc, Weak},
6 sync::Arc,
7};
8
9use smallvec::SmallVec;
10
11use crate::{
12 RecomposeScope, RecomposeScopeInner, ScopeId,
13 collections::map::{HashMap, HashSet},
14 hash::default as default_hash,
15 snapshot_v2::{
16 ReadObserver, StateObjectId, TransparentObserverMutableSnapshot, register_apply_observer,
17 },
18 state::StateObject,
19};
20
21type Executor = dyn Fn(Box<dyn FnOnce() + 'static>) + 'static;
22
23trait ScopeChangedCallback: Fn(&dyn Any) + Any {}
24
25impl<F: Fn(&dyn Any) + Any> ScopeChangedCallback for F {}
26
27#[derive(Clone)]
40pub struct SnapshotStateObserver {
41 inner: Rc<SnapshotStateObserverInner>,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
45pub struct SnapshotStateObserverDebugStats {
46 pub scopes_len: usize,
47 pub scopes_cap: usize,
48 pub fast_scopes_len: usize,
49 pub fast_scopes_cap: usize,
50 pub stateless_scope_count: usize,
51 pub observed_state_count: usize,
52 pub observed_state_capacity: usize,
53}
54
55impl SnapshotStateObserver {
56 pub fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
58 let inner = Rc::new(SnapshotStateObserverInner::new(on_changed_executor));
59 inner.set_self(Rc::downgrade(&inner));
60 Self { inner }
61 }
62
63 pub fn observe_reads<T, R>(
69 &self,
70 scope: T,
71 on_value_changed_for_scope: impl Fn(&T) + 'static,
72 block: impl FnOnce() -> R,
73 ) -> R
74 where
75 T: Any + Clone + Eq + Hash + 'static,
76 {
77 self.inner
78 .observe_reads(scope, on_value_changed_for_scope, block)
79 }
80
81 pub fn begin_frame(&self) {
83 self.inner.begin_frame();
84 }
85
86 pub fn prune_dead_scopes(&self) {
88 self.inner.prune_dead_scopes();
89 }
90
91 pub fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
93 self.inner.with_no_observations(block)
94 }
95
96 pub fn clear<T>(&self, scope: &T)
98 where
99 T: Any + Eq + Hash + 'static,
100 {
101 self.inner.clear(scope);
102 }
103
104 pub fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
106 self.inner.clear_if(predicate);
107 }
108
109 pub fn clear_all(&self) {
111 self.inner.clear_all();
112 }
113
114 pub fn start(&self) {
116 let weak = Rc::downgrade(&self.inner);
117 self.inner.start(weak);
118 }
119
120 pub fn stop(&self) {
122 self.inner.stop();
123 }
124
125 pub fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
126 self.inner.debug_stats()
127 }
128
129 #[cfg(test)]
130 pub fn notify_changes(&self, modified: &[Arc<dyn StateObject>]) {
131 self.inner.handle_apply(modified);
132 }
133}
134
135struct SnapshotStateObserverInner {
136 executor: Rc<Executor>,
137 owned_scopes: RefCell<HashMap<OwnedScopeIndexKey, OwnedScopeBucket>>,
138 fast_scopes: RefCell<HashMap<ScopeId, Rc<RefCell<ScopeEntry>>>>,
139 indexed_scopes: RefCell<HashMap<usize, Rc<RefCell<ScopeEntry>>>>,
140 observed_to_scopes: RefCell<HashMap<StateObjectId, HashSet<usize>>>,
141 pause_count: Rc<Cell<usize>>,
142 active_read_targets: Rc<RefCell<ReadObservationStack>>,
143 read_dispatcher: ReadObserver,
144 read_snapshot: RefCell<Option<Arc<TransparentObserverMutableSnapshot>>>,
145 apply_handle: RefCell<Option<crate::snapshot_v2::ObserverHandle>>,
146 weak_self: RefCell<Weak<SnapshotStateObserverInner>>,
147 frame_version: Cell<u64>,
148 next_entry_id: Cell<usize>,
149 capture_free_callbacks: RefCell<CaptureFreeCallbacks>,
152}
153
154#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
155struct OwnedScopeIndexKey {
156 type_id: TypeId,
157 value_hash: u64,
158}
159
160type OwnedScopeBucket = SmallVec<[Rc<RefCell<ScopeEntry>>; 1]>;
161
162type CaptureFreeCallbacks = SmallVec<[(TypeId, Rc<dyn ScopeChangedCallback>); 2]>;
164
165fn owned_scope_index_key<T>(scope: &T) -> OwnedScopeIndexKey
166where
167 T: Any + Hash + 'static,
168{
169 let mut hasher = default_hash::new();
170 scope.hash(&mut hasher);
171 OwnedScopeIndexKey {
172 type_id: TypeId::of::<T>(),
173 value_hash: hasher.finish(),
174 }
175}
176
177impl SnapshotStateObserverInner {
178 const MIN_RETAINED_SCOPE_CAPACITY: usize = 256;
179
180 fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
181 let pause_count = Rc::new(Cell::new(0));
182 let active_read_targets = Rc::new(RefCell::new(ReadObservationStack::default()));
183 let dispatcher_pause_count = Rc::clone(&pause_count);
184 let dispatcher_targets = Rc::clone(&active_read_targets);
185 let read_dispatcher: ReadObserver = Arc::new(move |state| {
186 if dispatcher_pause_count.get() > 0 {
187 return;
188 }
189 let observed = dispatcher_targets.borrow().last().cloned();
190 if let Some(observed) = observed {
191 observed.borrow_mut().insert(state);
192 }
193 });
194
195 Self {
196 executor: Rc::new(on_changed_executor),
197 owned_scopes: RefCell::new(HashMap::default()),
198 fast_scopes: RefCell::new(HashMap::default()),
199 indexed_scopes: RefCell::new(HashMap::default()),
200 observed_to_scopes: RefCell::new(HashMap::default()),
201 pause_count,
202 active_read_targets,
203 read_dispatcher,
204 read_snapshot: RefCell::new(None),
205 apply_handle: RefCell::new(None),
206 weak_self: RefCell::new(Weak::new()),
207 frame_version: Cell::new(0),
208 next_entry_id: Cell::new(0),
209 capture_free_callbacks: RefCell::new(SmallVec::new()),
210 }
211 }
212
213 fn set_self(&self, weak: Weak<SnapshotStateObserverInner>) {
214 self.weak_self.replace(weak);
215 }
216
217 fn begin_frame(&self) {
218 let next = self.frame_version.get().wrapping_add(1);
219 self.frame_version.set(next);
220 self.prune_dead_scopes();
221 }
222
223 fn observe_reads<T, R>(
224 &self,
225 scope: T,
226 on_value_changed_for_scope: impl Fn(&T) + 'static,
227 block: impl FnOnce() -> R,
228 ) -> R
229 where
230 T: Any + Clone + Eq + Hash + 'static,
231 {
232 let frame_version = self.frame_version.get();
233 let has_frame_version = frame_version != 0;
234
235 let existing_entry = self.find_scope_entry(&scope);
236 let on_changed = std::cell::LazyCell::new(|| {
237 let callback = move |scope_any: &dyn Any| {
238 if let Some(typed) = scope_any.downcast_ref::<T>() {
239 on_value_changed_for_scope(typed);
240 }
241 };
242 if std::mem::size_of_val(&callback) == 0 {
243 return self.capture_free_callback(callback);
244 }
245 match existing_entry.as_ref() {
246 Some(entry) => entry.borrow_mut().callback_reusing(callback),
247 None => Rc::new(callback),
248 }
249 });
250
251 if let Some(entry) = existing_entry.as_ref() {
252 let already_observed = {
253 let mut entry_mut = entry.borrow_mut();
254 entry_mut.update_scope(scope.clone());
255 has_frame_version && entry_mut.last_seen_version == frame_version
256 };
257 let callback = on_changed.clone();
258 entry.borrow_mut().on_changed = callback;
259 if already_observed {
260 return block();
261 }
262 }
263
264 let observed = self.active_read_targets.borrow_mut().push();
265 struct ActiveObservationGuard {
266 stack: Rc<RefCell<ReadObservationStack>>,
267 }
268 impl Drop for ActiveObservationGuard {
269 fn drop(&mut self) {
270 let target = self.stack.borrow_mut().pop();
271 let discarded = target.replace(ObservedIds::new());
272 drop(discarded);
273 }
274 }
275 let _guard = ActiveObservationGuard {
276 stack: Rc::clone(&self.active_read_targets),
277 };
278
279 let result = self.run_with_read_observer(block);
280
281 if observed.borrow().is_empty() {
282 if existing_entry.is_some() {
283 self.clear(&scope);
284 }
285 return result;
286 }
287
288 let observed = {
289 let mut observed = observed.borrow_mut();
290 std::mem::replace(&mut *observed, ObservedIds::new())
291 };
292 let entry = existing_entry
293 .clone()
294 .unwrap_or_else(|| self.insert_scope_entry(scope.clone(), on_changed.clone()));
295 {
296 let mut entry_mut = entry.borrow_mut();
297 entry_mut.update(scope, Rc::clone(&on_changed));
298 entry_mut.last_seen_version = if has_frame_version {
299 frame_version
300 } else {
301 u64::MAX
302 };
303 }
304 self.replace_observed_ids(&entry, observed);
305
306 result
307 }
308
309 fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
310 self.pause_count.set(self.pause_count.get() + 1);
311 let result = block();
312 self.pause_count
313 .set(self.pause_count.get().saturating_sub(1));
314 result
315 }
316
317 fn clear<T>(&self, scope: &T)
318 where
319 T: Any + Eq + Hash + 'static,
320 {
321 if let Some(rc_scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
322 if let Some(entry) = self.fast_scopes.borrow_mut().remove(&rc_scope.id()) {
323 self.unregister_entry(&entry);
324 }
325 return;
326 }
327
328 let removed = self.remove_owned_scope_entry(scope);
329 if let Some(entry) = removed {
330 self.unregister_entry(&entry);
331 }
332 }
333
334 fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
335 let removed_fast = {
336 let mut fast_scopes = self.fast_scopes.borrow_mut();
337 let removed_ids: Vec<_> = fast_scopes
338 .iter()
339 .filter(|(_, entry)| entry.borrow().matches_predicate(&predicate))
340 .map(|(scope_id, _)| *scope_id)
341 .collect();
342 removed_ids
343 .into_iter()
344 .filter_map(|scope_id| fast_scopes.remove(&scope_id))
345 .collect::<Vec<_>>()
346 };
347 let removed_owned =
348 { self.partition_owned_scopes(|entry| entry.matches_predicate(&predicate)) };
349
350 for entry in removed_fast.into_iter().chain(removed_owned) {
351 self.unregister_entry(&entry);
352 }
353 }
354
355 fn clear_all(&self) {
356 self.fast_scopes.borrow_mut().clear();
357 self.owned_scopes.borrow_mut().clear();
358 self.indexed_scopes.borrow_mut().clear();
359 self.observed_to_scopes.borrow_mut().clear();
360 }
361
362 fn start(&self, weak_self: Weak<SnapshotStateObserverInner>) {
363 if self.apply_handle.borrow().is_some() {
364 return;
365 }
366
367 let handle = register_apply_observer(Rc::new(move |modified, _snapshot_id| {
368 if let Some(inner) = weak_self.upgrade() {
369 inner.handle_apply(modified);
370 }
371 }));
372 self.apply_handle.replace(Some(handle));
373 }
374
375 fn stop(&self) {
376 if let Some(handle) = self.apply_handle.borrow_mut().take() {
377 drop(handle);
378 }
379 }
380
381 fn find_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
382 where
383 T: Any + Eq + Hash + 'static,
384 {
385 if let Some(scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
386 return self.fast_scopes.borrow().get(&scope.id()).cloned();
387 }
388
389 self.find_owned_scope_entry(scope)
390 }
391
392 fn capture_free_callback<F: Fn(&dyn Any) + 'static>(
396 &self,
397 callback: F,
398 ) -> Rc<dyn ScopeChangedCallback> {
399 let type_id = TypeId::of::<F>();
400 let mut shared = self.capture_free_callbacks.borrow_mut();
401 if let Some((_, callback)) = shared.iter().find(|(id, _)| *id == type_id) {
402 return Rc::clone(callback);
403 }
404 let callback: Rc<dyn ScopeChangedCallback> = Rc::new(callback);
405 shared.push((type_id, Rc::clone(&callback)));
406 callback
407 }
408
409 fn insert_scope_entry(
410 &self,
411 scope: impl Any + Clone + Eq + Hash + 'static,
412 on_changed: Rc<dyn ScopeChangedCallback>,
413 ) -> Rc<RefCell<ScopeEntry>> {
414 let entry_id = self.next_entry_id.get();
415 self.next_entry_id.set(entry_id.wrapping_add(1));
416 let recompose_scope_id = (&scope as &dyn Any)
417 .downcast_ref::<RecomposeScope>()
418 .map(RecomposeScope::id);
419 let owned_scope_key = recompose_scope_id
420 .is_none()
421 .then(|| owned_scope_index_key(&scope));
422 let entry = Rc::new(RefCell::new(ScopeEntry::new(entry_id, scope, on_changed)));
423 self.indexed_scopes
424 .borrow_mut()
425 .insert(entry_id, Rc::clone(&entry));
426 if let Some(scope_id) = recompose_scope_id {
427 self.fast_scopes
428 .borrow_mut()
429 .insert(scope_id, Rc::clone(&entry));
430 } else if let Some(scope_key) = owned_scope_key {
431 self.owned_scopes
432 .borrow_mut()
433 .entry(scope_key)
434 .or_default()
435 .push(Rc::clone(&entry));
436 }
437 entry
438 }
439
440 fn prune_dead_scopes(&self) {
441 let removed_fast = {
442 let mut fast_scopes = self.fast_scopes.borrow_mut();
443 let removed_ids: Vec<_> = fast_scopes
444 .iter()
445 .filter(|(_, entry)| !entry.borrow().should_retain())
446 .map(|(scope_id, _)| *scope_id)
447 .collect();
448 let removed = removed_ids
449 .into_iter()
450 .filter_map(|scope_id| fast_scopes.remove(&scope_id))
451 .collect::<Vec<_>>();
452 shrink_map_if_sparse(&mut fast_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
453 removed
454 };
455
456 let removed_owned = { self.partition_owned_scopes(|entry| !entry.should_retain()) };
457
458 for entry in removed_fast.into_iter().chain(removed_owned) {
459 self.unregister_entry(&entry);
460 }
461 }
462
463 fn find_owned_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
464 where
465 T: Any + Eq + Hash + 'static,
466 {
467 let key = owned_scope_index_key(scope);
468 self.owned_scopes.borrow().get(&key).and_then(|bucket| {
469 bucket
470 .iter()
471 .find(|entry| entry.borrow().matches_scope(scope))
472 .cloned()
473 })
474 }
475
476 fn remove_owned_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
477 where
478 T: Any + Eq + Hash + 'static,
479 {
480 let key = owned_scope_index_key(scope);
481 let mut owned_scopes = self.owned_scopes.borrow_mut();
482 let mut removed = None;
483 let mut remove_bucket = false;
484 if let Some(bucket) = owned_scopes.get_mut(&key)
485 && let Some(index) = bucket
486 .iter()
487 .position(|entry| entry.borrow().matches_scope(scope))
488 {
489 removed = Some(bucket.remove(index));
490 remove_bucket = bucket.is_empty();
491 }
492 if remove_bucket {
493 owned_scopes.remove(&key);
494 }
495 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
496 removed
497 }
498
499 fn partition_owned_scopes(
500 &self,
501 should_remove: impl Fn(&ScopeEntry) -> bool,
502 ) -> Vec<Rc<RefCell<ScopeEntry>>> {
503 let mut owned_scopes = self.owned_scopes.borrow_mut();
504 let mut retained = HashMap::default();
505 let mut removed = Vec::new();
506 for (key, mut bucket) in owned_scopes.drain() {
507 let mut retained_bucket = OwnedScopeBucket::new();
508 for entry in bucket.drain(..) {
509 if should_remove(&entry.borrow()) {
510 removed.push(entry);
511 } else {
512 retained_bucket.push(entry);
513 }
514 }
515 if !retained_bucket.is_empty() {
516 retained.insert(key, retained_bucket);
517 }
518 }
519 *owned_scopes = retained;
520 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
521 removed
522 }
523
524 fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
525 let owned_scopes = self.owned_scopes.borrow();
526 let fast_scopes = self.fast_scopes.borrow();
527 let owned_scope_len = owned_scopes.values().map(SmallVec::len).sum::<usize>();
528 let owned_scope_cap =
529 owned_scopes.capacity() + owned_scopes.values().map(SmallVec::capacity).sum::<usize>();
530 let scopes_len = owned_scope_len + fast_scopes.len();
531 let scopes_cap = owned_scope_cap + fast_scopes.capacity();
532 let mut observed_state_count = 0;
533 let mut observed_state_capacity = 0;
534 let mut stateless_scope_count = 0;
535
536 for entry in owned_scopes
537 .values()
538 .flat_map(|bucket| bucket.iter())
539 .chain(fast_scopes.values())
540 {
541 let entry = entry.borrow();
542 observed_state_count += entry.observed.len();
543 observed_state_capacity += entry.observed.capacity();
544 stateless_scope_count += usize::from(entry.observed.is_empty());
545 }
546
547 SnapshotStateObserverDebugStats {
548 scopes_len,
549 scopes_cap,
550 fast_scopes_len: fast_scopes.len(),
551 fast_scopes_cap: fast_scopes.capacity(),
552 stateless_scope_count,
553 observed_state_count,
554 observed_state_capacity,
555 }
556 }
557
558 fn run_with_read_observer<R>(&self, block: impl FnOnce() -> R) -> R {
559 use crate::snapshot_v2::take_transparent_observer_mutable_snapshot_reusing;
560
561 let mut snapshot = take_transparent_observer_mutable_snapshot_reusing(
562 Some(self.read_dispatcher.clone()),
563 None,
564 self.read_snapshot.take(),
565 );
566 let result = snapshot.enter(block);
567 snapshot.dispose();
568 if Arc::get_mut(&mut snapshot).is_some() && !snapshot.has_pending_changes() {
569 self.read_snapshot.replace(Some(snapshot));
570 }
571 result
572 }
573
574 fn handle_apply(&self, modified: &[Arc<dyn StateObject>]) {
575 if modified.is_empty() {
576 return;
577 }
578
579 let mut seen_scope_ids: HashSet<usize> = HashSet::default();
580 let mut to_notify: Vec<Rc<RefCell<ScopeEntry>>> = Vec::new();
581 {
582 let observed_to_scopes = self.observed_to_scopes.borrow();
583 let indexed_scopes = self.indexed_scopes.borrow();
584 for state in modified {
585 if let Some(scope_ids) = observed_to_scopes.get(&state.object_id().as_usize()) {
586 let mut ordered_scope_ids: SmallVec<[usize; 8]> =
587 scope_ids.iter().copied().collect();
588 ordered_scope_ids.sort_unstable();
589 for scope_id in ordered_scope_ids {
590 if seen_scope_ids.insert(scope_id)
591 && let Some(entry) = indexed_scopes.get(&scope_id)
592 {
593 to_notify.push(entry.clone());
594 }
595 }
596 }
597 }
598 }
599
600 if to_notify.is_empty() {
601 return;
602 }
603
604 for entry in to_notify {
605 let executor = self.executor.clone();
606 executor(Box::new(move || {
607 if let Ok(entry) = entry.try_borrow() {
608 entry.notify();
609 }
610 }));
611 }
612 }
613
614 fn replace_observed_ids(&self, entry: &Rc<RefCell<ScopeEntry>>, observed: ObservedIds) {
615 let (entry_id, previous) = {
616 let mut entry_mut = entry.borrow_mut();
617 let entry_id = entry_mut.id;
618 let previous = std::mem::replace(&mut entry_mut.observed, observed);
619 (entry_id, previous)
620 };
621 let entry_ref = entry.borrow();
622 if previous.iter().eq(entry_ref.observed.iter()) {
623 return;
624 }
625 self.unregister_observed_ids(entry_id, &previous);
626 self.register_observed_ids(entry_id, &entry_ref.observed);
627 }
628
629 fn register_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
630 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
631 for state_id in observed.iter() {
632 let scope_ids = observed_to_scopes.entry(state_id).or_default();
633 scope_ids.insert(entry_id);
634 }
635 }
636
637 fn unregister_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
638 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
639 let mut emptied = SmallVec::<[StateObjectId; MAX_OBSERVED_STATES]>::new();
640 for state_id in observed.iter() {
641 if let Some(scope_ids) = observed_to_scopes.get_mut(&state_id) {
642 scope_ids.remove(&entry_id);
643 if scope_ids.is_empty() {
644 emptied.push(state_id);
645 }
646 }
647 }
648 for state_id in emptied {
649 observed_to_scopes.remove(&state_id);
650 }
651 shrink_map_if_sparse(&mut observed_to_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
652 }
653
654 fn unregister_entry(&self, entry: &Rc<RefCell<ScopeEntry>>) {
655 let (entry_id, observed) = {
656 let mut entry_mut = entry.borrow_mut();
657 let observed = std::mem::replace(&mut entry_mut.observed, ObservedIds::new());
658 (entry_mut.id, observed)
659 };
660 self.unregister_observed_ids(entry_id, &observed);
661 self.indexed_scopes.borrow_mut().remove(&entry_id);
662 }
663}
664
665fn shrink_map_if_sparse<K, V>(map: &mut HashMap<K, V>, min_retained_capacity: usize)
666where
667 K: Eq + std::hash::Hash,
668{
669 if map.capacity() <= map.len().max(min_retained_capacity).saturating_mul(4) {
670 return;
671 }
672
673 let retained = map.len().max(min_retained_capacity);
674 let mut rebuilt = HashMap::default();
675 rebuilt.reserve(retained);
676 rebuilt.extend(map.drain());
677 *map = rebuilt;
678}
679
680#[derive(Default)]
681struct ReadObservationStack {
682 targets: Vec<Rc<RefCell<ObservedIds>>>,
683 depth: usize,
684}
685
686impl ReadObservationStack {
687 fn push(&mut self) -> Rc<RefCell<ObservedIds>> {
688 if self.depth == self.targets.len() {
689 self.targets.push(Rc::new(RefCell::new(ObservedIds::new())));
690 }
691 let target = Rc::clone(&self.targets[self.depth]);
692 self.depth += 1;
693 target
694 }
695
696 fn last(&self) -> Option<&Rc<RefCell<ObservedIds>>> {
697 self.depth.checked_sub(1).map(|index| &self.targets[index])
698 }
699
700 fn pop(&mut self) -> Rc<RefCell<ObservedIds>> {
701 self.depth -= 1;
702 Rc::clone(&self.targets[self.depth])
703 }
704}
705
706enum ObservedIds {
707 Small(SmallVec<[ObservedState; MAX_OBSERVED_STATES]>),
708 Large(HashMap<StateObjectId, Option<Rc<dyn Any>>>),
709}
710
711struct ObservedState {
712 id: StateObjectId,
713 _lease: Option<Rc<dyn Any>>,
714}
715
716impl ObservedIds {
717 fn new() -> Self {
718 ObservedIds::Small(SmallVec::new())
719 }
720
721 fn insert(&mut self, state: &dyn StateObject) {
722 let id = state.object_id().as_usize();
723 match self {
724 ObservedIds::Small(small) => {
725 if small.iter().any(|observed| observed.id == id) {
726 return;
727 }
728 if small.len() < MAX_OBSERVED_STATES {
729 small.push(ObservedState {
730 id,
731 _lease: state.observation_lease(),
732 });
733 } else {
734 let mut large =
735 HashMap::with_capacity_and_hasher(small.len() + 1, Default::default());
736 for observed in small.drain(..) {
737 large.insert(observed.id, observed._lease);
738 }
739 large.insert(id, state.observation_lease());
740 *self = ObservedIds::Large(large);
741 }
742 }
743 ObservedIds::Large(large) => {
744 large.entry(id).or_insert_with(|| state.observation_lease());
745 }
746 }
747 }
748
749 fn is_empty(&self) -> bool {
750 match self {
751 ObservedIds::Small(small) => small.is_empty(),
752 ObservedIds::Large(large) => large.is_empty(),
753 }
754 }
755
756 fn len(&self) -> usize {
757 match self {
758 ObservedIds::Small(small) => small.len(),
759 ObservedIds::Large(large) => large.len(),
760 }
761 }
762
763 fn capacity(&self) -> usize {
764 match self {
765 ObservedIds::Small(small) => small.capacity(),
766 ObservedIds::Large(large) => large.capacity(),
767 }
768 }
769
770 fn iter(&self) -> impl Iterator<Item = StateObjectId> + '_ {
771 let (small, large) = match self {
772 ObservedIds::Small(small) => (Some(small.as_slice()), None),
773 ObservedIds::Large(large) => (None, Some(large)),
774 };
775 small
776 .into_iter()
777 .flatten()
778 .map(|observed| observed.id)
779 .chain(large.into_iter().flat_map(|states| states.keys().copied()))
780 }
781}
782
783const MAX_OBSERVED_STATES: usize = 8;
784
785enum ScopeStorage {
786 Owned(Box<dyn Any>),
787 RecomposeScope {
788 id: ScopeId,
789 weak: Weak<RecomposeScopeInner>,
790 },
791}
792
793struct ScopeEntry {
794 id: usize,
795 scope: ScopeStorage,
796 on_changed: Rc<dyn ScopeChangedCallback>,
797 observed: ObservedIds,
798 last_seen_version: u64,
799}
800
801impl ScopeEntry {
802 fn new<T>(id: usize, scope: T, on_changed: Rc<dyn ScopeChangedCallback>) -> Self
803 where
804 T: Any + 'static,
805 {
806 Self {
807 id,
808 scope: ScopeStorage::from_value(scope),
809 on_changed,
810 observed: ObservedIds::new(),
811 last_seen_version: u64::MAX,
812 }
813 }
814
815 fn callback_reusing<F: Fn(&dyn Any) + 'static>(
816 &mut self,
817 callback: F,
818 ) -> Rc<dyn ScopeChangedCallback> {
819 if let Some(stored) = Rc::get_mut(&mut self.on_changed)
820 .and_then(|stored| (stored as &mut dyn Any).downcast_mut::<F>())
821 {
822 *stored = callback;
823 Rc::clone(&self.on_changed)
824 } else {
825 Rc::new(callback)
826 }
827 }
828
829 fn update<T>(&mut self, new_scope: T, on_changed: Rc<dyn ScopeChangedCallback>)
830 where
831 T: Any + 'static,
832 {
833 self.update_scope(new_scope);
834 self.on_changed = on_changed;
835 }
836
837 fn update_scope<T>(&mut self, new_scope: T)
838 where
839 T: Any + 'static,
840 {
841 if let ScopeStorage::Owned(stored) = &mut self.scope
842 && let Some(stored) = stored.downcast_mut::<T>()
843 {
844 *stored = new_scope;
845 } else {
846 self.scope = ScopeStorage::from_value(new_scope);
847 }
848 }
849
850 fn matches_scope<T>(&self, scope: &T) -> bool
851 where
852 T: Any + Eq + 'static,
853 {
854 if let Some(scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
855 return matches!(
856 &self.scope,
857 ScopeStorage::RecomposeScope { id, .. } if *id == scope.id()
858 );
859 }
860
861 match &self.scope {
862 ScopeStorage::Owned(stored) => stored
863 .downcast_ref::<T>()
864 .is_some_and(|stored| stored == scope),
865 ScopeStorage::RecomposeScope { .. } => false,
866 }
867 }
868
869 fn matches_predicate(&self, predicate: &impl Fn(&dyn Any) -> bool) -> bool {
870 match &self.scope {
871 ScopeStorage::Owned(scope) => predicate(scope.as_ref()),
872 ScopeStorage::RecomposeScope { weak, .. } => weak
873 .upgrade()
874 .is_none_or(|inner| predicate(&RecomposeScope { inner })),
875 }
876 }
877
878 fn should_retain(&self) -> bool {
879 match &self.scope {
880 ScopeStorage::Owned(_) => true,
881 ScopeStorage::RecomposeScope { weak, .. } => weak.upgrade().is_some(),
882 }
883 }
884
885 fn notify(&self) {
886 match &self.scope {
887 ScopeStorage::Owned(scope) => (self.on_changed)(scope.as_ref()),
888 ScopeStorage::RecomposeScope { weak, .. } => {
889 if let Some(inner) = weak.upgrade() {
890 (self.on_changed)(&RecomposeScope { inner });
891 }
892 }
893 }
894 }
895}
896
897impl ScopeStorage {
898 fn from_value<T>(value: T) -> Self
899 where
900 T: Any + 'static,
901 {
902 let any = &value as &dyn Any;
903 if let Some(scope) = any.downcast_ref::<RecomposeScope>() {
904 Self::RecomposeScope {
905 id: scope.id(),
906 weak: scope.downgrade(),
907 }
908 } else {
909 Self::Owned(Box::new(value))
910 }
911 }
912}
913
914#[cfg(test)]
915#[path = "tests/snapshot_state_observer_tests.rs"]
916mod tests;