loro_internal/
arena.rs

1mod str_arena;
2use self::str_arena::StrArena;
3use crate::sync::{Mutex, MutexGuard};
4use crate::{
5    change::Lamport,
6    container::{
7        idx::ContainerIdx,
8        list::list_op::{InnerListOp, ListOp},
9        map::MapSet,
10        ContainerID,
11    },
12    id::Counter,
13    op::{InnerContent, ListSlice, Op, RawOp, RawOpContent, SliceRange},
14    LoroValue,
15};
16use append_only_bytes::BytesSlice;
17use rustc_hash::FxHashMap;
18use loro_common::PeerID;
19use std::fmt;
20use std::{
21    num::NonZeroU16,
22    ops::{Range, RangeBounds},
23    sync::Arc,
24};
25
26pub(crate) struct LoadAllFlag;
27type ParentResolver = dyn Fn(ContainerID) -> Option<ContainerID> + Send + Sync + 'static;
28
29#[derive(Default)]
30struct InnerSharedArena {
31    // The locks should not be exposed outside this file.
32    // It might be better to use RwLock in the future
33    container_idx_to_id: Mutex<Vec<ContainerID>>,
34    // 0 stands for unknown, 1 stands for root containers
35    depth: Mutex<Vec<Option<NonZeroU16>>>,
36    container_id_to_idx: Mutex<FxHashMap<ContainerID, ContainerIdx>>,
37    /// The parent of each container.
38    parents: Mutex<FxHashMap<ContainerIdx, Option<ContainerIdx>>>,
39    values: Mutex<Vec<LoroValue>>,
40    root_c_idx: Mutex<Vec<ContainerIdx>>,
41    str: Arc<Mutex<StrArena>>,
42    /// Optional resolver used when querying parent for a container that has not been registered yet.
43    /// If set, `get_parent` will try this resolver to lazily fetch and register the parent.
44    parent_resolver: Mutex<Option<Arc<ParentResolver>>>,
45}
46
47impl fmt::Debug for InnerSharedArena {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.debug_struct("InnerSharedArena")
50            .field("container_idx_to_id", &"<Mutex<_>>")
51            .field("depth", &"<Mutex<_>>")
52            .field("container_id_to_idx", &"<Mutex<_>>")
53            .field("parents", &"<Mutex<_>>")
54            .field("values", &"<Mutex<_>>")
55            .field("root_c_idx", &"<Mutex<_>>")
56            .field("str", &"<Arc<Mutex<_>>>")
57            .field("parent_resolver", &"<Mutex<Option<...>>>")
58            .finish()
59    }
60}
61
62/// This is shared between [OpLog] and [AppState].
63///
64#[derive(Debug, Clone)]
65pub struct SharedArena {
66    inner: Arc<InnerSharedArena>,
67}
68
69#[derive(Debug)]
70pub struct StrAllocResult {
71    /// unicode start
72    pub start: usize,
73    /// unicode end
74    pub end: usize,
75}
76
77pub(crate) struct ArenaGuards<'a> {
78    container_id_to_idx: MutexGuard<'a, FxHashMap<ContainerID, ContainerIdx>>,
79    container_idx_to_id: MutexGuard<'a, Vec<ContainerID>>,
80    depth: MutexGuard<'a, Vec<Option<NonZeroU16>>>,
81    parents: MutexGuard<'a, FxHashMap<ContainerIdx, Option<ContainerIdx>>>,
82    root_c_idx: MutexGuard<'a, Vec<ContainerIdx>>,
83    parent_resolver: MutexGuard<'a, Option<Arc<ParentResolver>>>,
84}
85
86impl ArenaGuards<'_> {
87    pub fn register_container(&mut self, id: &ContainerID) -> ContainerIdx {
88        if let Some(&idx) = self.container_id_to_idx.get(id) {
89            return idx;
90        }
91
92        let idx = self.container_idx_to_id.len();
93        self.container_idx_to_id.push(id.clone());
94        let idx = ContainerIdx::from_index_and_type(idx as u32, id.container_type());
95        self.container_id_to_idx.insert(id.clone(), idx);
96        if id.is_root() {
97            self.root_c_idx.push(idx);
98            self.parents.insert(idx, None);
99            self.depth.push(NonZeroU16::new(1));
100        } else {
101            self.depth.push(None);
102        }
103        idx
104    }
105
106    pub fn set_parent(&mut self, child: ContainerIdx, parent: Option<ContainerIdx>) {
107        self.parents.insert(child, parent);
108
109        match parent {
110            Some(p) => {
111                if let Some(d) = get_depth(
112                    p,
113                    &mut self.depth,
114                    &self.parents,
115                    &self.parent_resolver,
116                    &mut self.container_idx_to_id,
117                    &mut self.container_id_to_idx,
118                ) {
119                    self.depth[child.to_index() as usize] = NonZeroU16::new(d.get() + 1);
120                } else {
121                    self.depth[child.to_index() as usize] = None;
122                }
123            }
124            None => {
125                self.depth[child.to_index() as usize] = NonZeroU16::new(1);
126            }
127        }
128    }
129}
130
131impl SharedArena {
132    #[allow(clippy::new_without_default)]
133    pub fn new() -> Self {
134        Self {
135            inner: Arc::new(InnerSharedArena::default()),
136        }
137    }
138
139    pub fn fork(&self) -> Self {
140        Self {
141            inner: Arc::new(InnerSharedArena {
142                container_idx_to_id: Mutex::new(
143                    self.inner.container_idx_to_id.lock().unwrap().clone(),
144                ),
145                depth: Mutex::new(self.inner.depth.lock().unwrap().clone()),
146                container_id_to_idx: Mutex::new(
147                    self.inner.container_id_to_idx.lock().unwrap().clone(),
148                ),
149                parents: Mutex::new(self.inner.parents.lock().unwrap().clone()),
150                values: Mutex::new(self.inner.values.lock().unwrap().clone()),
151                root_c_idx: Mutex::new(self.inner.root_c_idx.lock().unwrap().clone()),
152                str: self.inner.str.clone(),
153                parent_resolver: Mutex::new(self.inner.parent_resolver.lock().unwrap().clone()),
154            }),
155        }
156    }
157
158    pub(crate) fn with_guards(&self, f: impl FnOnce(&mut ArenaGuards)) {
159        let mut guards = self.get_arena_guards();
160        f(&mut guards);
161    }
162
163    fn get_arena_guards(&self) -> ArenaGuards<'_> {
164        ArenaGuards {
165            container_id_to_idx: self.inner.container_id_to_idx.lock().unwrap(),
166            container_idx_to_id: self.inner.container_idx_to_id.lock().unwrap(),
167            depth: self.inner.depth.lock().unwrap(),
168            parents: self.inner.parents.lock().unwrap(),
169            root_c_idx: self.inner.root_c_idx.lock().unwrap(),
170            parent_resolver: self.inner.parent_resolver.lock().unwrap(),
171        }
172    }
173
174    pub fn register_container(&self, id: &ContainerID) -> ContainerIdx {
175        let mut container_id_to_idx = self.inner.container_id_to_idx.lock().unwrap();
176        if let Some(&idx) = container_id_to_idx.get(id) {
177            return idx;
178        }
179
180        let mut container_idx_to_id = self.inner.container_idx_to_id.lock().unwrap();
181        let idx = container_idx_to_id.len();
182        container_idx_to_id.push(id.clone());
183        let idx = ContainerIdx::from_index_and_type(idx as u32, id.container_type());
184        container_id_to_idx.insert(id.clone(), idx);
185        if id.is_root() {
186            self.inner.root_c_idx.lock().unwrap().push(idx);
187            self.inner.parents.lock().unwrap().insert(idx, None);
188            self.inner.depth.lock().unwrap().push(NonZeroU16::new(1));
189        } else {
190            self.inner.depth.lock().unwrap().push(None);
191        }
192        idx
193    }
194
195    pub fn get_container_id(&self, idx: ContainerIdx) -> Option<ContainerID> {
196        let lock = self.inner.container_idx_to_id.lock().unwrap();
197        lock.get(idx.to_index() as usize).cloned()
198    }
199
200    /// Fast map from `ContainerID` to `ContainerIdx` for containers already registered
201    /// in the arena.
202    ///
203    /// Important: This is not an existence check. Absence here does not imply that a
204    /// container does not exist, since registration can be lazy and containers may
205    /// be persisted only in the state KV store until first use.
206    ///
207    /// For existence-aware lookup that consults persisted state and performs lazy
208    /// registration, prefer `DocState::resolve_idx`.
209    pub fn id_to_idx(&self, id: &ContainerID) -> Option<ContainerIdx> {
210        self.inner
211            .container_id_to_idx
212            .lock()
213            .unwrap()
214            .get(id)
215            .copied()
216    }
217
218    #[inline]
219    pub fn idx_to_id(&self, id: ContainerIdx) -> Option<ContainerID> {
220        let lock = self.inner.container_idx_to_id.lock().unwrap();
221        lock.get(id.to_index() as usize).cloned()
222    }
223
224    #[inline]
225    pub fn with_idx_to_id<R>(&self, f: impl FnOnce(&Vec<ContainerID>) -> R) -> R {
226        let lock = self.inner.container_idx_to_id.lock().unwrap();
227        f(&lock)
228    }
229
230    pub fn alloc_str(&self, str: &str) -> StrAllocResult {
231        let mut text_lock = self.inner.str.lock().unwrap();
232        _alloc_str(&mut text_lock, str)
233    }
234
235    /// return slice and unicode index
236    pub fn alloc_str_with_slice(&self, str: &str) -> (BytesSlice, StrAllocResult) {
237        let mut text_lock = self.inner.str.lock().unwrap();
238        _alloc_str_with_slice(&mut text_lock, str)
239    }
240
241    /// alloc str without extra info
242    pub fn alloc_str_fast(&self, bytes: &[u8]) {
243        let mut text_lock = self.inner.str.lock().unwrap();
244        text_lock.alloc(std::str::from_utf8(bytes).unwrap());
245    }
246
247    #[inline]
248    pub fn utf16_len(&self) -> usize {
249        self.inner.str.lock().unwrap().len_utf16()
250    }
251
252    #[inline]
253    pub fn alloc_value(&self, value: LoroValue) -> usize {
254        let mut values_lock = self.inner.values.lock().unwrap();
255        _alloc_value(&mut values_lock, value)
256    }
257
258    #[inline]
259    pub fn alloc_values(&self, values: impl Iterator<Item = LoroValue>) -> std::ops::Range<usize> {
260        let mut values_lock = self.inner.values.lock().unwrap();
261        _alloc_values(&mut values_lock, values)
262    }
263
264    #[inline]
265    pub fn set_parent(&self, child: ContainerIdx, parent: Option<ContainerIdx>) {
266        let parents = &mut self.inner.parents.lock().unwrap();
267        parents.insert(child, parent);
268        let mut depth = self.inner.depth.lock().unwrap();
269
270        match parent {
271            Some(p) => {
272                // Acquire the two maps as mutable guards so we can lazily register
273                // unknown parents while computing depth.
274                let mut idx_to_id_guard = self.inner.container_idx_to_id.lock().unwrap();
275                let mut id_to_idx_guard = self.inner.container_id_to_idx.lock().unwrap();
276                if let Some(d) = get_depth(
277                    p,
278                    &mut depth,
279                    parents,
280                    &self.inner.parent_resolver.lock().unwrap(),
281                    &mut idx_to_id_guard,
282                    &mut id_to_idx_guard,
283                ) {
284                    depth[child.to_index() as usize] = NonZeroU16::new(d.get() + 1);
285                } else {
286                    depth[child.to_index() as usize] = None;
287                }
288            }
289            None => {
290                depth[child.to_index() as usize] = NonZeroU16::new(1);
291            }
292        }
293    }
294
295    pub fn log_hierarchy(&self) {
296        if cfg!(debug_assertions) {
297            for (c, p) in self.inner.parents.lock().unwrap().iter() {
298                tracing::info!(
299                    "container {:?} {:?} {:?}",
300                    c,
301                    self.get_container_id(*c),
302                    p.map(|x| self.get_container_id(x))
303                );
304            }
305        }
306    }
307
308    pub fn log_all_containers(&self) {
309        self.inner
310            .container_id_to_idx
311            .lock()
312            .unwrap()
313            .iter()
314            .for_each(|(id, idx)| {
315                tracing::info!("container {:?} {:?}", id, idx);
316            });
317        self.inner
318            .container_idx_to_id
319            .lock()
320            .unwrap()
321            .iter()
322            .enumerate()
323            .for_each(|(i, id)| {
324                tracing::info!("container {} {:?}", i, id);
325            });
326    }
327
328    pub fn get_parent(&self, child: ContainerIdx) -> Option<ContainerIdx> {
329        if self.get_container_id(child).unwrap().is_root() {
330            // TODO: PERF: we can speed this up by use a special bit in ContainerIdx to indicate
331            // whether the target is a root container
332            return None;
333        }
334
335        // Try fast path first
336        if let Some(p) = self.inner.parents.lock().unwrap().get(&child).copied() {
337            return p;
338        }
339
340        // Fallback: try to resolve parent lazily via the resolver if provided.
341        let resolver = self.inner.parent_resolver.lock().unwrap().clone();
342        if let Some(resolver) = resolver {
343            let child_id = self.get_container_id(child).unwrap();
344            if let Some(parent_id) = resolver(child_id.clone()) {
345                let parent_idx = self.register_container(&parent_id);
346                self.set_parent(child, Some(parent_idx));
347                return Some(parent_idx);
348            }
349        }
350
351        panic!("InternalError: Parent is not registered")
352    }
353
354    /// Call `f` on each ancestor of `container`, including `container` itself.
355    ///
356    /// f(ContainerIdx, is_first)
357    pub fn with_ancestors(&self, container: ContainerIdx, mut f: impl FnMut(ContainerIdx, bool)) {
358        let mut container = Some(container);
359        let mut is_first = true;
360        while let Some(c) = container {
361            f(c, is_first);
362            is_first = false;
363            container = self.get_parent(c)
364        }
365    }
366
367    #[inline]
368    pub fn slice_by_unicode(&self, range: impl RangeBounds<usize>) -> BytesSlice {
369        self.inner.str.lock().unwrap().slice_by_unicode(range)
370    }
371
372    #[inline]
373    pub fn slice_by_utf8(&self, range: impl RangeBounds<usize>) -> BytesSlice {
374        self.inner.str.lock().unwrap().slice_bytes(range)
375    }
376
377    #[inline]
378    pub fn slice_str_by_unicode_range(&self, range: Range<usize>) -> String {
379        let mut s = self.inner.str.lock().unwrap();
380        let s: &mut StrArena = &mut s;
381        let mut ans = String::with_capacity(range.len());
382        ans.push_str(s.slice_str_by_unicode(range));
383        ans
384    }
385
386    #[inline]
387    pub fn with_text_slice(&self, range: Range<usize>, mut f: impl FnMut(&str)) {
388        f(self.inner.str.lock().unwrap().slice_str_by_unicode(range))
389    }
390
391    #[inline]
392    pub fn get_value(&self, idx: usize) -> Option<LoroValue> {
393        self.inner.values.lock().unwrap().get(idx).cloned()
394    }
395
396    #[inline]
397    pub fn get_values(&self, range: Range<usize>) -> Vec<LoroValue> {
398        (self.inner.values.lock().unwrap()[range]).to_vec()
399    }
400
401    pub fn convert_single_op(
402        &self,
403        container: &ContainerID,
404        peer: PeerID,
405        counter: Counter,
406        lamport: Lamport,
407        content: RawOpContent,
408    ) -> Op {
409        let container = self.register_container(container);
410        self.inner_convert_op(content, peer, counter, lamport, container)
411    }
412
413    pub fn can_import_snapshot(&self) -> bool {
414        self.inner.str.lock().unwrap().is_empty() && self.inner.values.lock().unwrap().is_empty()
415    }
416
417    fn inner_convert_op(
418        &self,
419        content: RawOpContent<'_>,
420        _peer: PeerID,
421        counter: i32,
422        _lamport: Lamport,
423        container: ContainerIdx,
424    ) -> Op {
425        match content {
426            crate::op::RawOpContent::Map(MapSet { key, value }) => Op {
427                counter,
428                container,
429                content: crate::op::InnerContent::Map(MapSet { key, value }),
430            },
431            crate::op::RawOpContent::List(list) => match list {
432                ListOp::Insert { slice, pos } => match slice {
433                    ListSlice::RawData(values) => {
434                        let range = self.alloc_values(values.iter().cloned());
435                        Op {
436                            counter,
437                            container,
438                            content: crate::op::InnerContent::List(InnerListOp::Insert {
439                                slice: SliceRange::from(range.start as u32..range.end as u32),
440                                pos,
441                            }),
442                        }
443                    }
444                    ListSlice::RawStr { str, unicode_len } => {
445                        let (slice, info) = self.alloc_str_with_slice(&str);
446                        Op {
447                            counter,
448                            container,
449                            content: crate::op::InnerContent::List(InnerListOp::InsertText {
450                                slice,
451                                unicode_start: info.start as u32,
452                                unicode_len: unicode_len as u32,
453                                pos: pos as u32,
454                            }),
455                        }
456                    }
457                },
458                ListOp::Delete(span) => Op {
459                    counter,
460                    container,
461                    content: crate::op::InnerContent::List(InnerListOp::Delete(span)),
462                },
463                ListOp::StyleStart {
464                    start,
465                    end,
466                    info,
467                    key,
468                    value,
469                } => Op {
470                    counter,
471                    container,
472                    content: InnerContent::List(InnerListOp::StyleStart {
473                        start,
474                        end,
475                        key,
476                        info,
477                        value,
478                    }),
479                },
480                ListOp::StyleEnd => Op {
481                    counter,
482                    container,
483                    content: InnerContent::List(InnerListOp::StyleEnd),
484                },
485                ListOp::Move {
486                    from,
487                    to,
488                    elem_id: from_id,
489                } => Op {
490                    counter,
491                    container,
492                    content: InnerContent::List(InnerListOp::Move {
493                        from,
494                        to,
495                        elem_id: from_id,
496                    }),
497                },
498                ListOp::Set { elem_id, value } => Op {
499                    counter,
500                    container,
501                    content: InnerContent::List(InnerListOp::Set { elem_id, value }),
502                },
503            },
504            crate::op::RawOpContent::Tree(tree) => Op {
505                counter,
506                container,
507                content: crate::op::InnerContent::Tree(tree.clone()),
508            },
509            #[cfg(feature = "counter")]
510            crate::op::RawOpContent::Counter(c) => Op {
511                counter,
512                container,
513                content: crate::op::InnerContent::Future(crate::op::FutureInnerContent::Counter(c)),
514            },
515            crate::op::RawOpContent::Unknown { prop, value } => Op {
516                counter,
517                container,
518                content: crate::op::InnerContent::Future(crate::op::FutureInnerContent::Unknown {
519                    prop,
520                    value: Box::new(value),
521                }),
522            },
523        }
524    }
525
526    #[inline]
527    pub fn convert_raw_op(&self, op: &RawOp) -> Op {
528        self.inner_convert_op(
529            op.content.clone(),
530            op.id.peer,
531            op.id.counter,
532            op.lamport,
533            op.container,
534        )
535    }
536
537    #[inline]
538    pub fn export_containers(&self) -> Vec<ContainerID> {
539        self.inner.container_idx_to_id.lock().unwrap().clone()
540    }
541
542    pub fn export_parents(&self) -> Vec<Option<ContainerIdx>> {
543        let parents = self.inner.parents.lock().unwrap();
544        let containers = self.inner.container_idx_to_id.lock().unwrap();
545        containers
546            .iter()
547            .enumerate()
548            .map(|(x, id)| {
549                let idx = ContainerIdx::from_index_and_type(x as u32, id.container_type());
550                let parent_idx = parents.get(&idx)?;
551                *parent_idx
552            })
553            .collect()
554    }
555
556    /// Returns all the possible root containers of the docs
557    ///
558    /// We need to load all the cached kv in DocState before we can ensure all root contains are covered.
559    /// So we need the flag type here.
560    #[inline]
561    pub(crate) fn root_containers(&self, _f: LoadAllFlag) -> Vec<ContainerIdx> {
562        self.inner.root_c_idx.lock().unwrap().clone()
563    }
564
565    // TODO: this can return a u16 directly now, since the depths are always valid
566    pub(crate) fn get_depth(&self, container: ContainerIdx) -> Option<NonZeroU16> {
567        {
568            let mut depth_guard = self.inner.depth.lock().unwrap();
569            let parents_guard = self.inner.parents.lock().unwrap();
570            let resolver_guard = self.inner.parent_resolver.lock().unwrap();
571            let mut idx_to_id_guard = self.inner.container_idx_to_id.lock().unwrap();
572            let mut id_to_idx_guard = self.inner.container_id_to_idx.lock().unwrap();
573            get_depth(
574                container,
575                &mut depth_guard,
576                &parents_guard,
577                &resolver_guard,
578                &mut idx_to_id_guard,
579                &mut id_to_idx_guard,
580            )
581        }
582    }
583
584    pub(crate) fn iter_value_slice(
585        &self,
586        range: Range<usize>,
587    ) -> impl Iterator<Item = LoroValue> + '_ {
588        let values = self.inner.values.lock().unwrap();
589        range
590            .into_iter()
591            .map(move |i| values.get(i).unwrap().clone())
592    }
593
594    pub(crate) fn get_root_container_idx_by_key(
595        &self,
596        root_index: &loro_common::InternalString,
597    ) -> Option<ContainerIdx> {
598        let inner = self.inner.container_id_to_idx.lock().unwrap();
599        for t in loro_common::ContainerType::ALL_TYPES.iter() {
600            let cid = ContainerID::Root {
601                name: root_index.clone(),
602                container_type: *t,
603            };
604            if let Some(idx) = inner.get(&cid) {
605                return Some(*idx);
606            }
607        }
608        None
609    }
610
611    #[allow(unused)]
612    pub(crate) fn log_all_values(&self) {
613        let values = self.inner.values.lock().unwrap();
614        for (i, v) in values.iter().enumerate() {
615            loro_common::debug!("value {} {:?}", i, v);
616        }
617    }
618}
619
620fn _alloc_str_with_slice(
621    text_lock: &mut MutexGuard<'_, StrArena>,
622    str: &str,
623) -> (BytesSlice, StrAllocResult) {
624    let start = text_lock.len_bytes();
625    let ans = _alloc_str(text_lock, str);
626    (text_lock.slice_bytes(start..), ans)
627}
628
629fn _alloc_values(
630    values_lock: &mut MutexGuard<'_, Vec<LoroValue>>,
631    values: impl Iterator<Item = LoroValue>,
632) -> Range<usize> {
633    values_lock.reserve(values.size_hint().0);
634    let start = values_lock.len();
635    for value in values {
636        values_lock.push(value);
637    }
638
639    start..values_lock.len()
640}
641
642fn _alloc_value(values_lock: &mut MutexGuard<'_, Vec<LoroValue>>, value: LoroValue) -> usize {
643    values_lock.push(value);
644    values_lock.len() - 1
645}
646
647fn _alloc_str(text_lock: &mut MutexGuard<'_, StrArena>, str: &str) -> StrAllocResult {
648    let start = text_lock.len_unicode();
649    text_lock.alloc(str);
650    StrAllocResult {
651        start,
652        end: text_lock.len_unicode(),
653    }
654}
655
656fn _slice_str(range: Range<usize>, s: &mut StrArena) -> String {
657    let mut ans = String::with_capacity(range.len());
658    ans.push_str(s.slice_str_by_unicode(range));
659    ans
660}
661
662fn get_depth(
663    target: ContainerIdx,
664    depth: &mut Vec<Option<NonZeroU16>>,
665    parents: &FxHashMap<ContainerIdx, Option<ContainerIdx>>,
666    parent_resolver: &Option<Arc<ParentResolver>>,
667    idx_to_id: &mut Vec<ContainerID>,
668    id_to_idx: &mut FxHashMap<ContainerID, ContainerIdx>,
669) -> Option<NonZeroU16> {
670    let mut d = depth[target.to_index() as usize];
671    if d.is_some() {
672        return d;
673    }
674
675    let parent: Option<ContainerIdx> = if let Some(p) = parents.get(&target) {
676        *p
677    } else {
678        let id = idx_to_id.get(target.to_index() as usize).unwrap();
679        if id.is_root() {
680            None
681        } else if let Some(parent_resolver) = parent_resolver.as_ref() {
682            let parent_id = parent_resolver(id.clone())?;
683            // If the parent is not registered yet, register it lazily instead of unwrapping.
684            let parent_idx = if let Some(idx) = id_to_idx.get(&parent_id).copied() {
685                idx
686            } else {
687                let new_index = idx_to_id.len();
688                idx_to_id.push(parent_id.clone());
689                let new_idx =
690                    ContainerIdx::from_index_and_type(new_index as u32, parent_id.container_type());
691                id_to_idx.insert(parent_id.clone(), new_idx);
692                // Keep depth vector in sync with containers list.
693                if parent_id.is_root() {
694                    depth.push(NonZeroU16::new(1));
695                } else {
696                    depth.push(None);
697                }
698                new_idx
699            };
700            Some(parent_idx)
701        } else {
702            return None;
703        }
704    };
705
706    match parent {
707        Some(p) => {
708            d = NonZeroU16::new(
709                get_depth(p, depth, parents, parent_resolver, idx_to_id, id_to_idx)?.get() + 1,
710            );
711            depth[target.to_index() as usize] = d;
712        }
713        None => {
714            depth[target.to_index() as usize] = NonZeroU16::new(1);
715            d = NonZeroU16::new(1);
716        }
717    }
718
719    d
720}
721
722impl SharedArena {
723    /// Register or clear a resolver to lazily determine a container's parent when missing.
724    ///
725    /// - The resolver receives the child `ContainerIdx` and returns an optional `ContainerID` of its parent.
726    /// - If the resolver returns `Some`, `SharedArena` will register the parent in the arena and link it.
727    /// - If the resolver is `None` or returns `None`, `get_parent` will panic for non-root containers as before.
728    pub fn set_parent_resolver<F>(&self, resolver: Option<F>)
729    where
730        F: Fn(ContainerID) -> Option<ContainerID> + Send + Sync + 'static,
731    {
732        let mut slot = self.inner.parent_resolver.lock().unwrap();
733        *slot = resolver.map(|f| Arc::new(f) as Arc<ParentResolver>);
734    }
735}