1use crate::internal_prelude::*;
2use crate::track::interface::IOAccess;
3use crate::track::interface::{CallbackError, CanonicalSubstateKey, NodeSubstates};
4
5pub struct Heap {
6 nodes: NonIterMap<NodeId, NodeSubstates>,
7}
8
9#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
10pub enum HeapRemovePartitionError {
11 NodeNotFound(error_models::ReferencedNodeId),
12 ModuleNotFound(PartitionNumber),
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
16pub enum HeapRemoveNodeError {
17 NodeNotFound(error_models::ReferencedNodeId),
18}
19
20impl Heap {
21 pub fn new() -> Self {
22 Self {
23 nodes: NonIterMap::new(),
24 }
25 }
26
27 pub fn is_empty(&self) -> bool {
28 self.nodes.is_empty()
29 }
30
31 pub fn remove_partition<E, F: FnMut(&Heap, IOAccess) -> Result<(), E>>(
32 &mut self,
33 node_id: &NodeId,
34 partition_number: PartitionNumber,
35 on_io_access: &mut F,
36 ) -> Result<
37 BTreeMap<SubstateKey, IndexedScryptoValue>,
38 CallbackError<HeapRemovePartitionError, E>,
39 > {
40 if let Some(substates) = self.nodes.get_mut(node_id) {
41 let partition = substates
42 .remove(&partition_number)
43 .ok_or(CallbackError::Error(
44 HeapRemovePartitionError::ModuleNotFound(partition_number),
45 ))?;
46
47 for (substate_key, substate_value) in &partition {
48 on_io_access(
49 self,
50 IOAccess::HeapSubstateUpdated {
51 canonical_substate_key: CanonicalSubstateKey {
52 node_id: *node_id,
53 partition_number,
54 substate_key: substate_key.clone(),
55 },
56 old_size: Some(substate_value.len()),
57 new_size: None,
58 },
59 )
60 .map_err(CallbackError::CallbackError)?;
61 }
62
63 Ok(partition)
64 } else {
65 Err(CallbackError::Error(
66 HeapRemovePartitionError::NodeNotFound((*node_id).into()),
67 ))
68 }
69 }
70
71 pub fn get_substate(
73 &self,
74 node_id: &NodeId,
75 partition_num: PartitionNumber,
76 substate_key: &SubstateKey,
77 ) -> Option<&IndexedScryptoValue> {
78 self.nodes
79 .get(node_id)
80 .and_then(|node| node.get(&partition_num))
81 .and_then(|partition_substates| partition_substates.get(substate_key))
82 }
83
84 pub fn set_substate<E, F: FnMut(&Heap, IOAccess) -> Result<(), E>>(
86 &mut self,
87 node_id: NodeId,
88 partition_number: PartitionNumber,
89 substate_key: SubstateKey,
90 substate_value: IndexedScryptoValue,
91 on_io_access: &mut F,
92 ) -> Result<(), E> {
93 let entry = self
94 .nodes
95 .entry(node_id)
96 .or_default()
97 .entry(partition_number)
98 .or_default()
99 .entry(substate_key.clone());
100
101 let old_size;
102 let new_size = Some(substate_value.len());
103 match entry {
104 btree_map::Entry::Vacant(e) => {
105 old_size = None;
106 e.insert(substate_value);
107 }
108 btree_map::Entry::Occupied(mut e) => {
109 old_size = Some(e.get().len());
110 e.insert(substate_value);
111 }
112 }
113
114 on_io_access(
115 self,
116 IOAccess::HeapSubstateUpdated {
117 canonical_substate_key: CanonicalSubstateKey {
118 node_id,
119 partition_number,
120 substate_key,
121 },
122 old_size,
123 new_size,
124 },
125 )?;
126
127 Ok(())
128 }
129
130 pub fn remove_substate<E, F: FnMut(&Heap, IOAccess) -> Result<(), E>>(
131 &mut self,
132 node_id: &NodeId,
133 partition_number: PartitionNumber,
134 substate_key: &SubstateKey,
135 on_io_access: &mut F,
136 ) -> Result<Option<IndexedScryptoValue>, E> {
137 let substate_value = self
138 .nodes
139 .get_mut(node_id)
140 .and_then(|n| n.get_mut(&partition_number))
141 .and_then(|s| s.remove(substate_key));
142
143 if let Some(value) = &substate_value {
144 on_io_access(
145 self,
146 IOAccess::HeapSubstateUpdated {
147 canonical_substate_key: CanonicalSubstateKey {
148 node_id: *node_id,
149 partition_number,
150 substate_key: substate_key.clone(),
151 },
152 old_size: Some(value.len()),
153 new_size: None,
154 },
155 )?;
156 }
157
158 Ok(substate_value)
159 }
160
161 pub fn scan_keys(
164 &self,
165 node_id: &NodeId,
166 partition_num: PartitionNumber,
167 count: u32,
168 ) -> Vec<SubstateKey> {
169 let node_substates = self.nodes.get(node_id).and_then(|n| n.get(&partition_num));
170 if let Some(substates) = node_substates {
171 let substate_keys: Vec<SubstateKey> = substates
172 .keys()
173 .take(count.try_into().unwrap())
174 .cloned()
175 .collect();
176
177 substate_keys
178 } else {
179 vec![]
180 }
181 }
182
183 pub fn drain_substates<E, F: FnMut(&Heap, IOAccess) -> Result<(), E>>(
186 &mut self,
187 node_id: &NodeId,
188 partition_number: PartitionNumber,
189 count: u32,
190 on_io_access: &mut F,
191 ) -> Result<Vec<(SubstateKey, IndexedScryptoValue)>, E> {
192 let node_substates = self
193 .nodes
194 .get_mut(node_id)
195 .and_then(|n| n.get_mut(&partition_number));
196 if let Some(substates) = node_substates {
197 let keys: Vec<SubstateKey> = substates
198 .keys()
199 .take(count.try_into().unwrap())
200 .cloned()
201 .collect();
202
203 let mut items = Vec::new();
204
205 for key in keys {
206 let value = substates.remove(&key).unwrap();
207 items.push((key, value));
208 }
209
210 for (key, value) in &items {
211 on_io_access(
212 self,
213 IOAccess::HeapSubstateUpdated {
214 canonical_substate_key: CanonicalSubstateKey {
215 node_id: *node_id,
216 partition_number,
217 substate_key: key.clone(),
218 },
219 old_size: Some(value.len()),
220 new_size: None,
221 },
222 )?;
223 }
224
225 Ok(items)
226 } else {
227 Ok(vec![])
228 }
229 }
230
231 pub fn create_node<E, F: FnMut(&Heap, IOAccess) -> Result<(), E>>(
233 &mut self,
234 node_id: NodeId,
235 substates: NodeSubstates,
236 on_io_access: &mut F,
237 ) -> Result<(), E> {
238 assert!(!self.nodes.contains_key(&node_id));
239
240 let sizes: IndexMap<PartitionNumber, IndexMap<SubstateKey, usize>> = substates
241 .iter()
242 .map(|(k, v)| (*k, v.iter().map(|(k, v)| (k.clone(), v.len())).collect()))
243 .collect();
244
245 self.nodes.insert(node_id, substates);
246
247 for (partition_number, partition) in sizes {
248 for (substate_key, substate_size) in partition {
249 on_io_access(
250 self,
251 IOAccess::HeapSubstateUpdated {
252 canonical_substate_key: CanonicalSubstateKey {
253 node_id,
254 partition_number,
255 substate_key: substate_key.clone(),
256 },
257 old_size: None,
258 new_size: Some(substate_size),
259 },
260 )?;
261 }
262 }
263
264 Ok(())
265 }
266
267 pub fn remove_node<E, F: FnMut(&Heap, IOAccess) -> Result<(), E>>(
269 &mut self,
270 node_id: &NodeId,
271 on_io_access: &mut F,
272 ) -> Result<NodeSubstates, CallbackError<HeapRemoveNodeError, E>> {
273 let node_substates = match self.nodes.remove(node_id) {
274 Some(node_substates) => node_substates,
275 None => Err(CallbackError::Error(HeapRemoveNodeError::NodeNotFound(
276 (*node_id).into(),
277 )))?,
278 };
279
280 for (partition_number, partition) in &node_substates {
281 for (substate_key, substate_value) in partition {
282 on_io_access(
283 self,
284 IOAccess::HeapSubstateUpdated {
285 canonical_substate_key: CanonicalSubstateKey {
286 node_id: *node_id,
287 partition_number: *partition_number,
288 substate_key: substate_key.clone(),
289 },
290 old_size: Some(substate_value.len()),
291 new_size: None,
292 },
293 )
294 .map_err(CallbackError::CallbackError)?;
295 }
296 }
297
298 Ok(node_substates)
299 }
300}
301
302impl Default for Heap {
303 fn default() -> Self {
304 Self::new()
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn test_heap_size_accounting() {
314 let mut heap = Heap::new();
315 let mut total_size = 0;
316
317 let mut on_io_access = |_: &_, io_access| {
318 if let IOAccess::HeapSubstateUpdated {
319 canonical_substate_key,
320 old_size,
321 new_size,
322 } = io_access
323 {
324 if old_size.is_none() {
325 total_size += canonical_substate_key.len();
326 }
327 if new_size.is_none() {
328 total_size -= canonical_substate_key.len();
329 }
330
331 total_size += new_size.unwrap_or_default();
332 total_size -= old_size.unwrap_or_default();
333 }
334
335 Result::<(), ()>::Ok(())
336 };
337
338 let node_id = NodeId([0u8; NodeId::LENGTH]);
339 let partition_number1 = PartitionNumber(5);
340 let partition_number2 = PartitionNumber(6);
341 let key1 = SubstateKey::Map(scrypto_encode(&"1").unwrap());
342 let key2 = SubstateKey::Map(scrypto_encode(&"2").unwrap());
343 heap.create_node(
344 NodeId([0u8; NodeId::LENGTH]),
345 btreemap!(
346 partition_number1 => btreemap!(
347 key1.clone() => IndexedScryptoValue::from_typed("A"),
348 key2.clone() => IndexedScryptoValue::from_typed("B"),
349 ),
350 partition_number2 => btreemap!(
351 key1.clone() => IndexedScryptoValue::from_typed("C"),
352 key2.clone() => IndexedScryptoValue::from_typed("D"),
353 )
354 ),
355 &mut on_io_access,
356 )
357 .unwrap();
358 heap.set_substate(
359 node_id,
360 partition_number1,
361 key1,
362 IndexedScryptoValue::from_typed("E"),
363 &mut on_io_access,
364 )
365 .unwrap();
366 heap.drain_substates(&node_id, partition_number1, 1, &mut on_io_access)
367 .unwrap();
368 heap.remove_substate(&node_id, partition_number2, &key2, &mut on_io_access)
369 .unwrap();
370 heap.remove_partition(&node_id, partition_number2, &mut on_io_access)
371 .unwrap();
372 heap.remove_node(&node_id, &mut on_io_access).unwrap();
373 assert_eq!(total_size, 0);
374 }
375}