cranpose_core/
slot_table.rs

1//! Slot table implementation using a gap-buffer strategy.
2//!
3//! This is the baseline/reference slot storage implementation that provides:
4//! - Gap-based slot reuse during conditional rendering
5//! - Anchor-based positional stability during reorganization
6//! - Efficient group skipping and scope-based recomposition
7//! - Batch anchor rebuilding for large structural changes
8
9// Complex slot state machine logic benefits from explicit nested pattern matching for clarity
10#![allow(clippy::collapsible_match)]
11
12use crate::{
13    slot_storage::{GroupId, SlotStorage, StartGroup, ValueSlotId},
14    AnchorId, Key, NodeId, Owned, ScopeId,
15};
16use std::any::Any;
17use std::cell::Cell;
18
19#[derive(Default)]
20pub struct SlotTable {
21    slots: Vec<Slot>, // FUTURE(no_std): replace Vec with arena-backed slot storage.
22    cursor: usize,
23    group_stack: Vec<GroupFrame>, // FUTURE(no_std): switch to small stack buffer.
24    /// Maps anchor IDs to their current physical positions in the slots array.
25    /// This indirection layer provides positional stability during slot reorganization.
26    anchors: Vec<usize>, // index = anchor_id.0
27    anchors_dirty: bool,
28    /// Counter for allocating unique anchor IDs.
29    next_anchor_id: Cell<usize>,
30    /// Tracks whether the most recent start() reused a gap slot.
31    last_start_was_gap: bool,
32}
33
34enum Slot {
35    Group {
36        key: Key,
37        anchor: AnchorId,
38        len: usize,
39        scope: Option<ScopeId>,
40        has_gap_children: bool,
41    },
42    Value {
43        anchor: AnchorId,
44        data: Box<dyn Any>,
45    },
46    Node {
47        anchor: AnchorId,
48        id: NodeId,
49    },
50    /// Gap: Marks an unused slot that can be reused or compacted.
51    /// This prevents destructive truncation that would destroy sibling components.
52    /// For Groups marked as gaps (e.g., during tab switching), we preserve their
53    /// key, scope, and length so they can be properly matched and reused when reactivated.
54    Gap {
55        anchor: AnchorId,
56        /// If this gap was a Group, preserve its key for reuse matching
57        group_key: Option<Key>,
58        /// If this gap was a Group, preserve its scope ID for state subscription continuity
59        group_scope: Option<ScopeId>,
60        /// If this gap was a Group, preserve its length to restore the full group structure
61        group_len: usize,
62    },
63}
64
65struct GroupFrame {
66    key: Key,
67    start: usize, // Physical position (will be phased out)
68    end: usize,   // Physical position (will be phased out)
69    force_children_recompose: bool,
70}
71
72const INVALID_ANCHOR_POS: usize = usize::MAX;
73
74#[derive(Debug, PartialEq)]
75enum SlotKind {
76    Group,
77    Value,
78    Node,
79    Gap,
80}
81
82impl Slot {
83    fn kind(&self) -> SlotKind {
84        match self {
85            Slot::Group { .. } => SlotKind::Group,
86            Slot::Value { .. } => SlotKind::Value,
87            Slot::Node { .. } => SlotKind::Node,
88            Slot::Gap { .. } => SlotKind::Gap,
89        }
90    }
91
92    /// Get the anchor ID for this slot.
93    fn anchor_id(&self) -> AnchorId {
94        match self {
95            Slot::Group { anchor, .. } => *anchor,
96            Slot::Value { anchor, .. } => *anchor,
97            Slot::Node { anchor, .. } => *anchor,
98            Slot::Gap { anchor, .. } => *anchor,
99        }
100    }
101
102    fn as_value<T: 'static>(&self) -> &T {
103        match self {
104            Slot::Value { data, .. } => data.downcast_ref::<T>().expect("slot value type mismatch"),
105            _ => panic!("slot is not a value"),
106        }
107    }
108
109    fn as_value_mut<T: 'static>(&mut self) -> &mut T {
110        match self {
111            Slot::Value { data, .. } => data.downcast_mut::<T>().expect("slot value type mismatch"),
112            _ => panic!("slot is not a value"),
113        }
114    }
115}
116
117impl Default for Slot {
118    fn default() -> Self {
119        Slot::Group {
120            key: 0,
121            anchor: AnchorId::INVALID,
122            len: 0,
123            scope: None,
124            has_gap_children: false,
125        }
126    }
127}
128
129impl SlotTable {
130    const INITIAL_CAP: usize = 32;
131    const LOCAL_GAP_SCAN: usize = 256; // tune
132
133    pub fn new() -> Self {
134        Self {
135            slots: Vec::new(),
136            cursor: 0,
137            group_stack: Vec::new(),
138            anchors: Vec::new(),
139            anchors_dirty: false,
140            next_anchor_id: Cell::new(1), // Start at 1 (0 is INVALID)
141            last_start_was_gap: false,
142        }
143    }
144
145    pub fn current_group(&self) -> usize {
146        self.group_stack.last().map(|f| f.start).unwrap_or(0)
147    }
148
149    pub fn group_key(&self, index: usize) -> Option<Key> {
150        match self.slots.get(index) {
151            Some(Slot::Group { key, .. }) => Some(*key),
152            Some(Slot::Gap { group_key, .. }) => *group_key,
153            _ => None,
154        }
155    }
156
157    fn ensure_capacity(&mut self) {
158        if self.slots.is_empty() {
159            self.slots.reserve(Self::INITIAL_CAP);
160            self.append_gap_slots(Self::INITIAL_CAP);
161        } else if self.cursor == self.slots.len() {
162            self.grow_slots();
163        }
164    }
165
166    fn force_gap_here(&mut self, cursor: usize) {
167        // we *know* we have capacity (ensure_capacity() already ran)
168        // so just overwrite the slot at cursor with a fresh gap
169        self.slots[cursor] = Slot::Gap {
170            anchor: AnchorId::INVALID,
171            group_key: None,
172            group_scope: None,
173            group_len: 0,
174        };
175    }
176
177    fn find_right_gap_run(&self, from: usize, scan_limit: usize) -> Option<(usize, usize)> {
178        let end = (from + scan_limit).min(self.slots.len());
179        let mut i = from;
180        while i < end {
181            if let Some(Slot::Gap { anchor, .. }) = self.slots.get(i) {
182                if *anchor == AnchorId::INVALID {
183                    let start = i;
184                    let mut len = 1;
185                    while i + len < end {
186                        match self.slots.get(i + len) {
187                            Some(Slot::Gap { anchor, .. }) if *anchor == AnchorId::INVALID => {
188                                len += 1;
189                            }
190                            _ => break,
191                        }
192                    }
193                    return Some((start, len));
194                }
195            }
196            i += 1;
197        }
198        None
199    }
200
201    fn ensure_gap_at_local(&mut self, cursor: usize) {
202        if matches!(self.slots.get(cursor), Some(Slot::Gap { .. })) {
203            return;
204        }
205        self.ensure_capacity();
206
207        if let Some((run_start, run_len)) = self.find_right_gap_run(cursor, Self::LOCAL_GAP_SCAN) {
208            self.shift_group_frames(cursor, run_len as isize);
209            self.shift_anchor_positions_from(cursor, run_len as isize);
210            self.slots[cursor..run_start + run_len].rotate_right(run_len);
211            return;
212        }
213
214        self.force_gap_here(cursor);
215    }
216
217    fn append_gap_slots(&mut self, count: usize) {
218        if count == 0 {
219            return;
220        }
221        for _ in 0..count {
222            self.slots.push(Slot::Gap {
223                anchor: AnchorId::INVALID,
224                group_key: None,
225                group_scope: None,
226                group_len: 0,
227            });
228        }
229    }
230
231    fn grow_slots(&mut self) {
232        let old_len = self.slots.len();
233        let target_len = (old_len.saturating_mul(2)).max(Self::INITIAL_CAP);
234        let additional = target_len.saturating_sub(old_len);
235        if additional == 0 {
236            return;
237        }
238        self.slots.reserve(additional);
239        self.append_gap_slots(additional);
240    }
241
242    /// Allocate a new unique anchor ID.
243    fn allocate_anchor(&self) -> AnchorId {
244        let id = self.next_anchor_id.get();
245        self.next_anchor_id.set(id + 1);
246        AnchorId(id)
247    }
248
249    /// Register an anchor at a specific position in the slots array.
250    fn register_anchor(&mut self, anchor: AnchorId, position: usize) {
251        debug_assert!(anchor.is_valid(), "attempted to register invalid anchor");
252        let idx = anchor.0;
253        if idx == 0 {
254            return;
255        }
256        if idx >= self.anchors.len() {
257            self.anchors.resize(idx + 1, INVALID_ANCHOR_POS);
258        }
259        self.anchors[idx] = position;
260    }
261
262    /// Returns whether the most recent `start` invocation reused a gap slot.
263    /// Resets the flag to false after reading.
264    fn take_last_start_was_gap(&mut self) -> bool {
265        let was_gap = self.last_start_was_gap;
266        self.last_start_was_gap = false;
267        was_gap
268    }
269
270    fn find_group_owner_index(&self, start: usize) -> Option<usize> {
271        if start == 0 || self.slots.is_empty() {
272            return None;
273        }
274
275        let mut idx = start.saturating_sub(1);
276        loop {
277            if let Some(Slot::Group { len, .. }) = self.slots.get(idx) {
278                let extent_end = idx.saturating_add(*len);
279                if start < extent_end {
280                    return Some(idx);
281                }
282            }
283            if idx == 0 {
284                break;
285            }
286            idx -= 1;
287        }
288        None
289    }
290
291    /// Resolve an anchor to its current position in the slots array.
292    fn resolve_anchor(&self, anchor: AnchorId) -> Option<usize> {
293        let idx = anchor.0;
294        if idx == 0 {
295            return None;
296        }
297        self.anchors
298            .get(idx)
299            .copied()
300            .filter(|&pos| pos != INVALID_ANCHOR_POS)
301    }
302
303    /// Mark a range of slots as gaps instead of truncating.
304    /// This preserves sibling components while allowing structure changes.
305    /// When encountering a Group, recursively marks the entire group structure as gaps.
306    pub fn mark_range_as_gaps(
307        &mut self,
308        start: usize,
309        end: usize,
310        owner_index: Option<usize>,
311    ) -> bool {
312        let mut i = start;
313        let end = end.min(self.slots.len());
314        let mut marked_any = false;
315
316        while i < end {
317            if i >= self.slots.len() {
318                break;
319            }
320
321            let (anchor, group_len, group_key, group_scope) = {
322                let slot = &self.slots[i];
323                let anchor = slot.anchor_id();
324                let (group_len, group_key, group_scope) = match slot {
325                    Slot::Group {
326                        len, key, scope, ..
327                    } => (*len, Some(*key), *scope),
328                    // Also preserve metadata for existing Gaps!
329                    // This is essential for the decrease-increase scenario where gaps
330                    // are re-marked as gaps but need to retain their original keys.
331                    Slot::Gap {
332                        group_len,
333                        group_key,
334                        group_scope,
335                        ..
336                    } => (*group_len, *group_key, *group_scope),
337                    _ => (0, None, None),
338                };
339                (anchor, group_len, group_key, group_scope)
340            };
341
342            // Mark this slot as a gap, preserving Group metadata if it was a Group
343            // This allows Groups to be properly matched and reused during tab switching
344            self.slots[i] = Slot::Gap {
345                anchor,
346                group_key,
347                group_scope,
348                group_len,
349            };
350            marked_any = true;
351
352            // If it was a group, recursively mark its children as gaps too
353            if group_len > 0 {
354                // Mark children (from i+1 to i+group_len)
355                let children_end = (i + group_len).min(end);
356                for j in (i + 1)..children_end {
357                    if j < self.slots.len() {
358                        // For nested Groups, preserve their metadata as well
359                        if let Slot::Group {
360                            key: nested_key,
361                            scope: nested_scope,
362                            len: nested_len,
363                            ..
364                        } = self.slots[j]
365                        {
366                            let child_anchor = self.slots[j].anchor_id();
367                            self.slots[j] = Slot::Gap {
368                                anchor: child_anchor,
369                                group_key: Some(nested_key),
370                                group_scope: nested_scope,
371                                group_len: nested_len,
372                            };
373                            marked_any = true;
374                        } else {
375                            // For Nodes and other slots, mark as regular gaps
376                            let child_anchor = self.slots[j].anchor_id();
377                            self.slots[j] = Slot::Gap {
378                                anchor: child_anchor,
379                                group_key: None,
380                                group_scope: None,
381                                group_len: 0,
382                            };
383                            marked_any = true;
384                        }
385                    }
386                }
387                i = (i + group_len).max(i + 1);
388            } else {
389                i += 1;
390            }
391        }
392
393        if marked_any {
394            let owner_idx = owner_index.or_else(|| self.find_group_owner_index(start));
395            if let Some(idx) = owner_idx {
396                if let Some(Slot::Group {
397                    has_gap_children, ..
398                }) = self.slots.get_mut(idx)
399                {
400                    *has_gap_children = true;
401                }
402                if let Some(frame) = self.group_stack.iter_mut().find(|frame| frame.start == idx) {
403                    frame.force_children_recompose = true;
404                }
405            }
406        }
407        marked_any
408    }
409
410    pub fn get_group_scope(&self, index: usize) -> Option<ScopeId> {
411        let slot = self
412            .slots
413            .get(index)
414            .expect("get_group_scope: index out of bounds");
415        match slot {
416            Slot::Group { scope, .. } => *scope,
417            _ => None,
418        }
419    }
420
421    pub fn set_group_scope(&mut self, index: usize, scope: ScopeId) {
422        let slot = self
423            .slots
424            .get_mut(index)
425            .expect("set_group_scope: index out of bounds");
426        match slot {
427            Slot::Group {
428                scope: scope_opt, ..
429            } => {
430                // With gaps implementation, Groups can be reused across compositions.
431                // Always update the scope to the current value.
432                *scope_opt = Some(scope);
433            }
434            _ => panic!("set_group_scope: slot at index is not a group"),
435        }
436    }
437
438    pub fn find_group_index_by_scope(&self, scope: ScopeId) -> Option<usize> {
439        self.slots
440            .iter()
441            .enumerate()
442            .find_map(|(i, slot)| match slot {
443                Slot::Group {
444                    scope: Some(id), ..
445                } if *id == scope => Some(i),
446                _ => None,
447            })
448    }
449
450    pub fn start_recranpose_at_scope(&mut self, scope: ScopeId) -> Option<usize> {
451        let index = self.find_group_index_by_scope(scope)?;
452        self.start_recompose(index);
453        Some(index)
454    }
455
456    pub fn debug_dump_groups(&self) -> Vec<(usize, Key, Option<ScopeId>, usize)> {
457        self.slots
458            .iter()
459            .enumerate()
460            .filter_map(|(i, slot)| match slot {
461                Slot::Group {
462                    key, len, scope, ..
463                } => Some((i, *key, *scope, *len)),
464                _ => None,
465            })
466            .collect()
467    }
468
469    pub fn debug_dump_all_slots(&self) -> Vec<(usize, String)> {
470        self.slots
471            .iter()
472            .enumerate()
473            .map(|(i, slot)| {
474                let kind = match slot {
475                    Slot::Group {
476                        key, scope, len, ..
477                    } => format!("Group(key={:?}, scope={:?}, len={})", key, scope, len),
478                    Slot::Value { .. } => "Value".to_string(),
479                    Slot::Node { id, .. } => format!("Node(id={:?})", id),
480                    Slot::Gap {
481                        group_key,
482                        group_scope,
483                        ..
484                    } => {
485                        if let Some(key) = group_key {
486                            format!("Gap(was_group_key={:?}, scope={:?})", key, group_scope)
487                        } else {
488                            "Gap".to_string()
489                        }
490                    }
491                };
492                (i, kind)
493            })
494            .collect()
495    }
496
497    fn update_group_bounds(&mut self) {
498        for frame in &mut self.group_stack {
499            if frame.end < self.cursor {
500                frame.end = self.cursor;
501            }
502        }
503    }
504
505    /// Update all anchor positions to match their current physical positions in the slots array.
506    /// This should be called after any operation that modifies slot positions (insert, remove, etc.)
507    fn rebuild_all_anchor_positions(&mut self) {
508        let mut max_anchor = 0usize;
509        for slot in &self.slots {
510            let idx = slot.anchor_id().0;
511            if idx > max_anchor {
512                max_anchor = idx;
513            }
514        }
515        if self.anchors.len() <= max_anchor {
516            self.anchors.resize(max_anchor + 1, INVALID_ANCHOR_POS);
517        }
518
519        for pos in &mut self.anchors {
520            *pos = INVALID_ANCHOR_POS;
521        }
522
523        for (position, slot) in self.slots.iter().enumerate() {
524            let idx = slot.anchor_id().0;
525            if idx == 0 {
526                continue;
527            }
528            self.anchors[idx] = position;
529        }
530    }
531
532    fn shift_group_frames(&mut self, index: usize, delta: isize) {
533        if delta == 0 {
534            return;
535        }
536        if delta > 0 {
537            let delta = delta as usize;
538            for frame in &mut self.group_stack {
539                if frame.start >= index {
540                    frame.start += delta;
541                    frame.end += delta;
542                } else if frame.end >= index {
543                    frame.end += delta;
544                }
545            }
546        } else {
547            let delta = (-delta) as usize;
548            for frame in &mut self.group_stack {
549                if frame.start >= index {
550                    frame.start = frame.start.saturating_sub(delta);
551                    frame.end = frame.end.saturating_sub(delta);
552                } else if frame.end > index {
553                    frame.end = frame.end.saturating_sub(delta);
554                }
555            }
556        }
557    }
558
559    pub fn start(&mut self, key: Key) -> usize {
560        self.ensure_capacity();
561
562        let cursor = self.cursor;
563        let parent_force = self
564            .group_stack
565            .last()
566            .map(|frame| frame.force_children_recompose)
567            .unwrap_or(false);
568
569        // === FAST PATH =======================================================
570        if let Some(Slot::Group {
571            key: existing_key,
572            len,
573            has_gap_children,
574            ..
575        }) = self.slots.get(cursor)
576        {
577            // Only fast-path if:
578            // 1) key matches
579            // 2) there were NO gap children before
580            // 3) parent is NOT forcing children to recompose
581            if *existing_key == key && !*has_gap_children && !parent_force {
582                self.last_start_was_gap = false;
583
584                let frame = GroupFrame {
585                    key,
586                    start: cursor,
587                    end: cursor + *len,
588                    force_children_recompose: false,
589                };
590                self.group_stack.push(frame);
591                self.cursor = cursor + 1;
592                self.update_group_bounds();
593                return cursor;
594            }
595        }
596
597        // if parent says "my children are unstable", don't try to be clever
598        if parent_force {
599            if let Some(Slot::Group {
600                key: existing_key,
601                len,
602                has_gap_children,
603                ..
604            }) = self.slots.get_mut(cursor)
605            {
606                if *existing_key == key {
607                    *has_gap_children = false;
608                    self.last_start_was_gap = true;
609                    let frame = GroupFrame {
610                        key,
611                        start: cursor,
612                        end: cursor + *len,
613                        force_children_recompose: true,
614                    };
615                    self.group_stack.push(frame);
616                    self.cursor = cursor + 1;
617                    self.update_group_bounds();
618                    return cursor;
619                }
620            }
621
622            if let Some(Slot::Gap {
623                anchor,
624                group_key: Some(gap_key),
625                group_scope,
626                group_len,
627            }) = self.slots.get(cursor)
628            {
629                if *gap_key == key {
630                    let anchor = *anchor;
631                    let gap_len = *group_len;
632                    let preserved_scope = *group_scope;
633                    self.slots[cursor] = Slot::Group {
634                        key,
635                        anchor,
636                        len: gap_len,
637                        scope: preserved_scope,
638                        has_gap_children: false,
639                    };
640                    self.register_anchor(anchor, cursor);
641                    self.last_start_was_gap = true;
642                    let frame = GroupFrame {
643                        key,
644                        start: cursor,
645                        end: cursor + gap_len,
646                        force_children_recompose: true,
647                    };
648                    self.group_stack.push(frame);
649                    self.cursor = cursor + 1;
650                    self.update_group_bounds();
651                    return cursor;
652                }
653            }
654
655            return self.insert_new_group_at_cursor(key);
656        }
657
658        self.last_start_was_gap = false;
659        let cursor = self.cursor;
660        debug_assert!(
661            cursor <= self.slots.len(),
662            "slot cursor {} out of bounds",
663            cursor
664        );
665
666        if cursor == self.slots.len() {
667            self.grow_slots();
668        }
669
670        debug_assert!(
671            cursor < self.slots.len(),
672            "slot cursor {} failed to grow",
673            cursor
674        );
675
676        if cursor > 0 && !matches!(self.slots.get(cursor), Some(Slot::Gap { .. })) {
677            if let Some(Slot::Group { key: prev_key, .. }) = self.slots.get(cursor - 1) {
678                if *prev_key == key {
679                    return self.insert_new_group_at_cursor(key);
680                }
681            }
682        }
683
684        // Check if we can reuse an existing Group at the cursor position without scanning.
685        if let Some(slot) = self.slots.get_mut(cursor) {
686            if let Slot::Group {
687                key: existing_key,
688                len,
689                has_gap_children,
690                ..
691            } = slot
692            {
693                if *existing_key == key {
694                    let group_len = *len;
695                    let had_gap_children = *has_gap_children;
696                    if had_gap_children {
697                        *has_gap_children = false;
698                    }
699                    let force_children = had_gap_children || parent_force;
700
701                    self.last_start_was_gap = false;
702                    let frame = GroupFrame {
703                        key,
704                        start: cursor,
705                        end: cursor + group_len,
706                        force_children_recompose: force_children,
707                    };
708                    self.group_stack.push(frame);
709                    self.cursor = cursor + 1;
710                    self.update_group_bounds();
711                    return cursor;
712                }
713            }
714        }
715
716        // Check if we can reuse an existing Group/GAP by converting in-place.
717        let mut reused_from_gap = false;
718        let reuse_result = match self.slots.get(cursor) {
719            // If there's already a Group here but with a different key, mark it as a gap
720            // instead of recycling it. This preserves the group so it can be found and
721            // restored later (critical for the decrease-increase and tab-switching scenarios).
722            // Then fall through to the search logic to find the desired key.
723            Some(Slot::Group {
724                key: existing_key,
725                anchor: old_anchor,
726                len: old_len,
727                scope: old_scope,
728                has_gap_children: _,
729            }) if *existing_key != key => {
730                // Copy all values to avoid borrow checker issues
731                let old_key = *existing_key;
732                let old_anchor_val = *old_anchor;
733                let old_len_val = *old_len;
734                let old_scope_val = *old_scope;
735
736                // Mark the group's children as gaps so they can be reused safely
737                let group_len = old_len_val.max(1);
738                if group_len > 1 {
739                    let start = cursor + 1;
740                    let end = cursor + group_len;
741                    let _ = self.mark_range_as_gaps(start, end, Some(cursor));
742                }
743
744                // Mark this group as a gap, preserving all its metadata
745                self.slots[cursor] = Slot::Gap {
746                    anchor: old_anchor_val,
747                    group_key: Some(old_key),
748                    group_scope: old_scope_val,
749                    group_len: old_len_val,
750                };
751                // Don't return early - fall through to search logic
752                None
753            }
754            // Also check for Gaps that were Groups with matching keys!
755            // This enables tab switching to reuse Groups that were marked as gaps.
756            Some(Slot::Gap {
757                anchor,
758                group_key: Some(gap_key),
759                group_scope,
760                group_len,
761            }) if *gap_key == key => {
762                // Convert the Gap back to a Group, preserving its scope and length
763                reused_from_gap = true;
764                Some((*group_len, *anchor, *group_scope))
765            }
766            Some(_slot) => None,
767            None => None,
768        };
769        if let Some((len, group_anchor, preserved_scope)) = reuse_result {
770            // Convert Gap back to Group if needed
771            if matches!(self.slots.get(cursor), Some(Slot::Gap { .. })) {
772                self.slots[cursor] = Slot::Group {
773                    key,
774                    anchor: group_anchor,
775                    len,
776                    scope: preserved_scope,
777                    has_gap_children: false,
778                };
779                self.register_anchor(group_anchor, cursor);
780            }
781
782            if reused_from_gap {
783                if let Some(Slot::Group {
784                    has_gap_children, ..
785                }) = self.slots.get_mut(cursor)
786                {
787                    *has_gap_children = false;
788                }
789            }
790
791            self.last_start_was_gap = reused_from_gap || parent_force;
792            let frame = GroupFrame {
793                key,
794                start: cursor,
795                end: cursor + len,
796                force_children_recompose: reused_from_gap || parent_force,
797            };
798            self.group_stack.push(frame);
799            self.cursor = cursor + 1;
800            self.update_group_bounds();
801            return cursor;
802        }
803
804        let allow_rescue = !parent_force && cursor < self.slots.len().saturating_sub(1);
805        if !allow_rescue {
806            return self.insert_new_group_at_cursor(key);
807        }
808
809        // When a group is restored from a gap, its preserved length may not accurately
810        // reflect where child groups/gaps are now located. To prevent duplicate group
811        // creation, we need to search beyond the preserved extent.
812        //
813        // Strategy: Search up to parent's end first (respects group hierarchy), but
814        // if that's less than slots.len(), we'll continue searching in a second pass
815        // below to find gaps that should be restored.
816        let parent_end = self
817            .group_stack
818            .last()
819            .map(|frame| frame.end.min(self.slots.len()))
820            .unwrap_or(self.slots.len());
821
822        // If parent_end seems constrained (less than slots length), we'll do an
823        // extended search for gaps after the initial search fails.
824        let needs_extended_search = parent_end < self.slots.len();
825
826        let mut search_index = cursor;
827        let mut found_group: Option<(usize, AnchorId, usize, Option<ScopeId>)> = None;
828        const SEARCH_BUDGET: usize = 16;
829        let mut scanned = 0usize;
830        while search_index < parent_end && scanned < SEARCH_BUDGET {
831            scanned += 1;
832            match self.slots.get(search_index) {
833                Some(Slot::Group {
834                    key: existing_key,
835                    anchor,
836                    len,
837                    scope: _,
838                    ..
839                }) => {
840                    let group_len = *len;
841                    if *existing_key == key {
842                        found_group = Some((search_index, *anchor, group_len, None));
843                        break;
844                    }
845                    // Search this group's children for gaps with matching keys
846                    // IMPORTANT: For gaps, we must search recursively inside them because
847                    // gaps can contain nested gaps after multiple decrease-increase cycles
848                    let mut child_index = search_index + 1;
849                    let search_limit = (search_index + group_len).min(self.slots.len());
850                    while child_index < search_limit {
851                        match self.slots.get(child_index) {
852                            Some(Slot::Gap {
853                                anchor: gap_anchor,
854                                group_key: Some(gap_key),
855                                group_scope,
856                                group_len: gap_len,
857                            }) if *gap_key == key => {
858                                // Found a matching gap!
859                                found_group =
860                                    Some((child_index, *gap_anchor, *gap_len, *group_scope));
861                                break;
862                            }
863                            Some(Slot::Gap {
864                                group_len: gap_len, ..
865                            }) => {
866                                // Skip this gap - we don't move deeply nested gaps
867                                // They will be restored when their parent gap is converted to a group
868                                child_index += (*gap_len).max(1);
869                            }
870                            Some(Slot::Group {
871                                len: nested_len, ..
872                            }) => {
873                                // Skip active groups (don't search inside them)
874                                child_index += (*nested_len).max(1);
875                            }
876                            _ => {
877                                child_index += 1;
878                            }
879                        }
880                    }
881                    if found_group.is_some() {
882                        break;
883                    }
884                    let advance = group_len.max(1);
885                    search_index = search_index.saturating_add(advance);
886                }
887                // Also search for Gaps that were Groups with matching keys
888                Some(Slot::Gap {
889                    anchor,
890                    group_key: Some(gap_key),
891                    group_scope,
892                    group_len,
893                }) => {
894                    if *gap_key == key {
895                        found_group = Some((search_index, *anchor, *group_len, *group_scope));
896                        break;
897                    }
898                    // Search this gap's children for gaps with matching keys
899                    // IMPORTANT: For nested gaps, search recursively inside them
900                    let gap_len_val = *group_len;
901                    let mut child_index = search_index + 1;
902                    let search_limit = (search_index + gap_len_val).min(self.slots.len());
903                    while child_index < search_limit {
904                        match self.slots.get(child_index) {
905                            Some(Slot::Gap {
906                                anchor: child_gap_anchor,
907                                group_key: Some(child_gap_key),
908                                group_scope: child_group_scope,
909                                group_len: child_gap_len,
910                            }) if *child_gap_key == key => {
911                                // Found a matching gap!
912                                found_group = Some((
913                                    child_index,
914                                    *child_gap_anchor,
915                                    *child_gap_len,
916                                    *child_group_scope,
917                                ));
918                                break;
919                            }
920                            Some(Slot::Gap {
921                                group_len: nested_gap_len,
922                                ..
923                            }) => {
924                                // Skip this gap - we don't move deeply nested gaps
925                                child_index += (*nested_gap_len).max(1);
926                            }
927                            Some(Slot::Group {
928                                len: nested_len, ..
929                            }) => {
930                                // Skip active groups (don't search inside them)
931                                child_index += (*nested_len).max(1);
932                            }
933                            _ => {
934                                child_index += 1;
935                            }
936                        }
937                    }
938                    if found_group.is_some() {
939                        break;
940                    }
941                    let advance = gap_len_val.max(1);
942                    search_index = search_index.saturating_add(advance);
943                }
944                Some(_slot) => {
945                    search_index += 1;
946                }
947                None => break,
948            }
949        }
950
951        // Extended search: If we didn't find the group within parent_end, but there are
952        // more slots to search, look for BOTH groups and gaps with matching keys.
953        // This handles the recursive decrease-increase case where a parent gap is restored
954        // but its children (which may be groups or gaps) are still at their original
955        // positions beyond the parent's preserved extent.
956        //
957        // IMPORTANT: After multiple decrease-increase cycles, gaps can drift beyond
958        // the parent's extent. We search all remaining slots to ensure we find the
959        // matching group/gap, regardless of structure complexity.
960        if found_group.is_none() && needs_extended_search {
961            search_index = parent_end;
962            const EXTENDED_SEARCH_BUDGET: usize = 16;
963            let mut extended_scanned = 0usize;
964            while search_index < self.slots.len() && extended_scanned < EXTENDED_SEARCH_BUDGET {
965                extended_scanned += 1;
966                match self.slots.get(search_index) {
967                    // Search for groups with matching keys (but only within limited range)
968                    Some(Slot::Group {
969                        key: existing_key,
970                        anchor,
971                        len,
972                        scope,
973                        ..
974                    }) => {
975                        if *existing_key == key {
976                            found_group = Some((search_index, *anchor, *len, *scope));
977                            break;
978                        }
979                        let advance = (*len).max(1);
980                        search_index = search_index.saturating_add(advance);
981                    }
982                    // Search for gaps with matching keys (candidates for restoration)
983                    Some(Slot::Gap {
984                        anchor,
985                        group_key: Some(gap_key),
986                        group_scope,
987                        group_len,
988                    }) => {
989                        if *gap_key == key {
990                            found_group = Some((search_index, *anchor, *group_len, *group_scope));
991                            break;
992                        }
993                        let advance = (*group_len).max(1);
994                        search_index = search_index.saturating_add(advance);
995                    }
996                    Some(_slot) => {
997                        search_index += 1;
998                    }
999                    None => break,
1000                }
1001            }
1002        }
1003
1004        if let Some((found_index, group_anchor, group_len, preserved_scope)) = found_group {
1005            // If we found a Gap, convert it back to a Group first
1006            let reused_gap = matches!(self.slots.get(found_index), Some(Slot::Gap { .. }));
1007            if reused_gap {
1008                self.slots[found_index] = Slot::Group {
1009                    key,
1010                    anchor: group_anchor,
1011                    len: group_len,
1012                    scope: preserved_scope,
1013                    has_gap_children: false,
1014                };
1015            }
1016
1017            self.last_start_was_gap = reused_gap || parent_force;
1018            let group_extent = group_len.max(1);
1019            let available = self.slots.len().saturating_sub(found_index);
1020            let actual_len = group_extent.min(available);
1021            if actual_len > 0 {
1022                self.shift_group_frames(found_index, -(actual_len as isize));
1023                let moved: Vec<_> = self
1024                    .slots
1025                    .drain(found_index..found_index + actual_len)
1026                    .collect();
1027                self.shift_group_frames(cursor, moved.len() as isize);
1028                self.slots.splice(cursor..cursor, moved);
1029                // Update all anchor positions after group move
1030                self.anchors_dirty = true;
1031                let frame = GroupFrame {
1032                    key,
1033                    start: cursor,
1034                    end: cursor + actual_len,
1035                    force_children_recompose: reused_gap || parent_force,
1036                };
1037                self.group_stack.push(frame);
1038                self.cursor = cursor + 1;
1039                self.update_group_bounds();
1040                return cursor;
1041            } else {
1042                // If the stored length exceeds available slots, fall back to inserting a new group.
1043                self.shift_group_frames(found_index, 0);
1044            }
1045        }
1046
1047        self.insert_new_group_at_cursor(key)
1048    }
1049
1050    fn insert_new_group_at_cursor(&mut self, key: Key) -> usize {
1051        // make sure we have space at the tail for pulling gaps
1052        self.ensure_capacity();
1053
1054        let cursor = self.cursor;
1055        self.ensure_gap_at_local(cursor);
1056        let parent_force = self
1057            .group_stack
1058            .last()
1059            .map(|frame| frame.force_children_recompose)
1060            .unwrap_or(false);
1061
1062        if cursor < self.slots.len() {
1063            debug_assert!(matches!(self.slots[cursor], Slot::Gap { .. }));
1064            let group_anchor = self.allocate_anchor();
1065            self.slots[cursor] = Slot::Group {
1066                key,
1067                anchor: group_anchor,
1068                len: 0,
1069                scope: None,
1070                has_gap_children: false,
1071            };
1072            self.register_anchor(group_anchor, cursor);
1073        } else {
1074            let group_anchor = self.allocate_anchor();
1075            self.slots.push(Slot::Group {
1076                key,
1077                anchor: group_anchor,
1078                len: 0,
1079                scope: None,
1080                has_gap_children: false,
1081            });
1082            self.register_anchor(group_anchor, cursor);
1083        }
1084        self.last_start_was_gap = parent_force;
1085        self.cursor = cursor + 1;
1086        self.group_stack.push(GroupFrame {
1087            key,
1088            start: cursor,
1089            end: self.cursor,
1090            force_children_recompose: parent_force,
1091        });
1092        self.update_group_bounds();
1093        cursor
1094    }
1095    fn shift_anchor_positions_from(&mut self, start_slot: usize, delta: isize) {
1096        for pos in &mut self.anchors {
1097            if *pos != INVALID_ANCHOR_POS && *pos >= start_slot {
1098                *pos = (*pos as isize + delta) as usize;
1099            }
1100        }
1101    }
1102    fn flush_anchors_if_dirty(&mut self) {
1103        if self.anchors_dirty {
1104            self.anchors_dirty = false;
1105            self.rebuild_all_anchor_positions();
1106        }
1107    }
1108    pub fn end(&mut self) {
1109        if let Some(frame) = self.group_stack.pop() {
1110            let end = self.cursor;
1111            if let Some(slot) = self.slots.get_mut(frame.start) {
1112                debug_assert_eq!(
1113                    SlotKind::Group,
1114                    slot.kind(),
1115                    "slot kind mismatch at {}",
1116                    frame.start
1117                );
1118                if let Slot::Group {
1119                    key,
1120                    len,
1121                    has_gap_children,
1122                    ..
1123                } = slot
1124                {
1125                    debug_assert_eq!(*key, frame.key, "group key mismatch");
1126                    // Calculate new length based on cursor position
1127                    let new_len = end.saturating_sub(frame.start);
1128                    let old_len = *len;
1129                    if new_len < old_len {
1130                        *has_gap_children = true;
1131                    }
1132                    const SHRINK_MIN_DROP: usize = 64;
1133                    const SHRINK_RATIO: usize = 4;
1134                    if old_len > new_len
1135                        && old_len >= new_len.saturating_mul(SHRINK_RATIO)
1136                        && (old_len - new_len) >= SHRINK_MIN_DROP
1137                    {
1138                        *len = new_len;
1139                    } else {
1140                        *len = old_len.max(new_len);
1141                    }
1142                }
1143            }
1144            if let Some(parent) = self.group_stack.last_mut() {
1145                if parent.end < end {
1146                    parent.end = end;
1147                }
1148            }
1149        }
1150    }
1151
1152    fn start_recompose(&mut self, index: usize) {
1153        if let Some(slot) = self.slots.get(index) {
1154            debug_assert_eq!(
1155                SlotKind::Group,
1156                slot.kind(),
1157                "slot kind mismatch at {}",
1158                index
1159            );
1160            if let Slot::Group { key, len, .. } = *slot {
1161                let frame = GroupFrame {
1162                    key,
1163                    start: index,
1164                    end: index + len,
1165                    force_children_recompose: false,
1166                };
1167                self.group_stack.push(frame);
1168                self.cursor = index + 1;
1169                if self.cursor < self.slots.len()
1170                    && matches!(self.slots.get(self.cursor), Some(Slot::Value { .. }))
1171                {
1172                    self.cursor += 1;
1173                }
1174            }
1175        }
1176    }
1177
1178    fn end_recompose(&mut self) {
1179        if let Some(frame) = self.group_stack.pop() {
1180            self.cursor = frame.end;
1181        }
1182    }
1183
1184    pub fn skip_current(&mut self) {
1185        if let Some(frame) = self.group_stack.last() {
1186            self.cursor = frame.end.min(self.slots.len());
1187        }
1188    }
1189
1190    pub fn node_ids_in_current_group(&self) -> Vec<NodeId> {
1191        let Some(frame) = self.group_stack.last() else {
1192            return Vec::new();
1193        };
1194        let end = frame.end.min(self.slots.len());
1195        self.slots[frame.start..end]
1196            .iter()
1197            .filter_map(|slot| match slot {
1198                Slot::Node { id, .. } => Some(*id),
1199                _ => None,
1200            })
1201            .collect()
1202    }
1203
1204    pub fn use_value_slot<T: 'static>(&mut self, init: impl FnOnce() -> T) -> usize {
1205        self.ensure_capacity();
1206
1207        let cursor = self.cursor;
1208        debug_assert!(
1209            cursor <= self.slots.len(),
1210            "slot cursor {} out of bounds",
1211            cursor
1212        );
1213
1214        if cursor < self.slots.len() {
1215            // Check if we can reuse the existing slot
1216            let reuse = matches!(
1217                self.slots.get(cursor),
1218                Some(Slot::Value { data, .. }) if data.is::<T>()
1219            );
1220            if reuse {
1221                self.cursor = cursor + 1;
1222                self.update_group_bounds();
1223                return cursor;
1224            }
1225
1226            // Check if the slot is a Gap that we can replace
1227            if matches!(self.slots.get(cursor), Some(Slot::Gap { .. })) {
1228                let anchor = self.allocate_anchor();
1229                let boxed: Box<dyn Any> = Box::new(init());
1230                self.slots[cursor] = Slot::Value {
1231                    anchor,
1232                    data: boxed,
1233                };
1234                self.register_anchor(anchor, cursor);
1235                self.cursor = cursor + 1;
1236                self.update_group_bounds();
1237                return cursor;
1238            }
1239
1240            // Type mismatch: replace current slot (mark old content as unreachable via gap)
1241            // We replace in-place to maintain cursor position
1242            let anchor = self.allocate_anchor();
1243            let boxed: Box<dyn Any> = Box::new(init());
1244            self.slots[cursor] = Slot::Value {
1245                anchor,
1246                data: boxed,
1247            };
1248            self.register_anchor(anchor, cursor);
1249            self.cursor = cursor + 1;
1250            self.update_group_bounds();
1251            return cursor;
1252        }
1253
1254        // We're at the end of the slot table, append new slot
1255        let anchor = self.allocate_anchor();
1256        let boxed: Box<dyn Any> = Box::new(init());
1257        let slot = Slot::Value {
1258            anchor,
1259            data: boxed,
1260        };
1261        self.slots.push(slot);
1262        self.register_anchor(anchor, cursor);
1263        self.cursor = cursor + 1;
1264        self.update_group_bounds();
1265        cursor
1266    }
1267
1268    pub fn read_value<T: 'static>(&self, idx: usize) -> &T {
1269        let slot = self
1270            .slots
1271            .get(idx)
1272            .unwrap_or_else(|| panic!("slot index {} out of bounds", idx));
1273        debug_assert_eq!(
1274            SlotKind::Value,
1275            slot.kind(),
1276            "slot kind mismatch at {}",
1277            idx
1278        );
1279        slot.as_value()
1280    }
1281
1282    pub fn read_value_mut<T: 'static>(&mut self, idx: usize) -> &mut T {
1283        let slot = self
1284            .slots
1285            .get_mut(idx)
1286            .unwrap_or_else(|| panic!("slot index {} out of bounds", idx));
1287        debug_assert_eq!(
1288            SlotKind::Value,
1289            slot.kind(),
1290            "slot kind mismatch at {}",
1291            idx
1292        );
1293        slot.as_value_mut()
1294    }
1295
1296    pub fn write_value<T: 'static>(&mut self, idx: usize, value: T) {
1297        if idx >= self.slots.len() {
1298            panic!("attempted to write slot {} out of bounds", idx);
1299        }
1300        let slot = &mut self.slots[idx];
1301        debug_assert_eq!(
1302            SlotKind::Value,
1303            slot.kind(),
1304            "slot kind mismatch at {}",
1305            idx
1306        );
1307        // Preserve the anchor when replacing the value
1308        let anchor = slot.anchor_id();
1309        *slot = Slot::Value {
1310            anchor,
1311            data: Box::new(value),
1312        };
1313    }
1314
1315    /// Read a value slot by its anchor ID.
1316    /// Provides stable access even if the slot's position changes.
1317    pub fn read_value_by_anchor<T: 'static>(&self, anchor: AnchorId) -> Option<&T> {
1318        let idx = self.resolve_anchor(anchor)?;
1319        Some(self.read_value(idx))
1320    }
1321
1322    /// Read a mutable value slot by its anchor ID.
1323    pub fn read_value_mut_by_anchor<T: 'static>(&mut self, anchor: AnchorId) -> Option<&mut T> {
1324        let idx = self.resolve_anchor(anchor)?;
1325        Some(self.read_value_mut(idx))
1326    }
1327
1328    pub fn remember<T: 'static>(&mut self, init: impl FnOnce() -> T) -> Owned<T> {
1329        let index = self.use_value_slot(|| Owned::new(init()));
1330        self.read_value::<Owned<T>>(index).clone()
1331    }
1332
1333    /// Remember a value and return both its index and anchor ID.
1334    /// The anchor provides stable access even if the slot's position changes.
1335    pub fn remember_with_anchor<T: 'static>(
1336        &mut self,
1337        init: impl FnOnce() -> T,
1338    ) -> (usize, AnchorId) {
1339        let index = self.use_value_slot(|| Owned::new(init()));
1340        let anchor = self
1341            .slots
1342            .get(index)
1343            .map(|slot| slot.anchor_id())
1344            .unwrap_or(AnchorId::INVALID);
1345        (index, anchor)
1346    }
1347
1348    pub fn record_node(&mut self, id: NodeId) {
1349        self.ensure_capacity();
1350
1351        let cursor = self.cursor;
1352        debug_assert!(
1353            cursor <= self.slots.len(),
1354            "slot cursor {} out of bounds",
1355            cursor
1356        );
1357        if cursor < self.slots.len() {
1358            // Check if we can reuse the existing node slot
1359            if let Some(Slot::Node { id: existing, .. }) = self.slots.get(cursor) {
1360                if *existing == id {
1361                    self.cursor = cursor + 1;
1362                    self.update_group_bounds();
1363                    return;
1364                }
1365            }
1366
1367            // Check if the slot is a Gap that we can replace
1368            if matches!(self.slots.get(cursor), Some(Slot::Gap { .. })) {
1369                let anchor = self.allocate_anchor();
1370                self.slots[cursor] = Slot::Node { anchor, id };
1371                self.register_anchor(anchor, cursor);
1372                self.cursor = cursor + 1;
1373                self.update_group_bounds();
1374                return;
1375            }
1376
1377            // Type mismatch: Replace the slot directly with the new node
1378            // For nodes, we can't use gaps because nodes exist in the applier
1379            // The old node becomes orphaned and will be garbage collected later
1380            let anchor = self.allocate_anchor();
1381            self.slots[cursor] = Slot::Node { anchor, id };
1382            self.register_anchor(anchor, cursor);
1383            self.cursor = cursor + 1;
1384            self.update_group_bounds();
1385            return;
1386        }
1387
1388        // No existing slot at cursor: add new slot
1389        let anchor = self.allocate_anchor();
1390        let slot = Slot::Node { anchor, id };
1391        self.slots.push(slot);
1392        self.register_anchor(anchor, cursor);
1393        self.cursor = cursor + 1;
1394        self.update_group_bounds();
1395    }
1396
1397    pub fn peek_node(&self) -> Option<NodeId> {
1398        let cursor = self.cursor;
1399        debug_assert!(
1400            cursor <= self.slots.len(),
1401            "slot cursor {} out of bounds",
1402            cursor
1403        );
1404        match self.slots.get(cursor) {
1405            Some(Slot::Node { id, .. }) => Some(*id),
1406            Some(_slot) => None,
1407            None => None,
1408        }
1409    }
1410
1411    pub fn read_node(&mut self) -> Option<NodeId> {
1412        let cursor = self.cursor;
1413        debug_assert!(
1414            cursor <= self.slots.len(),
1415            "slot cursor {} out of bounds",
1416            cursor
1417        );
1418        let node = match self.slots.get(cursor) {
1419            Some(Slot::Node { id, .. }) => Some(*id),
1420            Some(_slot) => None,
1421            None => None,
1422        };
1423        if node.is_some() {
1424            self.cursor = cursor + 1;
1425            self.update_group_bounds();
1426        }
1427        node
1428    }
1429
1430    pub fn advance_after_node_read(&mut self) {
1431        self.cursor += 1;
1432        self.update_group_bounds();
1433    }
1434
1435    pub fn reset(&mut self) {
1436        self.cursor = 0;
1437        self.group_stack.clear();
1438    }
1439
1440    /// Step the cursor back by one position.
1441    /// Used when we need to replace a slot that was just read but turned out to be incompatible.
1442    pub fn step_back(&mut self) {
1443        debug_assert!(self.cursor > 0, "Cannot step back from cursor 0");
1444        self.cursor = self.cursor.saturating_sub(1);
1445    }
1446
1447    /// Trim slots by marking unreachable slots as gaps.
1448    ///
1449    /// Instead of blindly truncating at cursor position, this method:
1450    /// 1. Marks slots from cursor to end of current group as gaps
1451    /// 2. Keeps the group length unchanged (gaps are part of the group's physical extent)
1452    /// 3. Preserves sibling components outside the current group
1453    ///
1454    /// This ensures effect states (LaunchedEffect, etc.) are preserved even when
1455    /// conditional rendering changes the composition structure.
1456    ///
1457    /// Key insight: Gap slots remain part of the group's physical length. The group's
1458    /// `len` field represents its physical extent in the slots array, not the count of
1459    /// active slots. This allows gap slots to be found and reused in subsequent compositions.
1460    pub fn trim_to_cursor(&mut self) -> bool {
1461        let mut marked = false;
1462        if let Some((owner_start, group_end)) = self
1463            .group_stack
1464            .last()
1465            .map(|frame| (frame.start, frame.end.min(self.slots.len())))
1466        {
1467            // Mark unreachable slots within this group as gaps
1468            if self.cursor < group_end
1469                && self.mark_range_as_gaps(self.cursor, group_end, Some(owner_start))
1470            {
1471                marked = true;
1472            }
1473
1474            // Update the frame end to current cursor
1475            // NOTE: We do NOT update the group's len field, because gap slots
1476            // are still part of the group's physical extent in the slots array.
1477            // The group len should remain unchanged so that traversal can find the gaps.
1478            if let Some(frame) = self.group_stack.last_mut() {
1479                frame.end = self.cursor;
1480            }
1481        } else if self.cursor < self.slots.len() {
1482            // If there's no group stack, we're at the root level
1483            // Mark everything beyond cursor as gaps
1484            if self.mark_range_as_gaps(self.cursor, self.slots.len(), None) {
1485                marked = true;
1486            }
1487        }
1488        marked
1489    }
1490}
1491
1492// ═══════════════════════════════════════════════════════════════════════════
1493// SlotStorage implementation for SlotTable
1494// ═══════════════════════════════════════════════════════════════════════════
1495
1496/// Baseline SlotStorage implementation using a gap-buffer strategy.
1497///
1498/// This is the reference / most-feature-complete backend, supporting:
1499/// - Gap-based slot reuse (preserving sibling state during conditional rendering)
1500/// - Anchor-based positional stability during group moves and insertions
1501/// - Efficient group skipping and recomposition via scope-based entry
1502/// - Batch anchor rebuilding for large structural changes
1503///
1504/// **Implementation Strategy:**
1505/// Uses UFCS (Uniform Function Call Syntax) to delegate to SlotTable's
1506/// inherent methods, avoiding infinite recursion while keeping the trait
1507/// implementation clean.
1508impl SlotStorage for SlotTable {
1509    type Group = GroupId;
1510    type ValueSlot = ValueSlotId;
1511
1512    fn begin_group(&mut self, key: Key) -> StartGroup<Self::Group> {
1513        let idx = SlotTable::start(self, key);
1514        let restored = SlotTable::take_last_start_was_gap(self);
1515        StartGroup {
1516            group: GroupId(idx),
1517            restored_from_gap: restored,
1518        }
1519    }
1520
1521    fn set_group_scope(&mut self, group: Self::Group, scope: ScopeId) {
1522        SlotTable::set_group_scope(self, group.0, scope);
1523    }
1524
1525    fn end_group(&mut self) {
1526        SlotTable::end(self);
1527    }
1528
1529    fn skip_current_group(&mut self) {
1530        SlotTable::skip_current(self);
1531    }
1532
1533    fn nodes_in_current_group(&self) -> Vec<NodeId> {
1534        SlotTable::node_ids_in_current_group(self)
1535    }
1536
1537    fn begin_recranpose_at_scope(&mut self, scope: ScopeId) -> Option<Self::Group> {
1538        SlotTable::start_recranpose_at_scope(self, scope).map(GroupId)
1539    }
1540
1541    fn end_recompose(&mut self) {
1542        SlotTable::end_recompose(self);
1543    }
1544
1545    fn alloc_value_slot<T: 'static>(&mut self, init: impl FnOnce() -> T) -> Self::ValueSlot {
1546        let idx = SlotTable::use_value_slot(self, init);
1547        ValueSlotId(idx)
1548    }
1549
1550    fn read_value<T: 'static>(&self, slot: Self::ValueSlot) -> &T {
1551        SlotTable::read_value(self, slot.0)
1552    }
1553
1554    fn read_value_mut<T: 'static>(&mut self, slot: Self::ValueSlot) -> &mut T {
1555        SlotTable::read_value_mut(self, slot.0)
1556    }
1557
1558    fn write_value<T: 'static>(&mut self, slot: Self::ValueSlot, value: T) {
1559        SlotTable::write_value(self, slot.0, value);
1560    }
1561
1562    fn remember<T: 'static>(&mut self, init: impl FnOnce() -> T) -> Owned<T> {
1563        SlotTable::remember(self, init)
1564    }
1565
1566    fn peek_node(&self) -> Option<NodeId> {
1567        SlotTable::peek_node(self)
1568    }
1569
1570    fn record_node(&mut self, id: NodeId) {
1571        SlotTable::record_node(self, id);
1572    }
1573
1574    fn advance_after_node_read(&mut self) {
1575        SlotTable::advance_after_node_read(self);
1576    }
1577
1578    fn step_back(&mut self) {
1579        SlotTable::step_back(self);
1580    }
1581
1582    fn finalize_current_group(&mut self) -> bool {
1583        SlotTable::trim_to_cursor(self)
1584    }
1585
1586    fn reset(&mut self) {
1587        SlotTable::reset(self);
1588    }
1589
1590    fn flush(&mut self) {
1591        SlotTable::flush_anchors_if_dirty(self);
1592    }
1593}