Skip to main content

loro_internal/
state.rs

1use crate::sync::{AtomicU64, Mutex, RwLock};
2#[cfg(test)]
3use std::cell::Cell;
4use std::sync::{Arc, Weak};
5use std::{borrow::Cow, io::Write, sync::atomic::Ordering};
6
7use container_store::ContainerStore;
8use dead_containers_cache::DeadContainersCache;
9use enum_as_inner::EnumAsInner;
10use enum_dispatch::enum_dispatch;
11use itertools::Itertools;
12use loro_common::{ContainerID, Lamport, LoroError, LoroResult, TreeID};
13use loro_delta::DeltaItem;
14use rustc_hash::{FxHashMap, FxHashSet};
15use tracing::{info_span, instrument, warn};
16
17use crate::{
18    configure::{Configure, DefaultRandom, SecureRandomGenerator},
19    container::{idx::ContainerIdx, richtext::config::StyleConfigMap},
20    cursor::{Cursor, PosType},
21    delta::TreeExternalDiff,
22    diff_calc::{DiffCalculator, DiffMode},
23    event::{Diff, EventTriggerKind, Index, InternalContainerDiff, InternalDiff},
24    fx_map,
25    handler::ValueOrHandler,
26    id::PeerID,
27    lock::{LoroLockGroup, LoroMutex},
28    op::{Op, RawOp},
29    version::Frontiers,
30    ContainerDiff, ContainerType, DocDiff, InternalString, LoroDocInner, LoroValue, OpLog,
31};
32
33pub(crate) mod analyzer;
34pub(crate) mod container_store;
35#[cfg(feature = "counter")]
36mod counter_state;
37mod dead_containers_cache;
38mod list_state;
39mod map_state;
40mod mergeable;
41mod movable_list_state;
42mod richtext_state;
43mod tree_state;
44mod unknown_state;
45
46pub(crate) use self::movable_list_state::{IndexType, MovableListState};
47pub(crate) use container_store::GcStore;
48pub(crate) use list_state::ListState;
49pub(crate) use map_state::MapState;
50pub(crate) use richtext_state::RichtextState;
51pub(crate) use tree_state::FiIfNotConfigured;
52pub(crate) use tree_state::{get_meta_value, FractionalIndexGenResult, NodePosition, TreeState};
53pub use tree_state::{TreeNode, TreeNodeWithChildren, TreeParentId};
54
55use self::{container_store::ContainerWrapper, unknown_state::UnknownState};
56
57#[cfg(feature = "counter")]
58use self::counter_state::CounterState;
59
60use super::{arena::SharedArena, event::InternalDocDiff};
61
62#[cfg(test)]
63thread_local! {
64    static FAIL_NEXT_IMPORT_STATE_APPLY: Cell<bool> = Cell::new(false);
65}
66
67#[cfg(test)]
68pub(crate) fn fail_next_import_state_apply_for_test() {
69    FAIL_NEXT_IMPORT_STATE_APPLY.with(|fail| fail.set(true));
70}
71
72fn visible_container_value_is_empty(kind: ContainerType, value: &LoroValue) -> bool {
73    match kind {
74        ContainerType::Text => value.as_string().is_some_and(|value| value.is_empty()),
75        ContainerType::Map | ContainerType::List | ContainerType::MovableList => {
76            value.is_empty_collection()
77        }
78        ContainerType::Tree => value.as_list().is_some_and(|value| value.is_empty()),
79        #[cfg(feature = "counter")]
80        ContainerType::Counter => false,
81        ContainerType::Unknown(_) => false,
82    }
83}
84
85fn deleted_root_container_value_is_cleared(kind: ContainerType, value: &LoroValue) -> bool {
86    match kind {
87        #[cfg(feature = "counter")]
88        ContainerType::Counter => value.as_double().is_some_and(|value| *value == 0.0),
89        _ => visible_container_value_is_empty(kind, value),
90    }
91}
92
93fn state_decode_error(message: impl Into<Box<str>>) -> LoroError {
94    LoroError::DecodeError(message.into())
95}
96
97fn decode_peer_table(bytes: &mut &[u8], context: &str) -> LoroResult<Vec<PeerID>> {
98    let peer_num = leb128::read::unsigned(bytes)
99        .map_err(|_| state_decode_error(format!("{context}: invalid peer table length")))?;
100    let peer_num = usize::try_from(peer_num)
101        .map_err(|_| state_decode_error(format!("{context}: peer table length overflow")))?;
102    let peer_bytes_len = peer_num
103        .checked_mul(std::mem::size_of::<PeerID>())
104        .ok_or_else(|| state_decode_error(format!("{context}: peer table byte length overflow")))?;
105    if bytes.len() < peer_bytes_len {
106        return Err(state_decode_error(format!(
107            "{context}: truncated peer table"
108        )));
109    }
110
111    let peer_bytes = &bytes[..peer_bytes_len];
112    let peers = peer_bytes
113        .chunks_exact(std::mem::size_of::<PeerID>())
114        .map(|chunk| {
115            let mut buf = [0u8; std::mem::size_of::<PeerID>()];
116            buf.copy_from_slice(chunk);
117            PeerID::from_le_bytes(buf)
118        })
119        .collect();
120    *bytes = &bytes[peer_bytes_len..];
121    Ok(peers)
122}
123
124fn decode_peer_from_table(peers: &[PeerID], peer_idx: usize, context: &str) -> LoroResult<PeerID> {
125    peers
126        .get(peer_idx)
127        .copied()
128        .ok_or_else(|| state_decode_error(format!("{context}: peer index out of range")))
129}
130
131fn read_state_leb_u64(bytes: &mut &[u8], context: &str) -> LoroResult<u64> {
132    leb128::read::unsigned(bytes)
133        .map_err(|_| state_decode_error(format!("{context}: invalid integer")))
134}
135
136fn decode_counter(counter: i32, context: &str) -> LoroResult<i32> {
137    if counter < 0 {
138        return Err(state_decode_error(format!("{context}: negative counter")));
139    }
140
141    Ok(counter)
142}
143
144fn decode_lamport_from_delta(
145    counter: i32,
146    lamport_sub_counter: i32,
147    context: &str,
148) -> LoroResult<Lamport> {
149    decode_counter(counter, context)?;
150    let lamport = counter
151        .checked_add(lamport_sub_counter)
152        .ok_or_else(|| state_decode_error(format!("{context}: lamport overflow")))?;
153    u32::try_from(lamport).map_err(|_| state_decode_error(format!("{context}: negative lamport")))
154}
155
156pub struct DocState {
157    pub(super) peer: Arc<AtomicU64>,
158
159    pub(super) frontiers: Frontiers,
160    // pub(super) states: FxHashMap<ContainerIdx, State>,
161    pub(super) store: ContainerStore,
162    pub(super) arena: SharedArena,
163    pub(crate) config: Configure,
164    // resolve event stuff
165    doc: Weak<LoroDocInner>,
166    // txn related stuff
167    in_txn: bool,
168    changed_idx_in_txn: FxHashSet<ContainerIdx>,
169
170    // diff related stuff
171    event_recorder: EventRecorder,
172
173    dead_containers_cache: DeadContainersCache,
174}
175
176impl std::fmt::Debug for DocState {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        f.debug_struct("DocState")
179            .field("peer", &self.peer)
180            .finish()
181    }
182}
183
184#[derive(Clone, Copy)]
185pub(crate) struct ContainerCreationContext<'a> {
186    pub configure: &'a Configure,
187    pub peer: PeerID,
188}
189
190pub(crate) struct DiffApplyContext<'a> {
191    pub mode: DiffMode,
192    pub doc: &'a Weak<LoroDocInner>,
193}
194
195pub(crate) trait FastStateSnapshot {
196    fn encode_snapshot_fast<W: Write>(&mut self, w: W);
197    fn decode_value(bytes: &[u8]) -> LoroResult<(LoroValue, &[u8])>;
198    fn decode_snapshot_fast(
199        idx: ContainerIdx,
200        v: (LoroValue, &[u8]),
201        ctx: ContainerCreationContext,
202    ) -> LoroResult<Self>
203    where
204        Self: Sized;
205}
206
207#[derive(Debug, Clone, Default)]
208pub(crate) struct ApplyLocalOpReturn {
209    pub deleted_containers: Vec<ContainerID>,
210}
211
212#[enum_dispatch]
213pub(crate) trait ContainerState {
214    fn container_idx(&self) -> ContainerIdx;
215
216    fn is_state_empty(&self) -> bool;
217
218    #[must_use]
219    fn apply_diff_and_convert(&mut self, diff: InternalDiff, ctx: DiffApplyContext) -> Diff;
220
221    /// Apply a state diff. Returns `Err` when the diff is malformed in a
222    /// way that would corrupt the container (e.g. a `Retain`/`Insert` that
223    /// overruns the current state). The error is propagated up to
224    /// `doc.import` so the caller can observe the failure without the
225    /// doc-state panicking inside a lock.
226    fn apply_diff(&mut self, diff: InternalDiff, ctx: DiffApplyContext) -> LoroResult<()>;
227
228    /// Validate a state diff before it is applied. Implementations should keep
229    /// this check proportional to the diff size and must not mutate container
230    /// state.
231    fn validate_diff(&self, _diff: &InternalDiff) -> LoroResult<()> {
232        Ok(())
233    }
234
235    fn apply_local_op(&mut self, raw_op: &RawOp, op: &Op) -> LoroResult<ApplyLocalOpReturn>;
236    /// Convert a state to a diff, such that an empty state will be transformed into the same as this state when it's applied.
237    fn to_diff(&mut self, doc: &Weak<LoroDocInner>) -> Diff;
238
239    fn get_value(&mut self) -> LoroValue;
240
241    /// Get the index of the child container
242    #[allow(unused)]
243    fn get_child_index(&self, id: &ContainerID) -> Option<Index>;
244
245    #[allow(unused)]
246    fn contains_child(&self, id: &ContainerID) -> bool;
247
248    #[allow(unused)]
249    fn get_child_containers(&self) -> Vec<ContainerID>;
250
251    fn fork(&self, config: &Configure) -> Self;
252}
253
254impl<T: FastStateSnapshot> FastStateSnapshot for Box<T> {
255    fn encode_snapshot_fast<W: Write>(&mut self, w: W) {
256        self.as_mut().encode_snapshot_fast(w)
257    }
258
259    fn decode_value(bytes: &[u8]) -> LoroResult<(LoroValue, &[u8])> {
260        T::decode_value(bytes)
261    }
262
263    fn decode_snapshot_fast(
264        idx: ContainerIdx,
265        v: (LoroValue, &[u8]),
266        ctx: ContainerCreationContext,
267    ) -> LoroResult<Self>
268    where
269        Self: Sized,
270    {
271        T::decode_snapshot_fast(idx, v, ctx).map(|x| Box::new(x))
272    }
273}
274
275impl<T: ContainerState> ContainerState for Box<T> {
276    fn container_idx(&self) -> ContainerIdx {
277        self.as_ref().container_idx()
278    }
279
280    fn is_state_empty(&self) -> bool {
281        self.as_ref().is_state_empty()
282    }
283
284    fn apply_diff_and_convert(&mut self, diff: InternalDiff, ctx: DiffApplyContext) -> Diff {
285        self.as_mut().apply_diff_and_convert(diff, ctx)
286    }
287
288    fn apply_diff(&mut self, diff: InternalDiff, ctx: DiffApplyContext) -> LoroResult<()> {
289        self.as_mut().apply_diff(diff, ctx)
290    }
291
292    fn validate_diff(&self, diff: &InternalDiff) -> LoroResult<()> {
293        self.as_ref().validate_diff(diff)
294    }
295
296    fn apply_local_op(&mut self, raw_op: &RawOp, op: &Op) -> LoroResult<ApplyLocalOpReturn> {
297        self.as_mut().apply_local_op(raw_op, op)
298    }
299
300    #[doc = r" Convert a state to a diff, such that an empty state will be transformed into the same as this state when it's applied."]
301    fn to_diff(&mut self, doc: &Weak<LoroDocInner>) -> Diff {
302        self.as_mut().to_diff(doc)
303    }
304
305    fn get_value(&mut self) -> LoroValue {
306        self.as_mut().get_value()
307    }
308
309    #[doc = r" Get the index of the child container"]
310    #[allow(unused)]
311    fn get_child_index(&self, id: &ContainerID) -> Option<Index> {
312        self.as_ref().get_child_index(id)
313    }
314
315    fn contains_child(&self, id: &ContainerID) -> bool {
316        self.as_ref().contains_child(id)
317    }
318
319    #[allow(unused)]
320    fn get_child_containers(&self) -> Vec<ContainerID> {
321        self.as_ref().get_child_containers()
322    }
323
324    fn fork(&self, config: &Configure) -> Self {
325        Box::new(self.as_ref().fork(config))
326    }
327}
328
329#[allow(clippy::enum_variant_names)]
330#[enum_dispatch(ContainerState)]
331#[derive(EnumAsInner, Debug)]
332pub enum State {
333    ListState(Box<ListState>),
334    MovableListState(Box<MovableListState>),
335    MapState(Box<MapState>),
336    RichtextState(Box<RichtextState>),
337    TreeState(Box<TreeState>),
338    #[cfg(feature = "counter")]
339    CounterState(Box<counter_state::CounterState>),
340    UnknownState(UnknownState),
341}
342
343impl From<ListState> for State {
344    fn from(s: ListState) -> Self {
345        Self::ListState(Box::new(s))
346    }
347}
348
349impl From<RichtextState> for State {
350    fn from(s: RichtextState) -> Self {
351        Self::RichtextState(Box::new(s))
352    }
353}
354
355impl From<MovableListState> for State {
356    fn from(s: MovableListState) -> Self {
357        Self::MovableListState(Box::new(s))
358    }
359}
360
361impl From<MapState> for State {
362    fn from(s: MapState) -> Self {
363        Self::MapState(Box::new(s))
364    }
365}
366
367impl From<TreeState> for State {
368    fn from(s: TreeState) -> Self {
369        Self::TreeState(Box::new(s))
370    }
371}
372
373#[cfg(feature = "counter")]
374impl From<CounterState> for State {
375    fn from(s: CounterState) -> Self {
376        Self::CounterState(Box::new(s))
377    }
378}
379
380impl State {
381    pub fn new_list(idx: ContainerIdx) -> Self {
382        Self::ListState(Box::new(ListState::new(idx)))
383    }
384
385    pub fn new_map(idx: ContainerIdx) -> Self {
386        Self::MapState(Box::new(MapState::new(idx)))
387    }
388
389    pub fn new_richtext(idx: ContainerIdx, config: Arc<RwLock<StyleConfigMap>>) -> Self {
390        Self::RichtextState(Box::new(RichtextState::new(idx, config)))
391    }
392
393    pub fn new_tree(idx: ContainerIdx, peer: PeerID) -> Self {
394        Self::TreeState(Box::new(TreeState::new(idx, peer)))
395    }
396
397    pub fn new_unknown(idx: ContainerIdx) -> Self {
398        Self::UnknownState(UnknownState::new(idx))
399    }
400
401    pub fn encode_snapshot_fast<W: Write>(&mut self, mut w: W) {
402        match self {
403            State::ListState(s) => s.encode_snapshot_fast(&mut w),
404            State::MovableListState(s) => s.encode_snapshot_fast(&mut w),
405            State::MapState(s) => s.encode_snapshot_fast(&mut w),
406            State::RichtextState(s) => s.encode_snapshot_fast(&mut w),
407            State::TreeState(s) => s.encode_snapshot_fast(&mut w),
408            #[cfg(feature = "counter")]
409            State::CounterState(s) => s.encode_snapshot_fast(&mut w),
410            State::UnknownState(s) => s.encode_snapshot_fast(&mut w),
411        }
412    }
413
414    pub fn fork(&self, config: &Configure) -> Self {
415        match self {
416            State::ListState(list_state) => State::ListState(list_state.fork(config)),
417            State::MovableListState(movable_list_state) => {
418                State::MovableListState(movable_list_state.fork(config))
419            }
420            State::MapState(map_state) => State::MapState(map_state.fork(config)),
421            State::RichtextState(richtext_state) => {
422                State::RichtextState(richtext_state.fork(config))
423            }
424            State::TreeState(tree_state) => State::TreeState(tree_state.fork(config)),
425            #[cfg(feature = "counter")]
426            State::CounterState(counter_state) => State::CounterState(counter_state.fork(config)),
427            State::UnknownState(unknown_state) => State::UnknownState(unknown_state.fork(config)),
428        }
429    }
430}
431
432impl DocState {
433    #[inline]
434    pub fn new_arc(
435        doc: Weak<LoroDocInner>,
436        arena: SharedArena,
437        config: Configure,
438        lock_group: &LoroLockGroup,
439    ) -> Arc<LoroMutex<Self>> {
440        let peer = DefaultRandom.next_u64();
441        // TODO: maybe we should switch to certain version in oplog?
442
443        let peer = Arc::new(AtomicU64::new(peer));
444        Arc::new(lock_group.new_lock(
445            Self {
446                store: ContainerStore::new(arena.clone(), config.clone(), peer.clone()),
447                peer,
448                arena,
449                frontiers: Frontiers::default(),
450                doc,
451                config,
452                in_txn: false,
453                changed_idx_in_txn: FxHashSet::default(),
454                event_recorder: Default::default(),
455                dead_containers_cache: Default::default(),
456            },
457            crate::lock::LockKind::DocState,
458        ))
459    }
460
461    pub fn fork_with_new_peer_id(
462        &mut self,
463        doc: Weak<LoroDocInner>,
464        arena: SharedArena,
465        config: Configure,
466    ) -> Arc<Mutex<Self>> {
467        let peer = Arc::new(AtomicU64::new(DefaultRandom.next_u64()));
468        let store = self.store.fork(arena.clone(), peer.clone(), config.clone());
469        Arc::new(Mutex::new(Self {
470            peer,
471            frontiers: self.frontiers.clone(),
472            store,
473            arena,
474            config,
475            doc,
476            in_txn: false,
477            changed_idx_in_txn: FxHashSet::default(),
478            event_recorder: Default::default(),
479            dead_containers_cache: Default::default(),
480        }))
481    }
482
483    pub fn start_recording(&mut self) {
484        if self.is_recording() {
485            return;
486        }
487
488        self.event_recorder.recording_diff = true;
489        self.event_recorder.diff_start_version = Some(self.frontiers.clone());
490    }
491
492    #[inline(always)]
493    pub fn stop_and_clear_recording(&mut self) {
494        self.event_recorder = Default::default();
495    }
496
497    #[inline(always)]
498    pub fn is_recording(&self) -> bool {
499        self.event_recorder.recording_diff
500    }
501
502    pub fn refresh_peer_id(&mut self) {
503        self.peer.store(
504            DefaultRandom.next_u64(),
505            std::sync::atomic::Ordering::Relaxed,
506        );
507    }
508
509    /// Take all the diffs that are recorded and convert them to events.
510    pub fn take_events(&mut self) -> Vec<DocDiff> {
511        if !self.is_recording() {
512            return vec![];
513        }
514
515        self.convert_current_batch_diff_into_event();
516        std::mem::take(&mut self.event_recorder.events)
517    }
518
519    /// Record the next diff.
520    /// Caller should call [pre_txn] before calling this.
521    ///
522    /// # Panic
523    ///
524    /// Panic when the diff cannot be merged with the previous diff.
525    /// Caller should call [pre_txn] before calling this to avoid panic.
526    fn record_diff(&mut self, diff: InternalDocDiff) {
527        if !self.event_recorder.recording_diff || diff.diff.is_empty() {
528            return;
529        }
530
531        let Some(last_diff) = self.event_recorder.diffs.last_mut() else {
532            self.event_recorder.diffs.push(diff.into_owned());
533            return;
534        };
535
536        if last_diff.can_merge(&diff) {
537            self.event_recorder.diffs.push(diff.into_owned());
538            return;
539        }
540
541        panic!("should call pre_txn before record_diff")
542    }
543
544    /// This should be called when DocState is going to apply a transaction / a diff.
545    fn pre_txn(&mut self, next_origin: InternalString, next_trigger: EventTriggerKind) {
546        if !self.is_recording() {
547            return;
548        }
549
550        let Some(last_diff) = self.event_recorder.diffs.last() else {
551            return;
552        };
553
554        if last_diff.origin == next_origin && last_diff.by == next_trigger {
555            return;
556        }
557
558        // current diff batch cannot merge with the incoming diff,
559        // need to convert all the current diffs into event
560        self.convert_current_batch_diff_into_event()
561    }
562
563    fn convert_current_batch_diff_into_event(&mut self) {
564        let recorder = &mut self.event_recorder;
565        if recorder.diffs.is_empty() {
566            return;
567        }
568
569        let diffs = std::mem::take(&mut recorder.diffs);
570        let start = recorder.diff_start_version.take().unwrap();
571        recorder.diff_start_version = Some((*diffs.last().unwrap().new_version).to_owned());
572        let event = self.diffs_to_event(diffs, start);
573        self.event_recorder.events.push(event);
574    }
575
576    /// Change the peer id of this doc state.
577    /// It changes the peer id for the future txn on this AppState
578    #[inline]
579    pub fn set_peer_id(&mut self, peer: PeerID) {
580        self.peer.store(peer, std::sync::atomic::Ordering::Relaxed);
581    }
582
583    pub fn peer_id(&self) -> PeerID {
584        self.peer.load(std::sync::atomic::Ordering::Relaxed)
585    }
586
587    /// It's expected that diff only contains [`InternalDiff`]
588    ///
589    /// Returns `Err` if any container state rejects its diff as malformed
590    /// (see `ContainerState::validate_diff`). Validation runs before mutating
591    /// any container state, so a returned error leaves the doc-state view at
592    /// the previous version.
593    #[instrument(skip_all)]
594    pub(crate) fn apply_diff(
595        &mut self,
596        mut diff: InternalDocDiff<'static>,
597        diff_mode: DiffMode,
598    ) -> LoroResult<()> {
599        if self.in_txn {
600            return Err(LoroError::TransactionError(
601                "apply_diff should not be called in a transaction"
602                    .to_string()
603                    .into_boxed_str(),
604            ));
605        }
606
607        let is_recording = self.is_recording();
608        let Cow::Owned(mut diffs) = std::mem::take(&mut diff.diff) else {
609            unreachable!()
610        };
611        self.validate_diff_batch(&diffs)?;
612        #[cfg(test)]
613        if diff.origin.as_str() == "__loro_fail_import_state_apply" {
614            return Err(LoroError::internal("state apply failpoint"));
615        }
616        #[cfg(test)]
617        if diff.by == EventTriggerKind::Import {
618            let should_fail = FAIL_NEXT_IMPORT_STATE_APPLY.with(|fail| {
619                let should_fail = fail.get();
620                if should_fail {
621                    fail.set(false);
622                }
623                should_fail
624            });
625            if should_fail {
626                return Err(LoroError::internal("state apply failpoint"));
627            }
628        }
629        match diff_mode {
630            DiffMode::Checkout => {
631                self.dead_containers_cache.clear();
632            }
633            _ => {
634                self.dead_containers_cache.clear_alive();
635            }
636        }
637        self.pre_txn(diff.origin.clone(), diff.by);
638
639        // # Revival
640        //
641        // A Container, if it is deleted from its parent Container, will still exist
642        // in the internal state of Loro;  whereas on the user side, a tree structure
643        // is maintained following Events, and at this point, the corresponding state
644        // is considered deleted.
645        //
646        // Sometimes, this "pseudo-dead" Container may be revived (for example, through
647        // backtracking or parallel editing),  and the user side should receive an Event
648        // that restores the consistency between the revived Container and the  internal
649        // state of Loro. This Event is required to restore the pseudo-dead  Container
650        // State to its current state on Loro, and we refer to this process as "revival".
651        //
652        // Revival occurs during the application of the internal diff, and this operation
653        // is necessary when it needs to be converted into an external Event.
654        //
655        // We can utilize the output of the Diff to determine which child nodes should be revived.
656        //
657        // For nodes that are to be revived, we can disregard the Events output by their
658        // round of apply_diff_and_convert,  and instead, directly convert their state into
659        // an Event once their application is complete.
660        //
661        // Suppose A is revived and B is A's child, and B also needs to be revived; therefore,
662        // we should process each level alternately.
663
664        // We need to ensure diff is processed in order
665        diffs.sort_by_cached_key(|diff| self.arena.get_depth(diff.idx));
666        let mut to_revive_in_next_layer: FxHashSet<ContainerIdx> = FxHashSet::default();
667        let mut to_revive_in_this_layer: FxHashSet<ContainerIdx> = FxHashSet::default();
668        let mut last_depth = 0;
669        let len = diffs.len();
670        for mut diff in std::mem::replace(&mut diffs, Vec::with_capacity(len)) {
671            let Some(depth) = self.arena.get_depth(diff.idx) else {
672                warn!("{:?} is not in arena. It could be a dangling container that was deleted before the shallow start version.", self.arena.idx_to_id(diff.idx));
673                continue;
674            };
675            let this_depth = depth.get();
676            while this_depth > last_depth {
677                // Clear `to_revive` when we are going to process a new level
678                // so that we can process the revival of the next level
679                let to_create = std::mem::take(&mut to_revive_in_this_layer);
680                to_revive_in_this_layer = std::mem::take(&mut to_revive_in_next_layer);
681                for new in to_create {
682                    let state = self.store.get_or_create_mut(new);
683                    if state.is_state_empty() {
684                        continue;
685                    }
686
687                    let external_diff = state.to_diff(&self.doc);
688                    trigger_on_new_container(
689                        &external_diff,
690                        |cid| {
691                            to_revive_in_this_layer.insert(cid);
692                        },
693                        &self.arena,
694                    );
695
696                    diffs.push(InternalContainerDiff {
697                        idx: new,
698                        bring_back: true,
699                        diff: external_diff.into(),
700                        diff_mode: DiffMode::Checkout,
701                    });
702                }
703
704                last_depth += 1;
705            }
706
707            let idx = diff.idx;
708            let internal_diff = std::mem::take(&mut diff.diff);
709            match &internal_diff {
710                crate::event::DiffVariant::None => {
711                    if is_recording {
712                        let state = self.store.get_or_create_mut(diff.idx);
713                        let extern_diff = state.to_diff(&self.doc);
714                        trigger_on_new_container(
715                            &extern_diff,
716                            |cid| {
717                                to_revive_in_next_layer.insert(cid);
718                            },
719                            &self.arena,
720                        );
721                        diff.diff = extern_diff.into();
722                    }
723                }
724                crate::event::DiffVariant::Internal(_) => {
725                    let cid = self.arena.idx_to_id(idx).unwrap();
726                    info_span!("apply diff on", container_id = ?cid).in_scope(
727                        || -> LoroResult<()> {
728                            if self.in_txn {
729                                self.changed_idx_in_txn.insert(idx);
730                            }
731                            let state = self.store.get_or_create_mut(idx);
732                            if is_recording {
733                                // process bring_back before apply
734                                let external_diff =
735                                    if diff.bring_back || to_revive_in_this_layer.contains(&idx) {
736                                        state.apply_diff(
737                                            internal_diff.into_internal().unwrap(),
738                                            DiffApplyContext {
739                                                mode: diff.diff_mode,
740                                                doc: &self.doc,
741                                            },
742                                        )?;
743                                        state.to_diff(&self.doc)
744                                    } else {
745                                        state.apply_diff_and_convert(
746                                            internal_diff.into_internal().unwrap(),
747                                            DiffApplyContext {
748                                                mode: diff.diff_mode,
749                                                doc: &self.doc,
750                                            },
751                                        )
752                                    };
753                                trigger_on_new_container(
754                                    &external_diff,
755                                    |cid| {
756                                        to_revive_in_next_layer.insert(cid);
757                                    },
758                                    &self.arena,
759                                );
760                                diff.diff = external_diff.into();
761                            } else {
762                                state.apply_diff(
763                                    internal_diff.into_internal().unwrap(),
764                                    DiffApplyContext {
765                                        mode: diff.diff_mode,
766                                        doc: &self.doc,
767                                    },
768                                )?;
769                            }
770                            Ok(())
771                        },
772                    )?;
773                }
774                crate::event::DiffVariant::External(_) => unreachable!(),
775            }
776
777            to_revive_in_this_layer.remove(&idx);
778            if !diff.diff.is_empty() {
779                diffs.push(diff);
780            }
781        }
782
783        // Revive the last several layers
784        while !to_revive_in_this_layer.is_empty() || !to_revive_in_next_layer.is_empty() {
785            let to_create = std::mem::take(&mut to_revive_in_this_layer);
786            for new in to_create {
787                let state = self.store.get_or_create_mut(new);
788                if state.is_state_empty() {
789                    continue;
790                }
791
792                let external_diff = state.to_diff(&self.doc);
793                trigger_on_new_container(
794                    &external_diff,
795                    |cid| {
796                        to_revive_in_next_layer.insert(cid);
797                    },
798                    &self.arena,
799                );
800
801                if !external_diff.is_empty() {
802                    diffs.push(InternalContainerDiff {
803                        idx: new,
804                        bring_back: true,
805                        diff: external_diff.into(),
806                        diff_mode: DiffMode::Checkout,
807                    });
808                }
809            }
810
811            to_revive_in_this_layer = std::mem::take(&mut to_revive_in_next_layer);
812        }
813
814        diff.diff = diffs.into();
815        self.frontiers = diff.new_version.clone().into_owned();
816
817        if self.is_recording() {
818            self.record_diff(diff)
819        }
820        Ok(())
821    }
822
823    fn validate_diff_batch(&mut self, diffs: &[InternalContainerDiff]) -> LoroResult<()> {
824        for diff in diffs {
825            let crate::event::DiffVariant::Internal(internal_diff) = &diff.diff else {
826                continue;
827            };
828            if let Some(state) = self.store.get_container(diff.idx) {
829                state.validate_diff(internal_diff)?;
830            } else {
831                let state = create_state_(diff.idx, &self.config, self.peer_id());
832                state.validate_diff(internal_diff)?;
833            }
834        }
835
836        Ok(())
837    }
838
839    pub fn apply_local_op(&mut self, raw_op: &RawOp, op: &Op) -> LoroResult<()> {
840        // set parent first, `MapContainer` will only be created for TreeID that does not contain
841        self.set_container_parent_by_raw_op(raw_op);
842        let state = self.store.get_or_create_mut(op.container);
843        if self.in_txn {
844            self.changed_idx_in_txn.insert(op.container);
845        }
846        let ret = state.apply_local_op(raw_op, op)?;
847        if !ret.deleted_containers.is_empty() {
848            self.dead_containers_cache.clear_alive();
849        }
850
851        Ok(())
852    }
853
854    pub(crate) fn start_txn(&mut self, origin: InternalString, trigger: EventTriggerKind) {
855        self.pre_txn(origin, trigger);
856        self.in_txn = true;
857    }
858
859    pub(crate) fn abort_txn(&mut self) {
860        self.in_txn = false;
861    }
862
863    pub fn iter_and_decode_all(&mut self) -> impl Iterator<Item = &mut State> {
864        self.store.iter_and_decode_all()
865    }
866
867    pub(crate) fn iter_all_containers_mut(
868        &mut self,
869    ) -> impl Iterator<Item = (ContainerIdx, &mut ContainerWrapper)> {
870        self.store.iter_all_containers()
871    }
872
873    pub fn does_container_exist(&mut self, id: &ContainerID) -> bool {
874        // A container may exist even if not yet registered in the arena.
875        // Check arena first, then fall back to KV presence in the store.
876        let is_mergeable = id.is_mergeable();
877        if id.is_root() && !is_mergeable {
878            return true;
879        }
880
881        if !is_mergeable {
882            if let Some(idx) = self.arena.id_to_idx(id) {
883                if self.arena.get_depth(idx).is_some() {
884                    return true;
885                }
886            }
887        }
888
889        if self.store.contains_id(id) {
890            return true;
891        }
892
893        // An ensured-but-empty mergeable child has no ops or KV state of its own yet;
894        // the parent map's binary child ref is the source of truth for its existence.
895        is_mergeable && self.get_reachable(id)
896    }
897
898    pub(crate) fn commit_txn(&mut self, new_frontiers: Frontiers, diff: Option<InternalDocDiff>) {
899        self.in_txn = false;
900        self.frontiers = new_frontiers;
901        if let Some(diff) = diff {
902            if self.is_recording() {
903                self.record_diff(diff);
904            }
905        }
906    }
907
908    /// Ensure the container is created and will be encoded in the next `encode` call
909    #[inline]
910    pub(crate) fn ensure_container(&mut self, id: &ContainerID) {
911        self.store.ensure_container(id);
912    }
913
914    /// Ensure all alive containers are created in DocState and will be encoded in the next `encode` call
915    pub(crate) fn ensure_all_alive_containers(&mut self) -> FxHashSet<ContainerID> {
916        // TODO: PERF This can be optimized because we shouldn't need to call get_value for
917        // all the containers every time we export
918        let ans = self.get_all_alive_containers();
919        for id in ans.iter() {
920            self.ensure_container(id);
921        }
922
923        ans
924    }
925
926    pub(crate) fn get_value_by_idx(&mut self, container_idx: ContainerIdx) -> LoroValue {
927        self.store
928            .get_value(container_idx)
929            .unwrap_or_else(|| container_idx.get_type().default_value())
930    }
931
932    pub(crate) fn get_map_value_by_key(
933        &mut self,
934        container_idx: ContainerIdx,
935        key: &str,
936    ) -> Option<LoroValue> {
937        self.store.map_get(container_idx, key)
938    }
939
940    pub(crate) fn get_map_len(&mut self, container_idx: ContainerIdx) -> usize {
941        self.store.map_len(container_idx)
942    }
943
944    pub(crate) fn get_map_keys(&mut self, container_idx: ContainerIdx) -> Vec<InternalString> {
945        self.store.map_keys(container_idx)
946    }
947
948    pub(crate) fn get_map_entries(
949        &mut self,
950        container_idx: ContainerIdx,
951    ) -> Vec<(InternalString, LoroValue)> {
952        self.store.map_entries(container_idx)
953    }
954
955    pub(crate) fn get_list_value_at(
956        &mut self,
957        container_idx: ContainerIdx,
958        index: usize,
959    ) -> Option<LoroValue> {
960        self.store.list_get(container_idx, index)
961    }
962
963    pub(crate) fn get_list_len(&mut self, container_idx: ContainerIdx) -> usize {
964        self.store.list_len(container_idx)
965    }
966
967    pub(crate) fn get_list_values(&mut self, container_idx: ContainerIdx) -> Vec<LoroValue> {
968        self.store.list_values(container_idx)
969    }
970
971    pub(crate) fn get_text_unicode_len(&mut self, container_idx: ContainerIdx) -> usize {
972        self.store.text_unicode_len(container_idx).unwrap_or(0)
973    }
974
975    pub(crate) fn get_text_utf16_len(&mut self, container_idx: ContainerIdx) -> usize {
976        self.store.text_utf16_len(container_idx).unwrap_or(0)
977    }
978
979    pub(crate) fn get_text_utf8_len(&mut self, container_idx: ContainerIdx) -> usize {
980        self.store.text_utf8_len(container_idx).unwrap_or(0)
981    }
982
983    pub(crate) fn has_decoded_container_state(&mut self, container_idx: ContainerIdx) -> bool {
984        self.store.has_decoded_state(container_idx)
985    }
986
987    /// Length of a text container in the given `pos_type`, taking a single
988    /// `DocState` lock and a single container-store lookup.
989    ///
990    /// The per-`pos_type` store helpers already branch on decoded-vs-lazy
991    /// internally, and their lazy branch reads the cheap length metadata
992    /// without materializing the full richtext state — preserving the
993    /// lazy-snapshot memory behavior. Callers previously took two separate
994    /// locks (one to check decoded-ness, one to query), which showed up as a
995    /// per-op regression on the text editing hot path. Only `Entity` length has
996    /// no store helper and falls back to the state path.
997    pub(crate) fn get_text_len(&mut self, container_idx: ContainerIdx, pos_type: PosType) -> usize {
998        match pos_type {
999            PosType::Unicode => self.get_text_unicode_len(container_idx),
1000            PosType::Utf16 => self.get_text_utf16_len(container_idx),
1001            PosType::Event if cfg!(feature = "wasm") => self.get_text_utf16_len(container_idx),
1002            PosType::Event => self.get_text_unicode_len(container_idx),
1003            PosType::Bytes => self.get_text_utf8_len(container_idx),
1004            PosType::Entity => self.with_state_mut(container_idx, |state| {
1005                state.as_richtext_state_mut().unwrap().len(PosType::Entity)
1006            }),
1007        }
1008    }
1009
1010    /// Set the state of the container with the given container idx.
1011    /// This is only used for decode.
1012    ///
1013    /// # Panic
1014    ///
1015    /// If the state is not empty.
1016    pub(super) fn init_with_states_and_version(
1017        &mut self,
1018        frontiers: Frontiers,
1019        oplog: &OpLog,
1020        unknown_containers: Vec<ContainerIdx>,
1021        need_to_register_parent: bool,
1022        origin: InternalString,
1023    ) -> LoroResult<()> {
1024        self.pre_txn(Default::default(), EventTriggerKind::Import);
1025        if need_to_register_parent {
1026            for state in self.store.iter_and_decode_all() {
1027                let idx = state.container_idx();
1028                let s = state;
1029                for child_id in s.get_child_containers() {
1030                    let child_idx = self.arena.register_container(&child_id);
1031                    self.arena.set_parent(child_idx, Some(idx));
1032                }
1033            }
1034        }
1035
1036        if !unknown_containers.is_empty() {
1037            let mut diff_calc = DiffCalculator::new(false);
1038            let stack_vv;
1039            let vv = if oplog.frontiers() == &frontiers {
1040                oplog.vv()
1041            } else {
1042                stack_vv = oplog.dag().frontiers_to_vv(&frontiers);
1043                stack_vv.as_ref().unwrap()
1044            };
1045
1046            let (unknown_diffs, _diff_mode) = diff_calc.calc_diff_internal(
1047                oplog,
1048                &Default::default(),
1049                &Default::default(),
1050                vv,
1051                &frontiers,
1052                Some(&|idx| !idx.is_unknown() && unknown_containers.contains(&idx)),
1053            );
1054            self.apply_diff(
1055                InternalDocDiff {
1056                    origin: origin.clone(),
1057                    by: EventTriggerKind::Import,
1058                    diff: unknown_diffs.into(),
1059                    new_version: Cow::Owned(frontiers.clone()),
1060                },
1061                DiffMode::Checkout,
1062            )?;
1063        }
1064
1065        if self.is_recording() {
1066            let diff: Vec<_> = self
1067                .store
1068                .iter_all_containers()
1069                .map(|(idx, state)| InternalContainerDiff {
1070                    idx,
1071                    bring_back: false,
1072                    diff: state
1073                        .get_state_mut(
1074                            idx,
1075                            ContainerCreationContext {
1076                                configure: &self.config,
1077                                peer: self.peer.load(Ordering::Relaxed),
1078                            },
1079                        )
1080                        .to_diff(&self.doc)
1081                        .into(),
1082                    diff_mode: DiffMode::Checkout,
1083                })
1084                .collect();
1085
1086            self.record_diff(InternalDocDiff {
1087                origin,
1088                by: EventTriggerKind::Import,
1089                diff: diff.into(),
1090                new_version: Cow::Borrowed(&frontiers),
1091            });
1092        }
1093
1094        self.frontiers = frontiers;
1095        Ok(())
1096    }
1097
1098    #[inline(always)]
1099    #[allow(unused)]
1100    pub(crate) fn with_state<F, R>(&mut self, idx: ContainerIdx, f: F) -> R
1101    where
1102        F: FnOnce(&State) -> R,
1103    {
1104        let depth = self.arena.get_depth(idx).unwrap().get() as usize;
1105        let state = self.store.get_or_create_imm(idx);
1106        f(state)
1107    }
1108
1109    #[inline(always)]
1110    pub(crate) fn with_state_mut<F, R>(&mut self, idx: ContainerIdx, f: F) -> R
1111    where
1112        F: FnOnce(&mut State) -> R,
1113    {
1114        let state = self.store.get_or_create_mut(idx);
1115        f(state)
1116    }
1117
1118    pub(super) fn is_in_txn(&self) -> bool {
1119        self.in_txn
1120    }
1121
1122    pub fn can_import_snapshot(&self) -> bool {
1123        !self.in_txn && self.arena.can_import_snapshot() && self.store.can_import_snapshot()
1124    }
1125
1126    pub(crate) fn reset_to_empty_for_failed_snapshot_import(&mut self) {
1127        self.frontiers = Frontiers::default();
1128        self.store =
1129            ContainerStore::new(self.arena.clone(), self.config.clone(), self.peer.clone());
1130        self.in_txn = false;
1131        self.changed_idx_in_txn.clear();
1132        self.event_recorder = Default::default();
1133        self.dead_containers_cache = Default::default();
1134    }
1135
1136    pub fn get_value(&mut self) -> LoroValue {
1137        let roots = self.preferred_root_containers();
1138        let ans: loro_common::LoroMapValue = roots
1139            .into_iter()
1140            .map(|idx| {
1141                let id = self.arena.idx_to_id(idx).unwrap();
1142                let ContainerID::Root {
1143                    name,
1144                    container_type: _,
1145                } = &id
1146                else {
1147                    unreachable!()
1148                };
1149                (name.to_string(), LoroValue::Container(id))
1150            })
1151            .collect();
1152        LoroValue::Map(ans)
1153    }
1154
1155    pub fn get_deep_value(&mut self) -> LoroValue {
1156        let roots = self.preferred_root_containers();
1157        let mut ans = FxHashMap::with_capacity_and_hasher(roots.len(), Default::default());
1158        let binding = self.config.deleted_root_containers.clone();
1159        let deleted_root_container = binding.lock();
1160        let should_hide_empty_root_container = self
1161            .config
1162            .hide_empty_root_containers
1163            .load(Ordering::Relaxed);
1164        for root_idx in roots {
1165            let id = self.arena.idx_to_id(root_idx).unwrap();
1166            match &id {
1167                loro_common::ContainerID::Root { name, .. } => {
1168                    let v = self.get_container_deep_value(root_idx);
1169                    if should_hide_empty_root_container
1170                        && visible_container_value_is_empty(root_idx.get_type(), &v)
1171                    {
1172                        continue;
1173                    }
1174
1175                    if deleted_root_container.contains(&id)
1176                        && deleted_root_container_value_is_cleared(root_idx.get_type(), &v)
1177                    {
1178                        continue;
1179                    }
1180
1181                    ans.insert(name.to_string(), v);
1182                }
1183                loro_common::ContainerID::Normal { .. } => {
1184                    unreachable!()
1185                }
1186            }
1187        }
1188
1189        LoroValue::Map(ans.into())
1190    }
1191
1192    pub fn get_deep_value_with_id(&mut self) -> LoroValue {
1193        let roots = self.preferred_root_containers();
1194        let mut ans = FxHashMap::with_capacity_and_hasher(roots.len(), Default::default());
1195        for root_idx in roots {
1196            let id = self.arena.idx_to_id(root_idx).unwrap();
1197            match id.clone() {
1198                loro_common::ContainerID::Root { name, .. } => {
1199                    ans.insert(
1200                        name.to_string(),
1201                        self.get_container_deep_value_with_id(root_idx, Some(id)),
1202                    );
1203                }
1204                loro_common::ContainerID::Normal { .. } => {
1205                    unreachable!()
1206                }
1207            }
1208        }
1209
1210        LoroValue::Map(ans.into())
1211    }
1212
1213    pub(crate) fn preferred_root_containers(&mut self) -> Vec<ContainerIdx> {
1214        let flag = self.store.load_root_containers();
1215        // Mergeable cids live in a private namespace and are logically children of a regular
1216        // Map — they must not appear here. `top_level_root_containers` already excludes them,
1217        // so this loop is O(top_level_roots) regardless of the number of mergeable cids.
1218        let roots = self.arena.top_level_root_containers(flag);
1219        let mut selected = FxHashMap::default();
1220        let mut names = Vec::new();
1221
1222        for idx in roots {
1223            let Some(id) = self.arena.idx_to_id(idx) else {
1224                continue;
1225            };
1226            if !self.store.contains_id(&id) {
1227                continue;
1228            }
1229            let Some(name) = self.root_container_name(idx) else {
1230                continue;
1231            };
1232            let is_empty = self.root_container_is_empty(idx);
1233            match selected.entry(name.clone()) {
1234                std::collections::hash_map::Entry::Vacant(entry) => {
1235                    names.push(name);
1236                    entry.insert((idx, is_empty));
1237                }
1238                std::collections::hash_map::Entry::Occupied(mut entry) => {
1239                    let (_, selected_is_empty) = entry.get();
1240                    // Keep the previous last-root-wins behavior, except an empty
1241                    // root should not hide a non-empty root with the same name.
1242                    if *selected_is_empty || !is_empty {
1243                        entry.insert((idx, is_empty));
1244                    }
1245                }
1246            }
1247        }
1248
1249        names
1250            .into_iter()
1251            .filter_map(|name| selected.remove(&name).map(|(idx, _)| idx))
1252            .collect()
1253    }
1254
1255    pub(crate) fn preferred_root_container_idx_by_key(
1256        &mut self,
1257        root_index: &InternalString,
1258    ) -> Option<ContainerIdx> {
1259        let flag = self.store.load_root_containers();
1260        // Same reasoning as `preferred_root_containers`: only top-level roots are eligible.
1261        let roots = self.arena.top_level_root_containers(flag);
1262        let mut selected = None;
1263
1264        for idx in roots {
1265            let Some(id) = self.arena.idx_to_id(idx) else {
1266                continue;
1267            };
1268            if !self.store.contains_id(&id) {
1269                continue;
1270            }
1271            let Some(name) = self.root_container_name(idx) else {
1272                continue;
1273            };
1274            if &name != root_index {
1275                continue;
1276            }
1277
1278            let is_empty = self.root_container_is_empty(idx);
1279            match selected {
1280                None => selected = Some((idx, is_empty)),
1281                Some((_, selected_is_empty)) => {
1282                    if selected_is_empty || !is_empty {
1283                        selected = Some((idx, is_empty));
1284                    }
1285                }
1286            }
1287        }
1288
1289        selected.map(|(idx, _)| idx)
1290    }
1291
1292    fn root_container_name(&self, idx: ContainerIdx) -> Option<InternalString> {
1293        match self.arena.idx_to_id(idx)? {
1294            ContainerID::Root { name, .. } => Some(name),
1295            ContainerID::Normal { .. } => None,
1296        }
1297    }
1298
1299    fn root_container_is_empty(&mut self, idx: ContainerIdx) -> bool {
1300        let value = self
1301            .store
1302            .get_value(idx)
1303            .unwrap_or_else(|| idx.get_type().default_value());
1304        visible_container_value_is_empty(idx.get_type(), &value)
1305    }
1306
1307    pub fn get_all_container_value_flat(&mut self) -> LoroValue {
1308        let mut map = FxHashMap::default();
1309        self.store.iter_and_decode_all().for_each(|c| {
1310            let value = c.get_value();
1311            let cid = self.arena.idx_to_id(c.container_idx()).unwrap().to_string();
1312            map.insert(cid, value);
1313        });
1314
1315        LoroValue::Map(map.into())
1316    }
1317
1318    pub(crate) fn get_container_deep_value_with_id(
1319        &mut self,
1320        container: ContainerIdx,
1321        id: Option<ContainerID>,
1322    ) -> LoroValue {
1323        let id = id.unwrap_or_else(|| self.arena.idx_to_id(container).unwrap());
1324        let Some(value) = self.store.get_value(container) else {
1325            return container.get_type().default_value();
1326        };
1327        let cid_str = LoroValue::String(format!("idx:{}, id:{}", container.to_index(), id).into());
1328        match value {
1329            LoroValue::Container(_) => unreachable!(),
1330            LoroValue::List(mut list) => {
1331                if container.get_type() == ContainerType::Tree {
1332                    get_meta_value(list.make_mut(), self);
1333                } else {
1334                    if list.iter().all(|x| !x.is_container()) {
1335                        return LoroValue::Map(
1336                            (fx_map!(
1337                                "cid".into() => cid_str,
1338                                "value".into() =>  LoroValue::List(list)
1339                            ))
1340                            .into(),
1341                        );
1342                    }
1343
1344                    let list_mut = list.make_mut();
1345                    for item in list_mut.iter_mut() {
1346                        if item.is_container() {
1347                            let container = item.as_container().unwrap();
1348                            let container_idx = self.arena.register_container(container);
1349                            let value = self.get_container_deep_value_with_id(
1350                                container_idx,
1351                                Some(container.clone()),
1352                            );
1353                            *item = value;
1354                        }
1355                    }
1356                }
1357                LoroValue::Map(
1358                    (fx_map!(
1359                        "cid".into() => cid_str,
1360                        "value".into() => LoroValue::List(list)
1361                    ))
1362                    .into(),
1363                )
1364            }
1365            LoroValue::Map(mut map) => {
1366                // A map's mergeable children are encoded as compact binary markers in the map's
1367                // own value table (loro-dev/loro#759), so they are already present in `map` here.
1368                // Derive the active children straight from that value — re-fetching the
1369                // `MapState` would force the snapshot-backed container to decode and break the
1370                // lazy-value invariant for roots.
1371                let mergeable_children = self.mergeable_children_from_value(&id, &map);
1372
1373                let map_mut = map.make_mut();
1374                for (_key, value) in map_mut.iter_mut() {
1375                    if value.is_container() {
1376                        let container = value.as_container().unwrap();
1377                        let container_idx = self.arena.register_container(container);
1378                        let new_value = self.get_container_deep_value_with_id(
1379                            container_idx,
1380                            Some(container.clone()),
1381                        );
1382                        *value = new_value;
1383                    }
1384                }
1385                // Replace each marker with the nested deep value of its resolved child, keyed
1386                // under the same logical key.
1387                for (key, cid) in mergeable_children {
1388                    let child_idx = self.arena.register_container(&cid);
1389                    let new_value = self.get_container_deep_value_with_id(child_idx, Some(cid));
1390                    map_mut.insert(key.to_string(), new_value);
1391                }
1392
1393                LoroValue::Map(
1394                    (fx_map!(
1395                        "cid".into() => cid_str,
1396                        "value".into() => LoroValue::Map(map)
1397                    ))
1398                    .into(),
1399                )
1400            }
1401            _ => LoroValue::Map(
1402                (fx_map!(
1403                    "cid".into() => cid_str,
1404                    "value".into() => value
1405                ))
1406                .into(),
1407            ),
1408        }
1409    }
1410
1411    pub fn get_container_deep_value(&mut self, container: ContainerIdx) -> LoroValue {
1412        let Some(value) = self.store.get_value(container) else {
1413            return container.get_type().default_value();
1414        };
1415        match value {
1416            LoroValue::Container(_) => unreachable!(),
1417            LoroValue::List(mut list) => {
1418                if container.get_type() == ContainerType::Tree {
1419                    // Each tree node has an associated map container to represent
1420                    // the metadata of this node. When the user get the deep value,
1421                    // we need to add a field named `meta` to the tree node,
1422                    // whose value is deep value of map container.
1423                    get_meta_value(list.make_mut(), self);
1424                } else {
1425                    if list.iter().all(|x| !x.is_container()) {
1426                        return LoroValue::List(list);
1427                    }
1428
1429                    let list_mut = list.make_mut();
1430                    for item in list_mut.iter_mut() {
1431                        if item.is_container() {
1432                            let container = item.as_container().unwrap();
1433                            let container_idx = self.arena.register_container(container);
1434                            let value = self.get_container_deep_value(container_idx);
1435                            *item = value;
1436                        }
1437                    }
1438                }
1439                LoroValue::List(list)
1440            }
1441            LoroValue::Map(mut map) => {
1442                // A map's mergeable children are encoded as compact binary markers in the map's
1443                // own value table (loro-dev/loro#759), so they are already present in `map` here.
1444                // Derive the active children straight from that value — re-fetching the
1445                // `MapState` would force the snapshot-backed container to decode and break the
1446                // lazy-value invariant for roots.
1447                let mergeable_children = self
1448                    .arena
1449                    .idx_to_id(container)
1450                    .map(|parent_id| self.mergeable_children_from_value(&parent_id, &map))
1451                    .unwrap_or_default();
1452
1453                if mergeable_children.is_empty() && map.iter().all(|x| !x.1.is_container()) {
1454                    return LoroValue::Map(map);
1455                }
1456
1457                let map_mut = map.make_mut();
1458                for (_key, value) in map_mut.iter_mut() {
1459                    if value.is_container() {
1460                        let container = value.as_container().unwrap();
1461                        let container_idx = self.arena.register_container(container);
1462                        let new_value = self.get_container_deep_value(container_idx);
1463                        *value = new_value;
1464                    }
1465                }
1466                // Replace each marker with the nested deep value of its resolved child, keyed
1467                // under the same logical key.
1468                for (key, cid) in mergeable_children {
1469                    let child_idx = self.arena.register_container(&cid);
1470                    let new_value = self.get_container_deep_value(child_idx);
1471                    map_mut.insert(key.to_string(), new_value);
1472                }
1473                LoroValue::Map(map)
1474            }
1475            _ => value,
1476        }
1477    }
1478
1479    pub(crate) fn get_all_alive_containers(&mut self) -> FxHashSet<ContainerID> {
1480        let flag = self.store.load_all();
1481        let mut ans = FxHashSet::default();
1482        let mut to_visit = self
1483            .arena
1484            .root_containers(flag)
1485            .iter()
1486            .map(|x| self.arena.get_container_id(*x).unwrap())
1487            .filter(|id| self.store.contains_id(id))
1488            .collect_vec();
1489
1490        while let Some(id) = to_visit.pop() {
1491            self.get_alive_children_of(&id, &mut to_visit);
1492            ans.insert(id);
1493        }
1494
1495        ans
1496    }
1497
1498    pub(crate) fn get_alive_children_of(&mut self, id: &ContainerID, ans: &mut Vec<ContainerID>) {
1499        let idx = self.arena.register_container(id);
1500        let Some(value) = self.store.get_value(idx) else {
1501            return;
1502        };
1503
1504        match value {
1505            LoroValue::Container(_) => unreachable!(),
1506            LoroValue::List(list) => {
1507                if idx.get_type() == ContainerType::Tree {
1508                    // Each tree node has an associated map container to represent
1509                    // the metadata of this node. When the user get the deep value,
1510                    // we need to add a field named `meta` to the tree node,
1511                    // whose value is deep value of map container.
1512                    let mut list = list.unwrap();
1513                    while let Some(node) = list.pop() {
1514                        let map = node.as_map().unwrap();
1515                        let meta = map.get("meta").unwrap();
1516                        let id = meta.as_container().unwrap();
1517                        ans.push(id.clone());
1518                        let children = map.get("children").unwrap();
1519                        let children = children.as_list().unwrap();
1520                        for child in children.iter() {
1521                            list.push(child.clone());
1522                        }
1523                    }
1524                } else {
1525                    for item in list.iter() {
1526                        if let LoroValue::Container(id) = item {
1527                            ans.push(id.clone());
1528                        }
1529                    }
1530                }
1531            }
1532            LoroValue::Map(map) => {
1533                for (_key, value) in map.iter() {
1534                    if let LoroValue::Container(id) = value {
1535                        ans.push(id.clone());
1536                    }
1537                }
1538                // Mergeable children are resolved from compact binary markers stored in this
1539                // map's own value table (loro-dev/loro#759): the active child at each key is the
1540                // deterministic cid for whichever marker the map's regular LWW resolved to.
1541                // Derive them from the value we already fetched lazily above — re-reading the
1542                // decoded `MapState` would force a snapshot-backed map to materialize its full
1543                // state. Pull them in so alive-container walks (notably shallow snapshot export)
1544                // include mergeable cids and don't filter their KV out by `retain_keys`.
1545                let mergeable_cids: Vec<ContainerID> = self
1546                    .arena
1547                    .idx_to_id(idx)
1548                    .map(|parent_id| {
1549                        self.mergeable_children_from_value(&parent_id, &map)
1550                            .into_iter()
1551                            .map(|(_key, cid)| cid)
1552                            .collect()
1553                    })
1554                    .unwrap_or_default();
1555                for cid in mergeable_cids {
1556                    self.arena.register_container(&cid);
1557                    ans.push(cid);
1558                }
1559            }
1560            _ => {}
1561        }
1562    }
1563
1564    // Because we need to calculate path based on [DocState], so we cannot extract
1565    // the event recorder to a separate module.
1566    fn diffs_to_event(&mut self, diffs: Vec<InternalDocDiff<'_>>, from: Frontiers) -> DocDiff {
1567        if diffs.is_empty() {
1568            panic!("diffs is empty");
1569        }
1570
1571        let triggered_by = diffs[0].by;
1572        debug_assert!(diffs.iter().all(|x| x.by == triggered_by));
1573        let mut containers = FxHashMap::default();
1574        let to = (*diffs.last().unwrap().new_version).to_owned();
1575        let origin = diffs[0].origin.clone();
1576        for diff in diffs {
1577            #[allow(clippy::unnecessary_to_owned)]
1578            for container_diff in diff.diff.into_owned() {
1579                let Some((last_container_diff, _)) = containers.get_mut(&container_diff.idx) else {
1580                    if let Some(path) = self.get_path(container_diff.idx) {
1581                        containers.insert(container_diff.idx, (container_diff.diff, path));
1582                    } else {
1583                        // if we cannot find the path to the container, the container must be overwritten afterwards.
1584                        // So we can ignore the diff from it.
1585                        let _container_id = self
1586                            .arena
1587                            .idx_to_id(container_diff.idx)
1588                            .map(|x| x.to_string())
1589                            .unwrap_or_else(|| "unknown".to_string());
1590                        #[cfg(feature = "logging")]
1591                        loro_common::warn!(
1592                            "⚠️ WARNING: ignore event because cannot find its path {:#?} container id:{}",
1593                            &container_diff,
1594                            _container_id
1595                        );
1596                    }
1597
1598                    continue;
1599                };
1600                // Compose in place. Cloning the accumulated diff here made a
1601                // batch of N same-container fragments O(N^2) (each compose
1602                // cloned the growing accumulator), which is hit whenever a
1603                // subscriber is attached and many edits land on one container
1604                // in a single event batch.
1605                let prev = std::mem::take(last_container_diff);
1606                *last_container_diff = prev.compose(container_diff.diff).unwrap();
1607            }
1608        }
1609        let mut diff: Vec<_> = containers
1610            .into_iter()
1611            .map(|(container, (diff, path))| {
1612                let idx = container;
1613                let id = self.arena.get_container_id(idx).unwrap();
1614                let is_unknown = id.is_unknown();
1615
1616                ContainerDiff {
1617                    id,
1618                    idx,
1619                    diff: diff.into_external().unwrap(),
1620                    is_unknown,
1621                    path,
1622                }
1623            })
1624            .collect();
1625
1626        // Sort by path length, so caller can apply the diff from the root to the leaf.
1627        // Otherwise, the caller may use a wrong path to apply the diff.
1628
1629        diff.sort_by_key(|x| {
1630            (
1631                x.path.len(),
1632                match &x.id {
1633                    ContainerID::Root { .. } => 0,
1634                    ContainerID::Normal { counter, .. } => *counter + 1,
1635                },
1636            )
1637        });
1638        DocDiff {
1639            from,
1640            to,
1641            origin,
1642            by: triggered_by,
1643            diff,
1644        }
1645    }
1646
1647    pub(crate) fn get_reachable(&mut self, id: &ContainerID) -> bool {
1648        if id.is_root() && !id.is_mergeable() {
1649            return true;
1650        }
1651
1652        // If not registered yet, check KV presence for ordinary containers, then register lazily.
1653        // Mergeable children can be active through a parent marker before they have their
1654        // own KV state, so their reachability must be resolved by the logical edge walk below.
1655        if self.arena.id_to_idx(id).is_none() {
1656            if !id.is_mergeable() && !self.does_container_exist(id) {
1657                return false;
1658            }
1659            self.arena.register_container(id);
1660        }
1661
1662        let mut idx = self.arena.id_to_idx(id).unwrap();
1663        loop {
1664            let id = self.arena.idx_to_id(idx).unwrap();
1665            if let Some(parent_idx) = self.arena.get_parent(idx) {
1666                if !self.contains_logical_child(parent_idx, &id) {
1667                    return false;
1668                }
1669                idx = parent_idx;
1670            } else {
1671                if id.is_root() && !id.is_mergeable() {
1672                    return true;
1673                }
1674
1675                return false;
1676            }
1677        }
1678    }
1679
1680    // the container may be override, so it may return None
1681    pub(super) fn get_path(&mut self, idx: ContainerIdx) -> Option<Vec<(ContainerID, Index)>> {
1682        let mut ans = Vec::new();
1683        let mut idx = idx;
1684        loop {
1685            let id = self.arena.idx_to_id(idx).unwrap();
1686            if let Some(parent_idx) = self.arena.get_parent(idx) {
1687                let Some(prop) = self.get_logical_child_index(parent_idx, &id) else {
1688                    tracing::warn!("Missing in parent's children");
1689                    return None;
1690                };
1691                ans.push((id, prop));
1692                idx = parent_idx;
1693            } else {
1694                // this container may be deleted
1695                if id.is_mergeable() {
1696                    tracing::info!(id = %id, "Missing parent - mergeable container is inactive");
1697                    return None;
1698                }
1699                let Ok(prop) = id.clone().into_root() else {
1700                    let id = format!("{}", &id);
1701                    tracing::info!(?id, "Missing parent - container is deleted");
1702                    return None;
1703                };
1704                ans.push((id, Index::Key(prop.0)));
1705                break;
1706            }
1707        }
1708
1709        ans.reverse();
1710
1711        Some(ans)
1712    }
1713
1714    pub(crate) fn check_before_decode_snapshot(&self) -> LoroResult<()> {
1715        if self.is_in_txn() {
1716            return Err(LoroError::DecodeError(
1717                "State is in txn".to_string().into_boxed_str(),
1718            ));
1719        }
1720
1721        if !self.can_import_snapshot() {
1722            return Err(LoroError::DecodeError(
1723                "State is not empty, cannot import snapshot directly"
1724                    .to_string()
1725                    .into_boxed_str(),
1726            ));
1727        }
1728
1729        Ok(())
1730    }
1731
1732    /// Check whether two [DocState]s are the same. Panic if not.
1733    ///
1734    /// Compared to check equality on `get_deep_value`, this function also checks the equality on richtext
1735    /// styles and states that are not reachable from the root.
1736    ///
1737    /// This is only used for test.
1738    pub(crate) fn check_is_the_same(&mut self, other: &mut Self) {
1739        fn get_entries_for_state(
1740            arena: &SharedArena,
1741            state: &mut State,
1742        ) -> Option<(ContainerID, (ContainerIdx, LoroValue))> {
1743            if state.is_state_empty() {
1744                return None;
1745            }
1746
1747            let id = arena.idx_to_id(state.container_idx()).unwrap();
1748            let value = match state {
1749                State::RichtextState(s) => s.get_richtext_value(),
1750                _ => state.get_value(),
1751            };
1752            if match &value {
1753                LoroValue::List(l) => l.is_empty(),
1754                LoroValue::Map(m) => m.is_empty(),
1755                _ => false,
1756            } {
1757                return None;
1758            }
1759            #[cfg(feature = "counter")]
1760            if id.container_type() == ContainerType::Counter {
1761                if let LoroValue::Double(c) = value {
1762                    if c.abs() < f64::EPSILON {
1763                        return None;
1764                    }
1765                }
1766            }
1767
1768            Some((id, (state.container_idx(), value)))
1769        }
1770
1771        let self_id_to_states: FxHashMap<ContainerID, (ContainerIdx, LoroValue)> = self
1772            .store
1773            .iter_and_decode_all()
1774            .filter_map(|state: &mut State| {
1775                let arena = &self.arena;
1776                get_entries_for_state(arena, state)
1777            })
1778            .collect();
1779        let mut other_id_to_states: FxHashMap<ContainerID, (ContainerIdx, LoroValue)> = other
1780            .store
1781            .iter_and_decode_all()
1782            .filter_map(|state: &mut State| {
1783                let arena = &other.arena;
1784                get_entries_for_state(arena, state)
1785            })
1786            .collect();
1787        for (id, (idx, this_value)) in self_id_to_states {
1788            let (_, other_value) = match other_id_to_states.remove(&id) {
1789                Some(x) => x,
1790                None => {
1791                    panic!(
1792                        "id: {:?}, path: {:?} is missing, value={:?}",
1793                        id,
1794                        self.get_path(idx),
1795                        &this_value
1796                    );
1797                }
1798            };
1799
1800            pretty_assertions::assert_eq!(
1801                this_value,
1802                other_value,
1803                "[self!=other] id: {:?}, path: {:?}",
1804                id,
1805                self.get_path(idx)
1806            );
1807        }
1808
1809        if !other_id_to_states.is_empty() {
1810            panic!("other has more states {:#?}", &other_id_to_states);
1811        }
1812    }
1813
1814    pub fn create_state(&self, idx: ContainerIdx) -> State {
1815        let config = &self.config;
1816        let peer = self.peer.load(std::sync::atomic::Ordering::Relaxed);
1817        create_state_(idx, config, peer)
1818    }
1819
1820    pub fn create_unknown_state(&self, idx: ContainerIdx) -> State {
1821        State::UnknownState(UnknownState::new(idx))
1822    }
1823
1824    pub fn get_relative_position(&mut self, pos: &Cursor, use_event_index: bool) -> Option<usize> {
1825        let idx = self.arena.register_container(&pos.container);
1826        let state = self.store.get_container_mut(idx)?;
1827        if let Some(id) = pos.id {
1828            match state {
1829                State::ListState(s) => s.get_index_of_id(id),
1830                State::RichtextState(s) => s.get_text_index_of_id(id, use_event_index),
1831                State::MovableListState(s) => s.get_index_of_id(id),
1832                State::MapState(_) | State::TreeState(_) | State::UnknownState(_) => unreachable!(),
1833                #[cfg(feature = "counter")]
1834                State::CounterState(_) => unreachable!(),
1835            }
1836        } else {
1837            if matches!(pos.side, crate::cursor::Side::Left) {
1838                return Some(0);
1839            }
1840
1841            match state {
1842                State::ListState(s) => Some(s.len()),
1843                State::RichtextState(s) => Some(if use_event_index {
1844                    s.len_event()
1845                } else {
1846                    s.len_unicode()
1847                }),
1848                State::MovableListState(s) => Some(s.len()),
1849                State::MapState(_) | State::TreeState(_) | State::UnknownState(_) => unreachable!(),
1850                #[cfg(feature = "counter")]
1851                State::CounterState(_) => unreachable!(),
1852            }
1853        }
1854    }
1855
1856    pub fn get_value_by_path(&mut self, path: &[Index]) -> Option<LoroValue> {
1857        if path.is_empty() {
1858            return None;
1859        }
1860
1861        enum CurContainer {
1862            Container(ContainerIdx),
1863            TreeNode {
1864                tree: ContainerIdx,
1865                node: Option<TreeID>,
1866            },
1867        }
1868
1869        let mut state_idx = {
1870            let root_index = path[0].as_key()?;
1871            CurContainer::Container(self.preferred_root_container_idx_by_key(root_index)?)
1872        };
1873
1874        if path.len() == 1 {
1875            if let CurContainer::Container(c) = state_idx {
1876                let cid = self.arena.idx_to_id(c)?;
1877                return Some(LoroValue::Container(cid));
1878            }
1879        }
1880
1881        let mut i = 1;
1882        while i < path.len() - 1 {
1883            let index = &path[i];
1884            match state_idx {
1885                CurContainer::Container(idx) => {
1886                    let parent_id = self.arena.idx_to_id(idx);
1887                    let parent_state = self.store.get_container_mut(idx)?;
1888                    match parent_state {
1889                        State::ListState(l) => {
1890                            let Some(LoroValue::Container(c)) = l.get(*index.as_seq()?) else {
1891                                return None;
1892                            };
1893                            state_idx = CurContainer::Container(self.arena.register_container(c));
1894                        }
1895                        State::MovableListState(l) => {
1896                            let Some(LoroValue::Container(c)) =
1897                                l.get(*index.as_seq()?, IndexType::ForUser)
1898                            else {
1899                                return None;
1900                            };
1901                            state_idx = CurContainer::Container(self.arena.register_container(c));
1902                        }
1903                        State::MapState(m) => {
1904                            let key = index.as_key()?;
1905                            let value = m.get(key)?;
1906                            let c = match value {
1907                                LoroValue::Container(c) => c.clone(),
1908                                value => {
1909                                    let parent_id = parent_id?;
1910                                    let kind = loro_common::parse_mergeable_marker(
1911                                        &parent_id, key, value,
1912                                    )?;
1913                                    ContainerID::new_mergeable(&parent_id, key, kind)
1914                                }
1915                            };
1916                            state_idx = CurContainer::Container(self.arena.register_container(&c));
1917                        }
1918                        State::RichtextState(_) => return None,
1919                        State::TreeState(_) => {
1920                            state_idx = CurContainer::TreeNode {
1921                                tree: idx,
1922                                node: None,
1923                            };
1924                            continue;
1925                        }
1926                        #[cfg(feature = "counter")]
1927                        State::CounterState(_) => return None,
1928                        State::UnknownState(_) => unreachable!(),
1929                    }
1930                }
1931                CurContainer::TreeNode { tree, node } => match index {
1932                    Index::Key(internal_string) => {
1933                        let node = node?;
1934                        let idx = self
1935                            .arena
1936                            .register_container(&node.associated_meta_container());
1937                        let map = self.store.get_container(idx)?;
1938                        let Some(LoroValue::Container(c)) =
1939                            map.as_map_state().unwrap().get(internal_string)
1940                        else {
1941                            return None;
1942                        };
1943
1944                        state_idx = CurContainer::Container(self.arena.register_container(c));
1945                    }
1946                    Index::Seq(i) => {
1947                        let tree_state =
1948                            self.store.get_container_mut(tree)?.as_tree_state().unwrap();
1949                        let parent: TreeParentId = if let Some(node) = node {
1950                            node.into()
1951                        } else {
1952                            TreeParentId::Root
1953                        };
1954                        let child = tree_state.get_children(&parent)?.nth(*i)?;
1955                        state_idx = CurContainer::TreeNode {
1956                            tree,
1957                            node: Some(child),
1958                        };
1959                    }
1960                    Index::Node(tree_id) => {
1961                        let tree_state =
1962                            self.store.get_container_mut(tree)?.as_tree_state().unwrap();
1963                        if tree_state.parent(tree_id).is_some() {
1964                            state_idx = CurContainer::TreeNode {
1965                                tree,
1966                                node: Some(*tree_id),
1967                            }
1968                        } else {
1969                            return None;
1970                        }
1971                    }
1972                },
1973            }
1974            i += 1;
1975        }
1976
1977        let parent_idx = match state_idx {
1978            CurContainer::Container(container_idx) => container_idx,
1979            CurContainer::TreeNode { tree, node } => {
1980                if let Some(node) = node {
1981                    self.arena
1982                        .register_container(&node.associated_meta_container())
1983                } else {
1984                    tree
1985                }
1986            }
1987        };
1988
1989        let index = path.last().unwrap();
1990        let parent_id = self.arena.idx_to_id(parent_idx);
1991        let parent_state = self.store.get_or_create_mut(parent_idx);
1992        let value: LoroValue = match parent_state {
1993            State::ListState(l) => l.get(*index.as_seq()?).cloned()?,
1994            State::MovableListState(l) => l.get(*index.as_seq()?, IndexType::ForUser).cloned()?,
1995            State::MapState(m) => {
1996                if let Some(key) = index.as_key() {
1997                    let value = m.get(key).cloned()?;
1998                    if let Some(parent_id) = &parent_id {
1999                        if let Some(kind) =
2000                            loro_common::parse_mergeable_marker(parent_id, key, &value)
2001                        {
2002                            let cid = ContainerID::new_mergeable(parent_id, key, kind);
2003                            LoroValue::Container(cid)
2004                        } else {
2005                            value
2006                        }
2007                    } else {
2008                        value
2009                    }
2010                } else if let CurContainer::TreeNode { tree, node } = state_idx {
2011                    match index {
2012                        Index::Seq(index) => {
2013                            let tree_state =
2014                                self.store.get_container_mut(tree)?.as_tree_state().unwrap();
2015                            let parent: TreeParentId = if let Some(node) = node {
2016                                node.into()
2017                            } else {
2018                                TreeParentId::Root
2019                            };
2020                            let child = tree_state.get_children(&parent)?.nth(*index)?;
2021                            child.associated_meta_container().into()
2022                        }
2023                        Index::Node(id) => id.associated_meta_container().into(),
2024                        _ => return None,
2025                    }
2026                } else {
2027                    return None;
2028                }
2029            }
2030            State::RichtextState(s) => {
2031                let s = s.to_string_mut();
2032                s.chars()
2033                    .nth(*index.as_seq()?)
2034                    .map(|c| c.to_string().into())?
2035            }
2036            State::TreeState(_) => {
2037                let id = index.as_node()?;
2038                let cid = id.associated_meta_container();
2039                cid.into()
2040            }
2041            #[cfg(feature = "counter")]
2042            State::CounterState(_) => unreachable!(),
2043            State::UnknownState(_) => unreachable!(),
2044        };
2045
2046        Some(value)
2047    }
2048
2049    pub(crate) fn shallow_root_store(&self) -> Option<&Arc<GcStore>> {
2050        self.store.shallow_root_store()
2051    }
2052}
2053
2054fn create_state_(idx: ContainerIdx, config: &Configure, peer: u64) -> State {
2055    match idx.get_type() {
2056        ContainerType::Map => State::MapState(Box::new(MapState::new(idx))),
2057        ContainerType::List => State::ListState(Box::new(ListState::new(idx))),
2058        ContainerType::Text => State::RichtextState(Box::new(RichtextState::new(
2059            idx,
2060            config.text_style_config.clone(),
2061        ))),
2062        ContainerType::Tree => State::TreeState(Box::new(TreeState::new(idx, peer))),
2063        ContainerType::MovableList => State::MovableListState(Box::new(MovableListState::new(idx))),
2064        #[cfg(feature = "counter")]
2065        ContainerType::Counter => {
2066            State::CounterState(Box::new(counter_state::CounterState::new(idx)))
2067        }
2068        ContainerType::Unknown(_) => State::UnknownState(UnknownState::new(idx)),
2069    }
2070}
2071
2072fn trigger_on_new_container(
2073    state_diff: &Diff,
2074    mut listener: impl FnMut(ContainerIdx),
2075    arena: &SharedArena,
2076) {
2077    match state_diff {
2078        Diff::List(list) => {
2079            for delta in list.iter() {
2080                if let DeltaItem::Replace {
2081                    value,
2082                    attr,
2083                    delete: _,
2084                } = delta
2085                {
2086                    if attr.from_move {
2087                        continue;
2088                    }
2089
2090                    for v in value.iter() {
2091                        if let ValueOrHandler::Handler(h) = v {
2092                            let idx = h.container_idx();
2093                            listener(idx);
2094                        }
2095                    }
2096                }
2097            }
2098        }
2099        Diff::Map(map) => {
2100            for (_, v) in map.updated.iter() {
2101                if let Some(ValueOrHandler::Handler(h)) = &v.value {
2102                    let idx = h.container_idx();
2103                    listener(idx);
2104                }
2105            }
2106        }
2107        Diff::Tree(tree) => {
2108            for item in tree.iter() {
2109                if matches!(item.action, TreeExternalDiff::Create { .. }) {
2110                    let id = item.target.associated_meta_container();
2111                    // Ensure registration instead of assuming it's already in arena
2112                    listener(arena.register_container(&id));
2113                }
2114            }
2115        }
2116        _ => {}
2117    };
2118}
2119
2120#[derive(Default, Clone)]
2121struct EventRecorder {
2122    recording_diff: bool,
2123    // A batch of diffs will be converted to a event when
2124    // they cannot be merged with the next diff.
2125    diffs: Vec<InternalDocDiff<'static>>,
2126    events: Vec<DocDiff>,
2127    diff_start_version: Option<Frontiers>,
2128}
2129
2130impl EventRecorder {
2131    #[allow(unused)]
2132    pub fn new() -> Self {
2133        Self::default()
2134    }
2135}
2136
2137#[test]
2138fn test_size() {
2139    println!("Size of State = {}", std::mem::size_of::<State>());
2140    println!("Size of MapState = {}", std::mem::size_of::<MapState>());
2141    println!("Size of ListState = {}", std::mem::size_of::<ListState>());
2142    println!(
2143        "Size of TextState = {}",
2144        std::mem::size_of::<RichtextState>()
2145    );
2146    println!("Size of TreeState = {}", std::mem::size_of::<TreeState>());
2147}