1use std::collections::HashMap;
25
26use num::One;
27use triton_vm::memory_layout::MemoryRegion;
28use triton_vm::prelude::*;
29
30pub mod dyn_malloc;
31pub mod memcpy;
32
33pub const FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS: BFieldElement =
37 BFieldElement::new(0);
38
39pub const LAST_ADDRESS_AVAILABLE_FOR_NON_DETERMINISTICALLY_ALLOCATED_MEMORY: BFieldElement =
40 BFieldElement::new(u32::MAX as u64);
41
42pub fn last_populated_nd_memory_address(
45 memory: &HashMap<BFieldElement, BFieldElement>,
46) -> Option<BFieldElement> {
47 memory
48 .keys()
49 .map(|b| b.value())
50 .filter(|u| *u <= LAST_ADDRESS_AVAILABLE_FOR_NON_DETERMINISTICALLY_ALLOCATED_MEMORY.value())
51 .max()
52 .map(|address| bfe!(address))
53}
54
55pub fn first_free_nd_address(
58 memory: &HashMap<BFieldElement, BFieldElement>,
59) -> Option<BFieldElement> {
60 let last_populated = last_populated_nd_memory_address(memory);
61 match last_populated {
62 None => Some(FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS),
63 Some(last_populated) => match last_populated {
64 LAST_ADDRESS_AVAILABLE_FOR_NON_DETERMINISTICALLY_ALLOCATED_MEMORY => None,
65 addr => Some(addr + BFieldElement::one()),
66 },
67 }
68}
69
70pub fn nd_memory_region() -> MemoryRegion {
71 let size = LAST_ADDRESS_AVAILABLE_FOR_NON_DETERMINISTICALLY_ALLOCATED_MEMORY.value()
72 - FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS.value();
73
74 MemoryRegion::new(FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS, size)
75}
76
77pub fn encode_to_memory<T: BFieldCodec>(
80 memory: &mut HashMap<BFieldElement, BFieldElement>,
81 address: BFieldElement,
82 object: &T,
83) -> BFieldElement {
84 let encoding = object.encode();
85 for (i, e) in encoding.iter().enumerate() {
86 memory.insert(address + bfe!(i), *e);
87 }
88 address + bfe!(encoding.len())
89}
90
91pub fn load_words_from_memory_leave_pointer(n: usize) -> Vec<LabelledInstruction> {
99 let num_full_chunk_reads = n / 5;
100 let num_remaining_words = n % 5;
101 let mut instructions = triton_asm![read_mem 5; num_full_chunk_reads];
102 if num_remaining_words > 0 {
103 instructions.extend(triton_asm!(read_mem {
104 num_remaining_words
105 }));
106 }
107 instructions
108}
109
110pub fn load_words_from_memory_pop_pointer(n: usize) -> Vec<LabelledInstruction> {
118 let instructions = load_words_from_memory_leave_pointer(n);
119
120 triton_asm!(
121 {&instructions}
122 pop 1
123 )
124}
125
126pub fn write_words_to_memory_leave_pointer(n: usize) -> Vec<LabelledInstruction> {
134 let num_full_chunk_reads = n / 5;
135 let num_remaining_words = n % 5;
136 let mut instructions = vec![triton_instr!(write_mem 5); num_full_chunk_reads];
137 if num_remaining_words > 0 {
138 instructions.extend(triton_asm!(write_mem {
139 num_remaining_words
140 }));
141 }
142
143 instructions
144}
145
146pub fn write_words_to_memory_pop_pointer(n: usize) -> Vec<LabelledInstruction> {
153 let instructions = write_words_to_memory_leave_pointer(n);
154
155 triton_asm!(
156 {&instructions}
157 pop 1
158 )
159}
160
161#[cfg(test)]
162mod tests {
163 use std::collections::HashMap;
164
165 use num::Zero;
166
167 use super::*;
168 use crate::test_prelude::*;
169
170 #[macro_rules_attr::apply(test)]
171 fn last_populated_nd_memory_address_looks_sane() {
172 let empty_memory = HashMap::default();
173 assert!(last_populated_nd_memory_address(&empty_memory).is_none());
174 assert_eq!(
175 Some(BFieldElement::zero()),
176 first_free_nd_address(&empty_memory)
177 );
178
179 let empty_nd_region: HashMap<_, _> = [(bfe!(1u64 << 32), BFieldElement::new(42))]
180 .into_iter()
181 .collect();
182 assert!(last_populated_nd_memory_address(&empty_nd_region).is_none());
183 assert_eq!(
184 Some(BFieldElement::zero()),
185 first_free_nd_address(&empty_nd_region)
186 );
187
188 for address in [0, 1, 100, u32::MAX - 3, u32::MAX - 1, u32::MAX] {
189 let one_populated_word: HashMap<_, _> = [(bfe!(address), BFieldElement::new(42))]
190 .into_iter()
191 .collect();
192 assert_eq!(
193 Some(bfe!(address)),
194 last_populated_nd_memory_address(&one_populated_word)
195 );
196 }
197
198 for address in [1, 100, u32::MAX - 3, u32::MAX - 1, u32::MAX] {
199 let two_populated_words: HashMap<_, _> = [
200 (bfe!(address), BFieldElement::new(42)),
201 (bfe!(address - 1), BFieldElement::new(42)),
202 ]
203 .into_iter()
204 .collect();
205 assert_eq!(
206 Some(bfe!(address)),
207 last_populated_nd_memory_address(&two_populated_words)
208 );
209 }
210
211 let much_poulated_zero_is_empty: HashMap<_, _> =
212 (1..4000).map(|address| (bfe!(address), bfe!(42))).collect();
213 assert_eq!(
214 Some(bfe!(3999)),
215 last_populated_nd_memory_address(&much_poulated_zero_is_empty)
216 );
217 assert_eq!(
218 Some(bfe!(4000)),
219 first_free_nd_address(&much_poulated_zero_is_empty)
220 );
221
222 let much_poulated_zero_populated: HashMap<_, _> =
223 (0..4021).map(|address| (bfe!(address), bfe!(42))).collect();
224 assert_eq!(
225 Some(bfe!(4020)),
226 last_populated_nd_memory_address(&much_poulated_zero_populated)
227 );
228 assert_eq!(
229 Some(bfe!(4021)),
230 first_free_nd_address(&much_poulated_zero_populated)
231 );
232
233 let scattered_population: HashMap<_, _> = [(bfe!(30), bfe!(42)), (bfe!(2000), bfe!(42))]
234 .into_iter()
235 .collect();
236 assert_eq!(
237 Some(bfe!(2000)),
238 last_populated_nd_memory_address(&scattered_population)
239 );
240 assert_eq!(
241 Some(bfe!(2001)),
242 first_free_nd_address(&scattered_population)
243 );
244 }
245
246 #[macro_rules_attr::apply(test)]
247 fn first_free_nd_address_looks_sane() {
248 for address in [0, 1, 100, u32::MAX - 3, u32::MAX - 3] {
249 let one_populated_word: HashMap<_, _> = [(bfe!(address), BFieldElement::new(42))]
250 .into_iter()
251 .collect();
252 assert_eq!(
253 Some(bfe!(address + 1)),
254 first_free_nd_address(&one_populated_word)
255 );
256
257 let two_populated_words: HashMap<_, _> = [
258 (bfe!(address), BFieldElement::new(42)),
259 (bfe!(address + 1), BFieldElement::new(42)),
260 ]
261 .into_iter()
262 .collect();
263 assert_eq!(
264 Some(bfe!(address + 2)),
265 first_free_nd_address(&two_populated_words)
266 );
267 }
268
269 let last_word_populated: HashMap<_, _> = [(
270 LAST_ADDRESS_AVAILABLE_FOR_NON_DETERMINISTICALLY_ALLOCATED_MEMORY,
271 bfe!(42),
272 )]
273 .into_iter()
274 .collect();
275 assert!(first_free_nd_address(&last_word_populated).is_none());
276 }
277
278 #[macro_rules_attr::apply(proptest)]
279 fn all_addresses_between_0_and_u32max_belong_to_nd_memory(nd_address: u32) {
280 prop_assert!(nd_memory_region().contains_address(bfe!(nd_address)));
281 }
282
283 #[macro_rules_attr::apply(proptest)]
284 fn addresses_outside_u32_range_do_not_belong_to_nd_memory(
285 #[strategy(arb())]
286 #[filter(#address.value() > u32::MAX as u64)]
287 address: BFieldElement,
288 ) {
289 prop_assert!(!nd_memory_region().contains_address(address));
290 }
291}