cranpose_core/memory_applier/
applier_impl.rs1use std::{any::TypeId, cmp::Reverse, collections::BinaryHeap};
2
3use super::MemoryApplier;
4use crate::{Applier, Node, NodeError, NodeId, RecycleOrigin, RecycledNode, RecycledNodeInsertion};
5
6impl Applier for MemoryApplier {
7 fn record_structural_change(&mut self, parent_id: NodeId) {
8 if self.state.structural_change_parents.last() != Some(&parent_id) {
9 self.state.structural_change_parents.push(parent_id);
10 }
11 }
12
13 fn create(&mut self, node: Box<dyn Node>) -> NodeId {
14 let stable_id = self.state.next_stable_id;
15 self.state.next_stable_id = self.state.next_stable_id.next();
16 if stable_id >= Self::HIGH_ID_THRESHOLD {
17 self.insert_high_id_node(stable_id, node, RecycleOrigin::Cold);
18 return stable_id;
19 }
20
21 let physical_id = if let Some(Reverse(id)) = self.state.free_ids.pop() {
22 debug_assert!(
23 self.state.nodes[id].is_none(),
24 "freelist entry {id} is not None"
25 );
26 self.state.nodes[id] = Some(node);
27 self.state.physical_stable_ids[id] = Self::pack_stable_id(stable_id);
28 self.state.physical_warm_recycled_origins[id] = RecycleOrigin::Cold;
29 id
30 } else {
31 self.ensure_dense_node_storage_capacity();
32 let id = self.state.nodes.len();
33 self.state.nodes.push(Some(node));
34 self.state
35 .physical_stable_ids
36 .push(Self::pack_stable_id(stable_id));
37 self.state
38 .physical_warm_recycled_origins
39 .push(RecycleOrigin::Cold);
40 id
41 };
42 self.state.stable_index.insert_fresh(stable_id, physical_id);
43 stable_id
44 }
45
46 fn node_generation(&self, id: NodeId) -> u32 {
47 if id >= Self::HIGH_ID_THRESHOLD {
48 return self
49 .state
50 .high_ids
51 .get(&id)
52 .map_or(0, |slot| slot.generation);
53 }
54 self.state.stable_index.generation(id)
55 }
56
57 #[inline]
58 fn get_mut(&mut self, id: NodeId) -> Result<&mut dyn Node, NodeError> {
59 match self.resolve_node_index(id) {
60 Some(physical_id) => self
61 .state
62 .nodes
63 .get_mut(physical_id)
64 .and_then(Option::as_deref_mut)
65 .ok_or(NodeError::Missing { id }),
66 None => self.high_id_node_mut(id),
67 }
68 }
69
70 fn remove(&mut self, id: NodeId) -> Result<(), NodeError> {
71 self.state.bubble_passes.forget();
72 self.remove_subtree_postorder(id).map(|_| ())
73 }
74
75 fn begin_bubble_batch(&mut self) {
76 self.state.bubble_passes.begin_batch();
77 }
78
79 fn note_layout_dirt(&mut self, id: NodeId) {
80 self.state.layout_dirt_roots.note(id);
81 }
82
83 fn end_bubble_batch(&mut self) {
84 self.state.bubble_passes.end_batch();
85 }
86
87 fn forget_bubble_passes(&mut self) {
88 self.state.bubble_passes.forget();
89 }
90
91 fn note_bubble_pass(&mut self, id: NodeId, flags: u8) -> bool {
92 self.resolve_node_index(id)
93 .is_some_and(|index| self.state.bubble_passes.note(index, flags))
94 }
95
96 fn bubble_node(&mut self, id: NodeId, flags: u8, note: bool) -> Option<&mut dyn Node> {
98 let Some(index) = self.resolve_node_index(id) else {
99 return self
100 .state
101 .high_ids
102 .get_mut(&id)
103 .and_then(|slot| slot.node.as_deref_mut());
104 };
105 if note && self.state.bubble_passes.note(index, flags) {
106 return None;
107 }
108 self.state.nodes.get_mut(index)?.as_deref_mut()
109 }
110
111 fn get_ref(&self, id: NodeId) -> Result<&dyn Node, NodeError> {
112 Self::get_ref(self, id)
113 }
114
115 fn create_virtual(&mut self, node: Box<dyn Node>) -> NodeId {
116 let id = self.create(node);
117 self.state.virtual_node_ids.insert(id);
118 id
119 }
120
121 fn insert_recycled_node_or_create(
122 &mut self,
123 stable_id: NodeId,
124 node: Box<dyn Node>,
125 ) -> RecycledNodeInsertion {
126 if self.contains_node_id(stable_id) {
127 let id = self.create(node);
128 return RecycledNodeInsertion::fresh(
129 id,
130 Some(NodeError::AlreadyExists { id: stable_id }),
131 );
132 }
133
134 self.insert_available_with_id(stable_id, node);
135 RecycledNodeInsertion::reused(stable_id)
136 }
137
138 fn compact(&mut self) {
139 let live_count = self
140 .state
141 .nodes
142 .iter()
143 .filter(|slot| slot.is_some())
144 .count();
145 let tombstone_count = self.state.nodes.len().saturating_sub(live_count);
146 if tombstone_count == 0 {
147 return;
148 }
149 if self.state.nodes.len() > Self::EAGER_COMPACT_NODE_LEN && tombstone_count < live_count {
150 return;
151 }
152 let rehouse_live_nodes = tombstone_count >= live_count;
153 let mut packed_nodes = Vec::with_capacity(live_count);
154 let mut packed_physical_stable_ids = Vec::with_capacity(live_count);
155 let mut packed_warm_recycled_origins = Vec::with_capacity(live_count);
156
157 for physical_id in 0..self.state.nodes.len() {
158 let Some(mut node) = self.state.nodes[physical_id].take() else {
159 continue;
160 };
161 if rehouse_live_nodes && let Some(rehoused) = node.rehouse_for_live_compaction() {
162 node = rehoused;
163 }
164 let stable_id = std::mem::replace(
165 &mut self.state.physical_stable_ids[physical_id],
166 Self::INVALID_STABLE_ID,
167 );
168 debug_assert_ne!(
169 stable_id,
170 Self::INVALID_STABLE_ID,
171 "live physical slot must have a stable id",
172 );
173 let stable_id = Self::unpack_stable_id(stable_id);
174 packed_nodes.push(Some(node));
175 packed_physical_stable_ids.push(Self::pack_stable_id(stable_id));
176 packed_warm_recycled_origins
177 .push(self.state.physical_warm_recycled_origins[physical_id]);
178 self.state
179 .stable_index
180 .set_slot(stable_id, packed_nodes.len() - 1);
181 }
182
183 self.state.nodes = packed_nodes;
184 self.state.physical_stable_ids = packed_physical_stable_ids;
185 self.state.physical_warm_recycled_origins = packed_warm_recycled_origins;
186 self.state.free_ids = BinaryHeap::new();
187 debug_assert_eq!(
188 self.state.stable_index.live(),
189 live_count,
190 "every live node keeps one indexed slot through compaction",
191 );
192 self.prune_stable_generations();
193 }
194
195 fn take_recycled_node(&mut self, key: TypeId) -> Option<RecycledNode> {
196 self.state.recycle_pools.get_mut(&key)?.take()
197 }
198
199 fn set_recycled_node_origin(&mut self, id: NodeId, origin: RecycleOrigin) {
200 if let Some(physical_id) = self.resolve_node_index(id) {
201 self.state.physical_warm_recycled_origins[physical_id] = origin;
202 } else if let Some(slot) = self.state.high_ids.get_mut(&id)
203 && slot.node.is_some()
204 {
205 slot.origin = origin;
206 }
207 }
208
209 fn seed_recycled_node_shell(
210 &mut self,
211 key: TypeId,
212 recycle_pool_limit: Option<usize>,
213 shell: Box<dyn Node>,
214 ) {
215 self.seed_recycled_node_shell_impl(key, recycle_pool_limit, shell);
216 }
217
218 fn record_fresh_recyclable_creation(&mut self, key: TypeId) {
219 self.state
220 .recycle_pools
221 .entry(key)
222 .or_default()
223 .fresh_creations += 1;
224 }
225
226 fn clear_recycled_nodes(&mut self) {
227 let next_stable_id = &mut self.state.next_stable_id;
228 for pool in self.state.recycle_pools.values_mut() {
229 pool.end_pass(next_stable_id);
230 }
231 self.prune_stable_generations();
232 self.compact();
233 }
234}