Skip to main content

loro_internal/
state.rs

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