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.clone().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_insert_with(|| NodeSubstates::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 .iter()
173 .map(|(key, _value)| key.clone())
174 .take(count.try_into().unwrap())
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 .iter()
199 .map(|(key, _)| key.clone())
200 .take(count.try_into().unwrap())
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)| {
243 (
244 k.clone(),
245 v.iter().map(|(k, v)| (k.clone(), v.len())).collect(),
246 )
247 })
248 .collect();
249
250 self.nodes.insert(node_id, substates);
251
252 for (partition_number, partition) in sizes {
253 for (substate_key, substate_size) in partition {
254 on_io_access(
255 self,
256 IOAccess::HeapSubstateUpdated {
257 canonical_substate_key: CanonicalSubstateKey {
258 node_id,
259 partition_number,
260 substate_key: substate_key.clone(),
261 },
262 old_size: None,
263 new_size: Some(substate_size),
264 },
265 )?;
266 }
267 }
268
269 Ok(())
270 }
271
272 pub fn remove_node<E, F: FnMut(&Heap, IOAccess) -> Result<(), E>>(
274 &mut self,
275 node_id: &NodeId,
276 on_io_access: &mut F,
277 ) -> Result<NodeSubstates, CallbackError<HeapRemoveNodeError, E>> {
278 let node_substates = match self.nodes.remove(node_id) {
279 Some(node_substates) => node_substates,
280 None => Err(CallbackError::Error(HeapRemoveNodeError::NodeNotFound(
281 node_id.clone().into(),
282 )))?,
283 };
284
285 for (partition_number, partition) in &node_substates {
286 for (substate_key, substate_value) in partition {
287 on_io_access(
288 self,
289 IOAccess::HeapSubstateUpdated {
290 canonical_substate_key: CanonicalSubstateKey {
291 node_id: *node_id,
292 partition_number: *partition_number,
293 substate_key: substate_key.clone(),
294 },
295 old_size: Some(substate_value.len()),
296 new_size: None,
297 },
298 )
299 .map_err(CallbackError::CallbackError)?;
300 }
301 }
302
303 Ok(node_substates)
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[test]
312 fn test_heap_size_accounting() {
313 let mut heap = Heap::new();
314 let mut total_size = 0;
315
316 let mut on_io_access = |_: &_, io_access| {
317 match io_access {
318 IOAccess::HeapSubstateUpdated {
319 canonical_substate_key,
320 old_size,
321 new_size,
322 } => {
323 if old_size.is_none() {
324 total_size += canonical_substate_key.len();
325 }
326 if new_size.is_none() {
327 total_size -= canonical_substate_key.len();
328 }
329
330 total_size += new_size.unwrap_or_default();
331 total_size -= old_size.unwrap_or_default();
332 }
333 _ => {}
334 }
335
336 Result::<(), ()>::Ok(())
337 };
338
339 let node_id = NodeId([0u8; NodeId::LENGTH]);
340 let partition_number1 = PartitionNumber(5);
341 let partition_number2 = PartitionNumber(6);
342 let key1 = SubstateKey::Map(scrypto_encode(&"1").unwrap());
343 let key2 = SubstateKey::Map(scrypto_encode(&"2").unwrap());
344 heap.create_node(
345 NodeId([0u8; NodeId::LENGTH]),
346 btreemap!(
347 partition_number1 => btreemap!(
348 key1.clone() => IndexedScryptoValue::from_typed("A"),
349 key2.clone() => IndexedScryptoValue::from_typed("B"),
350 ),
351 partition_number2 => btreemap!(
352 key1.clone() => IndexedScryptoValue::from_typed("C"),
353 key2.clone() => IndexedScryptoValue::from_typed("D"),
354 )
355 ),
356 &mut on_io_access,
357 )
358 .unwrap();
359 heap.set_substate(
360 node_id,
361 partition_number1,
362 key1,
363 IndexedScryptoValue::from_typed("E"),
364 &mut on_io_access,
365 )
366 .unwrap();
367 heap.drain_substates(&node_id, partition_number1, 1, &mut on_io_access)
368 .unwrap();
369 heap.remove_substate(&node_id, partition_number2, &key2, &mut on_io_access)
370 .unwrap();
371 heap.remove_partition(&node_id, partition_number2, &mut on_io_access)
372 .unwrap();
373 heap.remove_node(&node_id, &mut on_io_access).unwrap();
374 assert_eq!(total_size, 0);
375 }
376}