Skip to main content

cranpose_core/slot/
introspection.rs

1use super::{
2    GroupRecord, SlotDebugAnchor, SlotDebugEntry, SlotDebugEntryKind, SlotDebugGroup,
3    SlotDebugScope, SlotDebugSnapshot, SlotTable, SlotTableLocalDebugStats,
4    SlotTableMutationDebugStats,
5};
6use crate::{Key, RecomposeScope, ScopeId};
7
8impl SlotTable {
9    fn group_heap_bytes(&self) -> usize {
10        self.groups.capacity() * size_of::<GroupRecord>()
11    }
12
13    /// Heap bytes reserved by the table for groups, payloads, nodes and anchors.
14    pub fn heap_bytes(&self) -> usize {
15        self.group_heap_bytes()
16            + self.payloads.heap_bytes()
17            + self.node_heap_bytes()
18            + self.anchors.heap_bytes()
19            + self.payload_anchors.heap_bytes()
20    }
21
22    /// Returns size and count figures for this table alone, without retained or pending-drop data.
23    pub fn debug_stats(&self) -> SlotTableLocalDebugStats {
24        SlotTableLocalDebugStats {
25            group_count: self.groups.len(),
26            group_capacity: self.groups.capacity(),
27            group_record_size: size_of::<GroupRecord>(),
28            group_heap_bytes: self.group_heap_bytes(),
29            payload_count: self.total_payload_count(),
30            payload_capacity: self.payloads.capacity(),
31            active_payload_anchor_count: self.payload_anchors.active_len(),
32            payload_anchor_slot_count: self.payload_anchors.slot_len(),
33            detached_payload_anchor_count: self.payload_anchors.detached_len(),
34            invalidated_payload_anchor_count: self.payload_anchors.invalidated_len(),
35            free_payload_anchor_count: self.payload_anchors.free_len(),
36            payload_anchor_capacity: self.payload_anchors.capacity(),
37            payload_anchor_heap_bytes: self.payload_anchors.heap_bytes(),
38            node_count: self.total_node_count(),
39            node_capacity: self.node_debug_capacity(),
40            active_anchor_count: self.anchors.active_len(),
41            anchor_slot_count: self.anchors.slot_len(),
42            anchor_sparse_count: self.anchors.sparse_slot_len(),
43            detached_anchor_count: self.anchors.detached_len(),
44            invalidated_anchor_count: self.anchors.invalidated_len(),
45            free_anchor_count: self.anchors.free_len(),
46            anchor_capacity: self.anchors.capacity(),
47            anchor_heap_bytes: self.anchors.heap_bytes(),
48            mutation: SlotTableMutationDebugStats {
49                payload_shift_bytes: self.payloads.shift_bytes(),
50                ..self.diagnostics.mutation()
51            },
52        }
53    }
54
55    /// Returns one `(group index, call site key, scope id, subtree length)` tuple per group.
56    pub fn debug_dump_groups(&self) -> Vec<(usize, Key, Option<ScopeId>, usize)> {
57        self.groups
58            .iter()
59            .enumerate()
60            .map(|(index, group)| {
61                (
62                    index,
63                    group.key.static_key,
64                    group.scope.as_ref().map(RecomposeScope::id),
65                    group.subtree_len as usize,
66                )
67            })
68            .collect()
69    }
70
71    /// Returns a copy of the groups, anchors and scopes of this table.
72    /// The retained subtree counts are zero here; the composer fills them in.
73    pub fn debug_snapshot(&self) -> SlotDebugSnapshot {
74        let active_groups = self
75            .groups
76            .iter()
77            .enumerate()
78            .map(|(index, group)| SlotDebugGroup {
79                index,
80                anchor: group.anchor,
81                parent_anchor: group.parent_anchor,
82                static_key: group.key.static_key,
83                explicit_key: group.key.explicit_key,
84                ordinal: group.key.ordinal,
85                scope_id: group.scope.as_ref().map(RecomposeScope::id),
86                depth: group.depth,
87                subtree_len: group.subtree_len,
88                payload_len: self.group_payload_len_at(index),
89                node_len: self.group_node_len_at(index),
90                subtree_node_count: group.subtree_node_count,
91            })
92            .collect::<Vec<_>>();
93        let mut anchors = self
94            .anchors
95            .active_entries()
96            .map(|(anchor, stored)| SlotDebugAnchor {
97                anchor,
98                group_index: self.resolve_group_index(stored, anchor).unwrap_or(stored),
99            })
100            .collect::<Vec<_>>();
101        anchors.sort_by_key(|entry| entry.group_index);
102
103        let mut scopes = self
104            .groups
105            .iter()
106            .enumerate()
107            .filter_map(|(group_index, group)| {
108                group.scope.as_ref().map(|scope| SlotDebugScope {
109                    scope_id: scope.id(),
110                    anchor: group.anchor,
111                    group_index,
112                })
113            })
114            .collect::<Vec<_>>();
115        scopes.sort_by_key(|entry| entry.scope_id);
116
117        SlotDebugSnapshot {
118            active_payload_count: self.total_payload_count(),
119            active_node_count: self.total_node_count(),
120            active_scope_count: scopes.len(),
121            active_groups,
122            anchors,
123            scopes,
124            retained_subtree_count: 0,
125            retained_group_count: 0,
126            retained_payload_count: 0,
127            retained_node_count: 0,
128            retained_scope_count: 0,
129        }
130    }
131
132    /// Returns one row per group, payload and node in the table, in that order.
133    pub fn debug_dump_slot_entries(&self) -> Vec<SlotDebugEntry> {
134        let mut rows = Vec::new();
135        for (index, group) in self.groups.iter().enumerate() {
136            rows.push(SlotDebugEntry {
137                kind: SlotDebugEntryKind::Group,
138                path: format!("group[{index}]"),
139                line: format!(
140                    "Group(key={:?}, scope={:?}, subtree_len={}, payload_len={}, node_len={})",
141                    group.key,
142                    group.scope.as_ref().map(RecomposeScope::id),
143                    group.subtree_len,
144                    self.group_payload_len_at(index),
145                    self.group_node_len_at(index)
146                ),
147            });
148        }
149        for (group_index, group) in self.groups.iter().enumerate() {
150            for (payload_index, payload) in self.group_payload_records_at(group_index).enumerate() {
151                rows.push(SlotDebugEntry {
152                    kind: SlotDebugEntryKind::Payload,
153                    path: format!("group[{group_index}].payload[{payload_index}]"),
154                    line: format!(
155                        "Payload(owner={:?}, kind={}, type={})",
156                        group.anchor,
157                        payload.kind.label(),
158                        (payload.payload_type.type_name)()
159                    ),
160                });
161            }
162        }
163        for (group_index, _) in self.groups.iter().enumerate() {
164            for (node_index, node) in self.group_node_records_at(group_index).iter().enumerate() {
165                rows.push(SlotDebugEntry {
166                    kind: SlotDebugEntryKind::Node,
167                    path: format!("group[{group_index}].node[{node_index}]"),
168                    line: format!(
169                        "Node(owner={:?}, parent={:?}, id={}, gen={}, lifecycle={:?})",
170                        node.owner, node.parent_id, node.id, node.generation, node.lifecycle
171                    ),
172                });
173            }
174        }
175        rows
176    }
177}