Skip to content

Commit 6c897f3

Browse files
committed
memory: add get_array method
1 parent dbfefb5 commit 6c897f3

File tree

6 files changed

+126
-28
lines changed

6 files changed

+126
-28
lines changed

crates/leanVm/src/context/run_context.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,7 @@ mod tests {
154154

155155
// Insert a value at that address manually.
156156
let expected_val = MemoryValue::Int(F::from_u64(99));
157-
memory
158-
.memory
159-
.insert(addr_to_read, expected_val.clone())
160-
.unwrap();
157+
memory.memory.insert(addr_to_read, expected_val).unwrap();
161158

162159
// Create a RunContext with that fp.
163160
let ctx = RunContext::new(
@@ -240,10 +237,7 @@ mod tests {
240237
let fp = memory.add();
241238
let addr_to_read = fp.add_usize::<F>(7).unwrap();
242239
let expected_val = MemoryValue::<F>::Address(MemoryAddress::new(5, 5));
243-
memory
244-
.memory
245-
.insert(addr_to_read, expected_val.clone())
246-
.unwrap();
240+
memory.memory.insert(addr_to_read, expected_val).unwrap();
247241

248242
let ctx = RunContext::new(MemoryAddress::new(0, 0), fp);
249243
let operand = MemOrFpOrConstant::MemoryAfterFp { shift: 7 };

crates/leanVm/src/core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ mod tests {
378378
vm.memory_manager.add();
379379
}
380380
// Insert the value at the specified address, panicking on failure for test simplicity.
381-
vm.memory_manager.memory.insert(*addr, val.clone()).unwrap();
381+
vm.memory_manager.memory.insert(*addr, *val).unwrap();
382382
}
383383
// Return the fully configured VM.
384384
vm

crates/leanVm/src/memory/cell.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,15 @@ mod tests {
206206

207207
// Test on a valid integer cell.
208208
let int_val = MemoryValue::Int(F::from_u64(123));
209-
let int_cell = MemoryCell::from(int_val.clone());
209+
let int_cell = MemoryCell::from(int_val);
210210
assert_eq!(int_cell.value(), Some(int_val));
211211

212212
// Test on a valid address cell.
213213
let addr_val = MemoryValue::<F>::Address(MemoryAddress {
214214
segment_index: 5,
215215
offset: 10,
216216
});
217-
let addr_cell = MemoryCell::from(addr_val.clone());
217+
let addr_cell = MemoryCell::from(addr_val);
218218
assert_eq!(addr_cell.value(), Some(addr_val));
219219
}
220220

@@ -272,7 +272,7 @@ mod tests {
272272
val in any::<MemoryValue<F>>()
273273
) {
274274
// Convert the generated MemoryValue to a MemoryCell.
275-
let cell = MemoryCell::from(val.clone());
275+
let cell = MemoryCell::from(val);
276276

277277
// Convert the MemoryCell back to a MemoryValue.
278278
let roundtrip_val = MemoryValue::<F>::from(cell);

crates/leanVm/src/memory/manager.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl MemoryManager {
7777
// This reverse order allows any required memory segment resizing
7878
// (e.g., length extension or capacity reservation) to occur *once*
7979
// at the highest offset instead of repeatedly during writes.
80-
for (num, value) in data.iter().enumerate().rev() {
80+
for (num, &value) in data.iter().enumerate().rev() {
8181
// Compute the target address: ptr + num.
8282
//
8383
// This operation may fail if it causes overflow.
@@ -87,7 +87,7 @@ impl MemoryManager {
8787
//
8888
// This enforces the write-once rule — it will fail if the cell is already
8989
// initialized with a different value.
90-
self.memory.insert(addr, value.clone())?;
90+
self.memory.insert(addr, value)?;
9191
}
9292

9393
// After writing all values, compute and return the address after the last item.
@@ -168,12 +168,12 @@ mod tests {
168168
assert_eq!(end_addr.offset, base_addr.offset + values.len());
169169

170170
// Verify that each value was inserted correctly at its expected offset.
171-
for (i, expected) in values.iter().enumerate() {
171+
for (i, &expected) in values.iter().enumerate() {
172172
let addr = MemoryAddress {
173173
segment_index: base_addr.segment_index,
174174
offset: base_addr.offset + i,
175175
};
176-
assert_eq!(manager.memory.get(addr), Some(expected.clone()));
176+
assert_eq!(manager.memory.get(addr), Some(expected));
177177
}
178178
}
179179

crates/leanVm/src/memory/mem.rs

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,43 @@ impl Memory {
127127
.and_then(|segment| segment.get(offset))
128128
.and_then(|mem_cell| mem_cell.value())
129129
}
130+
131+
/// Retrieves a fixed-size array of field elements starting from a given address.
132+
///
133+
/// This function reads `DIM` consecutive memory cells starting from `start_address`,
134+
/// expecting each cell to contain an integer (`MemoryValue::Int`). It's a generic
135+
/// way to perform the "vectorized memory access". We need to check further if it is better
136+
/// to use vectorized memory cells directly.
137+
///
138+
/// # Arguments
139+
/// * `start_address`: The `MemoryAddress` of the first element of the vector.
140+
pub(crate) fn get_array<F, const DIM: usize>(
141+
&self,
142+
start_address: MemoryAddress,
143+
) -> Result<[MemoryValue<F>; DIM], MemoryError<F>>
144+
where
145+
F: PrimeField64,
146+
{
147+
// Initialize an array to store the result.
148+
let mut result = [MemoryValue::default(); DIM];
149+
150+
// Iterate from 0 to DIM-1 to read each element of the vector.
151+
for (i, res) in result.iter_mut().enumerate().take(DIM) {
152+
// Calculate the address of the current element by adding the index `i` to the start offset.
153+
let current_addr = start_address.add_usize(i).map_err(MemoryError::Math)?;
154+
155+
// Retrieve the value from the calculated address.
156+
let mem_val = self
157+
.get(current_addr)
158+
.ok_or(MemoryError::UninitializedMemory(current_addr))?;
159+
160+
// Assign the validated field element to the result array.
161+
*res = mem_val;
162+
}
163+
164+
// Return the completed array.
165+
Ok(result)
166+
}
130167
}
131168

132169
#[cfg(test)]
@@ -156,7 +193,7 @@ mod tests {
156193
};
157194
let val = MemoryValue::<F>::Int(F::from_u64(100));
158195

159-
assert!(memory.insert(addr, val.clone()).is_ok());
196+
assert!(memory.insert(addr, val).is_ok());
160197
assert_eq!(memory.get(addr), Some(val));
161198
}
162199

@@ -169,7 +206,7 @@ mod tests {
169206
};
170207
let val = MemoryValue::<F>::Int(F::from_u64(200));
171208

172-
assert!(memory.insert(addr, val.clone()).is_ok());
209+
assert!(memory.insert(addr, val).is_ok());
173210

174211
// Verify the segment was resized and padded with NONE.
175212
assert_eq!(memory.data[0].len(), 6);
@@ -197,9 +234,9 @@ mod tests {
197234
let val = MemoryValue::<F>::Int(F::from_u64(300));
198235

199236
// First insert should succeed.
200-
assert!(memory.insert(addr, val.clone()).is_ok());
237+
assert!(memory.insert(addr, val).is_ok());
201238
// Inserting the exact same value again should also succeed.
202-
assert!(memory.insert(addr, val.clone()).is_ok());
239+
assert!(memory.insert(addr, val).is_ok());
203240

204241
assert_eq!(memory.get(addr), Some(val));
205242
}
@@ -228,10 +265,10 @@ mod tests {
228265
let val2 = MemoryValue::<F>::Int(F::from_u64(600));
229266

230267
// First write is OK.
231-
memory.insert(addr, val1.clone()).unwrap();
268+
memory.insert(addr, val1).unwrap();
232269

233270
// Second write with a different value should fail.
234-
let err = memory.insert(addr, val2.clone()).unwrap_err();
271+
let err = memory.insert(addr, val2).unwrap_err();
235272
assert_eq!(
236273
err,
237274
MemoryError::InconsistentMemory(Box::new((addr, val1, val2)))
@@ -251,7 +288,7 @@ mod tests {
251288
};
252289
let val = MemoryValue::<F>::Address(address_value);
253290

254-
assert!(memory.insert(addr_to_insert_at, val.clone()).is_ok());
291+
assert!(memory.insert(addr_to_insert_at, val).is_ok());
255292
assert_eq!(memory.get(addr_to_insert_at), Some(val));
256293
}
257294

@@ -263,7 +300,7 @@ mod tests {
263300
offset: 0,
264301
};
265302
let val = MemoryValue::<F>::Int(F::from_u64(123));
266-
memory.insert(addr, val.clone()).unwrap();
303+
memory.insert(addr, val).unwrap();
267304

268305
assert_eq!(memory.get(addr), Some(val));
269306
}
@@ -310,4 +347,62 @@ mod tests {
310347
};
311348
assert_eq!(memory.get::<F>(gap_addr), None);
312349
}
350+
351+
#[test]
352+
fn test_get_array_successful() {
353+
// Setup: Create a memory instance with one segment.
354+
let mut memory = create_memory_with_segments(1);
355+
// Define the starting address for the array read.
356+
let start_addr = MemoryAddress {
357+
segment_index: 0,
358+
offset: 2,
359+
};
360+
// Define the array of mixed `MemoryValue` types to insert.
361+
let values_to_insert = [
362+
MemoryValue::Int(F::from_u64(100)),
363+
MemoryValue::Address(MemoryAddress::new(0, 99)),
364+
MemoryValue::Int(F::from_u64(300)),
365+
];
366+
367+
// Loop through the data and insert each value at a consecutive address.
368+
for (i, &val) in values_to_insert.iter().enumerate() {
369+
memory
370+
// Calculate the address for the current item: `start_addr + i`.
371+
.insert(start_addr.add_usize::<F>(i).unwrap(), val)
372+
.unwrap();
373+
}
374+
375+
// Execute: Call `get_array` to retrieve the data.
376+
let result: Result<[MemoryValue<F>; 3], _> = memory.get_array(start_addr);
377+
// Verify: The retrieved array should be identical to the one we inserted.
378+
assert_eq!(result.unwrap(), values_to_insert);
379+
}
380+
381+
#[test]
382+
fn test_get_array_fails_on_uninitialized() {
383+
// Setup: Create a memory instance with one segment.
384+
let mut memory = create_memory_with_segments(1);
385+
// Define the starting address.
386+
let start_addr = MemoryAddress {
387+
segment_index: 0,
388+
offset: 0,
389+
};
390+
// Insert only the *first* value of the array we intend to read.
391+
memory
392+
.insert(start_addr, MemoryValue::Int(F::from_u64(100)))
393+
.unwrap();
394+
395+
// Execute: Attempt to read a 2-element array. This should fail on the second element.
396+
let result: Result<[MemoryValue<F>; 2], _> = memory.get_array(start_addr);
397+
// Verify: The operation should fail.
398+
assert!(result.is_err());
399+
// Verify: The error must be `UninitializedMemory` at the exact address of the missing element.
400+
assert_eq!(
401+
result.unwrap_err(),
402+
MemoryError::UninitializedMemory(MemoryAddress {
403+
segment_index: 0,
404+
offset: 1
405+
})
406+
);
407+
}
313408
}

crates/leanVm/src/memory/val.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ use proptest::prelude::*;
77
use super::address::MemoryAddress;
88
use crate::errors::memory::MemoryError;
99

10-
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Debug)]
10+
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Copy, Debug)]
1111
pub enum MemoryValue<F> {
1212
Address(MemoryAddress),
1313
Int(F),
1414
}
1515

16+
impl<F> Default for MemoryValue<F>
17+
where
18+
F: PrimeField64,
19+
{
20+
fn default() -> Self {
21+
Self::Int(F::default())
22+
}
23+
}
24+
1625
impl<F> MemoryValue<F>
1726
where
1827
F: PrimeField64,
@@ -58,7 +67,7 @@ where
5867
type Output = Result<Self, MemoryError<F>>;
5968

6069
fn mul(self, rhs: Self) -> Self::Output {
61-
match (self.clone(), rhs.clone()) {
70+
match (self, rhs) {
6271
// Case 1 (Success): Multiply two integers.
6372
(Self::Int(a), Self::Int(b)) => Ok(Self::Int(a * b)),
6473

@@ -266,7 +275,7 @@ mod tests {
266275
let val2 = MemoryValue::Int(F::from_u64(5));
267276

268277
// Action: Attempt to multiply them.
269-
let err = (val1.clone() * val2.clone()).unwrap_err();
278+
let err = (val1 * val2).unwrap_err();
270279

271280
// Verify: The operation should fail with the specific `InvalidMul` error,
272281
// containing the values that caused the failure.
@@ -280,7 +289,7 @@ mod tests {
280289
let val2 = MemoryValue::<F>::Address(MemoryAddress::new(2, 20));
281290

282291
// Action: Attempt to multiply them.
283-
let err = (val1.clone() * val2.clone()).unwrap_err();
292+
let err = (val1 * val2).unwrap_err();
284293

285294
// Verify: The operation should fail with the `InvalidMul` error.
286295
assert!(matches!(err, MemoryError::InvalidMul(boxed) if *boxed == (val1, val2)));

0 commit comments

Comments
 (0)