Skip to main content

loro_internal/
state.rs

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