1use std::{array, cmp::max};
2
3use openvm_circuit_primitives::{
4 assert_less_than::AssertLtSubAir, var_range::SharedVariableRangeCheckerChip,
5};
6use openvm_stark_backend::p3_field::PrimeField32;
7use rustc_hash::FxHashSet;
8
9use super::{AddressMap, PagedVec, PAGE_SIZE};
10use crate::{
11 arch::MemoryConfig,
12 system::memory::{
13 adapter::{AccessAdapterInventory, AccessAdapterRecord, AccessAdapterRecordKind},
14 offline_checker::{MemoryBridge, MemoryBus},
15 MemoryAuxColsFactory, MemoryImage, RecordId, TimestampedEquipartition, TimestampedValues,
16 },
17};
18
19pub const INITIAL_TIMESTAMP: u32 = 0;
20
21#[repr(C)]
22#[derive(Clone, Default, PartialEq, Eq, Debug)]
23struct BlockData {
24 pointer: u32,
25 timestamp: u32,
26 size: usize,
27}
28
29struct BlockMap {
30 id: AddressMap<usize, PAGE_SIZE>,
32 storage: Vec<BlockData>,
34 initial_block_size: usize,
35}
36
37impl BlockMap {
38 pub fn from_mem_config(mem_config: &MemoryConfig, initial_block_size: usize) -> Self {
39 assert!(initial_block_size.is_power_of_two());
40 Self {
41 id: AddressMap::from_mem_config(mem_config),
42 storage: vec![],
43 initial_block_size,
44 }
45 }
46
47 fn initial_block_data(pointer: u32, initial_block_size: usize) -> BlockData {
48 let aligned_pointer = (pointer / initial_block_size as u32) * initial_block_size as u32;
49 BlockData {
50 pointer: aligned_pointer,
51 size: initial_block_size,
52 timestamp: INITIAL_TIMESTAMP,
53 }
54 }
55
56 pub fn get_without_adding(&self, address: &(u32, u32)) -> BlockData {
57 let idx = self.id.get(address).unwrap_or(&0);
58 if idx == &0 {
59 Self::initial_block_data(address.1, self.initial_block_size)
60 } else {
61 self.storage[idx - 1].clone()
62 }
63 }
64
65 pub fn get(&mut self, address: &(u32, u32)) -> &BlockData {
66 let (address_space, pointer) = *address;
67 let idx = self.id.get(&(address_space, pointer)).unwrap_or(&0);
68 if idx == &0 {
69 let pointer = pointer & !(self.initial_block_size as u32 - 1);
71 self.set_range(
72 &(address_space, pointer),
73 self.initial_block_size,
74 Self::initial_block_data(pointer, self.initial_block_size),
75 );
76 self.storage.last().unwrap()
77 } else {
78 &self.storage[idx - 1]
79 }
80 }
81
82 pub fn get_mut(&mut self, address: &(u32, u32)) -> &mut BlockData {
83 let (address_space, pointer) = *address;
84 let idx = self.id.get(&(address_space, pointer)).unwrap_or(&0);
85 if idx == &0 {
86 let pointer = pointer - pointer % self.initial_block_size as u32;
87 self.set_range(
88 &(address_space, pointer),
89 self.initial_block_size,
90 Self::initial_block_data(pointer, self.initial_block_size),
91 );
92 self.storage.last_mut().unwrap()
93 } else {
94 &mut self.storage[idx - 1]
95 }
96 }
97
98 pub fn set_range(&mut self, address: &(u32, u32), len: usize, block: BlockData) {
99 let (address_space, pointer) = address;
100 self.storage.push(block);
101 for i in 0..len {
102 self.id
103 .insert(&(*address_space, pointer + i as u32), self.storage.len());
104 }
105 }
106
107 pub fn items(&self) -> impl Iterator<Item = ((u32, u32), &BlockData)> + '_ {
108 self.id
109 .items()
110 .filter(|(_, idx)| *idx > 0)
111 .map(|(address, idx)| (address, &self.storage[idx - 1]))
112 }
113}
114
115#[derive(Debug, Clone, PartialEq)]
116pub struct MemoryRecord<T> {
117 pub address_space: T,
118 pub pointer: T,
119 pub timestamp: u32,
120 pub prev_timestamp: u32,
121 data: Vec<T>,
122 prev_data: Option<Vec<T>>,
124}
125
126impl<T> MemoryRecord<T> {
127 pub fn data_slice(&self) -> &[T] {
128 self.data.as_slice()
129 }
130
131 pub fn prev_data_slice(&self) -> Option<&[T]> {
132 self.prev_data.as_deref()
133 }
134}
135
136impl<T: Copy> MemoryRecord<T> {
137 pub fn data_at(&self, index: usize) -> T {
138 self.data[index]
139 }
140}
141
142pub struct OfflineMemory<F> {
143 block_data: BlockMap,
144 data: Vec<PagedVec<F, PAGE_SIZE>>,
145 as_offset: u32,
146 timestamp: u32,
147 timestamp_max_bits: usize,
148
149 memory_bus: MemoryBus,
150 range_checker: SharedVariableRangeCheckerChip,
151
152 log: Vec<Option<MemoryRecord<F>>>,
153}
154
155impl<F: PrimeField32> OfflineMemory<F> {
156 pub fn new(
160 initial_memory: MemoryImage<F>,
161 initial_block_size: usize,
162 memory_bus: MemoryBus,
163 range_checker: SharedVariableRangeCheckerChip,
164 config: MemoryConfig,
165 ) -> Self {
166 assert_eq!(initial_memory.as_offset, config.as_offset);
167 Self {
168 block_data: BlockMap::from_mem_config(&config, initial_block_size),
169 data: initial_memory.paged_vecs,
170 as_offset: config.as_offset,
171 timestamp: INITIAL_TIMESTAMP + 1,
172 timestamp_max_bits: config.clk_max_bits,
173 memory_bus,
174 range_checker,
175 log: vec![],
176 }
177 }
178
179 pub fn set_initial_memory(&mut self, initial_memory: MemoryImage<F>, config: MemoryConfig) {
180 assert_eq!(self.timestamp, INITIAL_TIMESTAMP + 1);
181 assert_eq!(initial_memory.as_offset, config.as_offset);
182 self.as_offset = config.as_offset;
183 self.data = initial_memory.paged_vecs;
184 }
185
186 pub(super) fn set_log_capacity(&mut self, access_capacity: usize) {
187 assert!(self.log.is_empty());
188 self.log = Vec::with_capacity(access_capacity);
189 }
190
191 pub fn memory_bridge(&self) -> MemoryBridge {
192 MemoryBridge::new(
193 self.memory_bus,
194 self.timestamp_max_bits,
195 self.range_checker.bus(),
196 )
197 }
198
199 pub fn timestamp(&self) -> u32 {
200 self.timestamp
201 }
202
203 pub fn increment_timestamp(&mut self) {
205 self.increment_timestamp_by(1)
206 }
207
208 pub fn increment_timestamp_by(&mut self, delta: u32) {
210 self.log.push(None);
211 self.timestamp += delta;
212 }
213
214 pub fn write(
216 &mut self,
217 address_space: u32,
218 pointer: u32,
219 values: Vec<F>,
220 records: &mut AccessAdapterInventory<F>,
221 ) {
222 let len = values.len();
223 assert!(len.is_power_of_two());
224 assert_ne!(address_space, 0);
225
226 let prev_timestamp = self.access_updating_timestamp(address_space, pointer, len, records);
227
228 debug_assert!(prev_timestamp < self.timestamp);
229
230 let pointer = pointer as usize;
231 let prev_data = self.data[(address_space - self.as_offset) as usize]
232 .set_range(pointer..pointer + len, &values);
233
234 let record = MemoryRecord {
235 address_space: F::from_canonical_u32(address_space),
236 pointer: F::from_canonical_usize(pointer),
237 timestamp: self.timestamp,
238 prev_timestamp,
239 data: values,
240 prev_data: Some(prev_data),
241 };
242 self.log.push(Some(record));
243 self.timestamp += 1;
244 }
245
246 pub fn read(
248 &mut self,
249 address_space: u32,
250 pointer: u32,
251 len: usize,
252 adapter_records: &mut AccessAdapterInventory<F>,
253 ) {
254 assert!(len.is_power_of_two());
255 if address_space == 0 {
256 let pointer = F::from_canonical_u32(pointer);
257 self.log.push(Some(MemoryRecord {
258 address_space: F::ZERO,
259 pointer,
260 timestamp: self.timestamp,
261 prev_timestamp: 0,
262 data: vec![pointer],
263 prev_data: None,
264 }));
265 self.timestamp += 1;
266 return;
267 }
268
269 let prev_timestamp =
270 self.access_updating_timestamp(address_space, pointer, len, adapter_records);
271
272 debug_assert!(prev_timestamp < self.timestamp);
273
274 let values = self.range_vec(address_space, pointer, len);
275
276 self.log.push(Some(MemoryRecord {
277 address_space: F::from_canonical_u32(address_space),
278 pointer: F::from_canonical_u32(pointer),
279 timestamp: self.timestamp,
280 prev_timestamp,
281 data: values,
282 prev_data: None,
283 }));
284 self.timestamp += 1;
285 }
286
287 pub fn record_by_id(&self, id: RecordId) -> &MemoryRecord<F> {
288 self.log[id.0].as_ref().unwrap()
289 }
290
291 pub fn finalize<const N: usize>(
292 &mut self,
293 adapter_records: &mut AccessAdapterInventory<F>,
294 ) -> TimestampedEquipartition<F, N> {
295 let to_access: FxHashSet<_> = self
298 .block_data
299 .items()
300 .map(|((address_space, pointer), _)| (address_space, (pointer / N as u32) * N as u32))
301 .collect();
302
303 for &(address_space, pointer) in to_access.iter() {
304 let block = self.block_data.get(&(address_space, pointer));
305 if block.pointer != pointer || block.size != N {
306 self.access(address_space, pointer, N, adapter_records);
307 }
308 }
309
310 let mut equipartition = TimestampedEquipartition::<F, N>::new();
311 for (address_space, pointer) in to_access {
312 let block = self.block_data.get(&(address_space, pointer));
313
314 debug_assert_eq!(block.pointer % N as u32, 0);
315 debug_assert_eq!(block.size, N);
316
317 equipartition.insert(
318 (address_space, pointer / N as u32),
319 TimestampedValues {
320 timestamp: block.timestamp,
321 values: self.range_array::<N>(address_space, pointer),
322 },
323 );
324 }
325 equipartition
326 }
327
328 fn split_to_make_boundary(
330 &mut self,
331 address_space: u32,
332 query: u32,
333 records: &mut AccessAdapterInventory<F>,
334 ) {
335 let lim = (self.data[(address_space - self.as_offset) as usize].memory_size()) as u32;
336 if query == lim {
337 return;
338 }
339 assert!(query < lim);
340 let original_block = self.block_containing(address_space, query);
341 if original_block.pointer == query {
342 return;
343 }
344
345 let data = self.range_vec(address_space, original_block.pointer, original_block.size);
346
347 let timestamp = original_block.timestamp;
348
349 let mut cur_ptr = original_block.pointer;
350 let mut cur_size = original_block.size;
351 while cur_size > 0 {
352 records.add_record(AccessAdapterRecord {
354 timestamp,
355 address_space: F::from_canonical_u32(address_space),
356 start_index: F::from_canonical_u32(cur_ptr),
357 data: data[(cur_ptr - original_block.pointer) as usize
358 ..(cur_ptr - original_block.pointer) as usize + cur_size]
359 .to_vec(),
360 kind: AccessAdapterRecordKind::Split,
361 });
362
363 let half_size = cur_size / 2;
364 let half_size_u32 = half_size as u32;
365 let mid_ptr = cur_ptr + half_size_u32;
366
367 if query <= mid_ptr {
368 let block = BlockData {
370 pointer: mid_ptr,
371 size: half_size,
372 timestamp,
373 };
374 self.block_data
375 .set_range(&(address_space, mid_ptr), half_size, block);
376 }
377 if query >= cur_ptr + half_size_u32 {
378 let block = BlockData {
380 pointer: cur_ptr,
381 size: half_size,
382 timestamp,
383 };
384 self.block_data
385 .set_range(&(address_space, cur_ptr), half_size, block);
386 }
387 if mid_ptr <= query {
388 cur_ptr = mid_ptr;
389 }
390 if cur_ptr == query {
391 break;
392 }
393 cur_size = half_size;
394 }
395 }
396
397 fn access_updating_timestamp(
398 &mut self,
399 address_space: u32,
400 pointer: u32,
401 size: usize,
402 records: &mut AccessAdapterInventory<F>,
403 ) -> u32 {
404 self.access(address_space, pointer, size, records);
405
406 let mut prev_timestamp = None;
407
408 let mut i = 0;
409 while i < size as u32 {
410 let block = self.block_data.get_mut(&(address_space, pointer + i));
411 debug_assert!(i == 0 || prev_timestamp == Some(block.timestamp));
412 prev_timestamp = Some(block.timestamp);
413 block.timestamp = self.timestamp;
414 i = block.pointer + block.size as u32;
415 }
416 prev_timestamp.unwrap()
417 }
418
419 fn access(
420 &mut self,
421 address_space: u32,
422 pointer: u32,
423 size: usize,
424 records: &mut AccessAdapterInventory<F>,
425 ) {
426 self.split_to_make_boundary(address_space, pointer, records);
427 self.split_to_make_boundary(address_space, pointer + size as u32, records);
428
429 let block_data = self.block_containing(address_space, pointer);
430
431 if block_data.pointer == pointer && block_data.size == size {
432 return;
433 }
434 assert!(size > 1);
435
436 let half_size = size / 2;
438 self.access(address_space, pointer, half_size, records);
439 self.access(
440 address_space,
441 pointer + half_size as u32,
442 half_size,
443 records,
444 );
445
446 self.merge_block_with_next(address_space, pointer, records);
447 }
448
449 fn merge_block_with_next(
454 &mut self,
455 address_space: u32,
456 pointer: u32,
457 records: &mut AccessAdapterInventory<F>,
458 ) {
459 let left_block = self.block_data.get(&(address_space, pointer));
460
461 let left_timestamp = left_block.timestamp;
462 let size = left_block.size;
463
464 let right_timestamp = self
465 .block_data
466 .get(&(address_space, pointer + size as u32))
467 .timestamp;
468
469 let timestamp = max(left_timestamp, right_timestamp);
470 self.block_data.set_range(
471 &(address_space, pointer),
472 2 * size,
473 BlockData {
474 pointer,
475 size: 2 * size,
476 timestamp,
477 },
478 );
479 records.add_record(AccessAdapterRecord {
480 timestamp,
481 address_space: F::from_canonical_u32(address_space),
482 start_index: F::from_canonical_u32(pointer),
483 data: self.range_vec(address_space, pointer, 2 * size),
484 kind: AccessAdapterRecordKind::Merge {
485 left_timestamp,
486 right_timestamp,
487 },
488 });
489 }
490
491 fn block_containing(&mut self, address_space: u32, pointer: u32) -> BlockData {
492 self.block_data
493 .get_without_adding(&(address_space, pointer))
494 }
495
496 pub fn get(&self, address_space: u32, pointer: u32) -> F {
497 self.data[(address_space - self.as_offset) as usize]
498 .get(pointer as usize)
499 .cloned()
500 .unwrap_or_default()
501 }
502
503 fn range_array<const N: usize>(&self, address_space: u32, pointer: u32) -> [F; N] {
504 array::from_fn(|i| self.get(address_space, pointer + i as u32))
505 }
506
507 fn range_vec(&self, address_space: u32, pointer: u32, len: usize) -> Vec<F> {
508 let pointer = pointer as usize;
509 self.data[(address_space - self.as_offset) as usize].range_vec(pointer..pointer + len)
510 }
511
512 pub fn aux_cols_factory(&self) -> MemoryAuxColsFactory<F> {
513 let range_bus = self.range_checker.bus();
514 MemoryAuxColsFactory {
515 range_checker: self.range_checker.clone(),
516 timestamp_lt_air: AssertLtSubAir::new(range_bus, self.timestamp_max_bits),
517 _marker: Default::default(),
518 }
519 }
520
521 #[cfg(test)]
523 fn last_record(&self) -> &MemoryRecord<F> {
524 self.log.last().unwrap().as_ref().unwrap()
525 }
526}
527
528#[cfg(test)]
529mod tests {
530 use openvm_circuit_primitives::var_range::{
531 SharedVariableRangeCheckerChip, VariableRangeCheckerBus,
532 };
533 use openvm_stark_backend::p3_field::FieldAlgebra;
534 use openvm_stark_sdk::p3_baby_bear::BabyBear;
535
536 use super::{BlockData, MemoryRecord, OfflineMemory};
537 use crate::{
538 arch::MemoryConfig,
539 system::memory::{
540 adapter::{AccessAdapterInventory, AccessAdapterRecord, AccessAdapterRecordKind},
541 offline_checker::MemoryBus,
542 paged_vec::AddressMap,
543 MemoryImage, TimestampedValues,
544 },
545 };
546
547 macro_rules! bb {
548 ($x:expr) => {
549 BabyBear::from_canonical_u32($x)
550 };
551 }
552
553 macro_rules! bba {
554 [$($x:expr),*] => {
555 [$(BabyBear::from_canonical_u32($x)),*]
556 }
557 }
558
559 macro_rules! bbvec {
560 [$($x:expr),*] => {
561 vec![$(BabyBear::from_canonical_u32($x)),*]
562 }
563 }
564
565 fn setup_test(
566 initial_memory: MemoryImage<BabyBear>,
567 initial_block_size: usize,
568 ) -> (OfflineMemory<BabyBear>, AccessAdapterInventory<BabyBear>) {
569 let memory_bus = MemoryBus::new(0);
570 let range_checker =
571 SharedVariableRangeCheckerChip::new(VariableRangeCheckerBus::new(1, 29));
572 let mem_config = MemoryConfig {
573 as_offset: initial_memory.as_offset,
574 ..Default::default()
575 };
576 let memory = OfflineMemory::new(
577 initial_memory,
578 initial_block_size,
579 memory_bus,
580 range_checker.clone(),
581 mem_config,
582 );
583 let access_adapter_inventory = AccessAdapterInventory::new(
584 range_checker,
585 memory_bus,
586 mem_config.clk_max_bits,
587 mem_config.max_access_adapter_n,
588 );
589 (memory, access_adapter_inventory)
590 }
591
592 #[test]
593 fn test_partition() {
594 let initial_memory = AddressMap::new(0, 1, 16);
595 let (mut memory, _) = setup_test(initial_memory, 8);
596 assert_eq!(
597 memory.block_containing(1, 13),
598 BlockData {
599 pointer: 8,
600 size: 8,
601 timestamp: 0,
602 }
603 );
604
605 assert_eq!(
606 memory.block_containing(1, 8),
607 BlockData {
608 pointer: 8,
609 size: 8,
610 timestamp: 0,
611 }
612 );
613
614 assert_eq!(
615 memory.block_containing(1, 15),
616 BlockData {
617 pointer: 8,
618 size: 8,
619 timestamp: 0,
620 }
621 );
622
623 assert_eq!(
624 memory.block_containing(1, 16),
625 BlockData {
626 pointer: 16,
627 size: 8,
628 timestamp: 0,
629 }
630 );
631 }
632
633 #[test]
634 fn test_write_read_initial_block_len_1() {
635 let (mut memory, mut access_adapters) = setup_test(MemoryImage::default(), 1);
636 let address_space = 1;
637
638 memory.write(address_space, 0, bbvec![1, 2, 3, 4], &mut access_adapters);
639
640 memory.read(address_space, 0, 2, &mut access_adapters);
641 let read_record = memory.last_record();
642 assert_eq!(read_record.data, bba![1, 2]);
643
644 memory.write(address_space, 2, bbvec![100], &mut access_adapters);
645
646 memory.read(address_space, 0, 4, &mut access_adapters);
647 let read_record = memory.last_record();
648 assert_eq!(read_record.data, bba![1, 2, 100, 4]);
649 }
650
651 #[test]
652 fn test_records_initial_block_len_1() {
653 let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 1);
654
655 memory.write(1, 0, bbvec![1, 2, 3, 4], &mut adapter_records);
656
657 assert_eq!(
659 adapter_records.records_for_n(2)[0],
660 AccessAdapterRecord {
661 timestamp: 0,
662 address_space: bb!(1),
663 start_index: bb!(0),
664 data: bbvec![0, 0],
665 kind: AccessAdapterRecordKind::Merge {
666 left_timestamp: 0,
667 right_timestamp: 0,
668 },
669 }
670 );
671 assert_eq!(
673 adapter_records.records_for_n(2)[1],
674 AccessAdapterRecord {
675 timestamp: 0,
676 address_space: bb!(1),
677 start_index: bb!(2),
678 data: bbvec![0, 0],
679 kind: AccessAdapterRecordKind::Merge {
680 left_timestamp: 0,
681 right_timestamp: 0,
682 },
683 }
684 );
685 assert_eq!(
687 adapter_records.records_for_n(4)[0],
688 AccessAdapterRecord {
689 timestamp: 0,
690 address_space: bb!(1),
691 start_index: bb!(0),
692 data: bbvec![0, 0, 0, 0],
693 kind: AccessAdapterRecordKind::Merge {
694 left_timestamp: 0,
695 right_timestamp: 0,
696 },
697 }
698 );
699 let write_record = memory.last_record();
701 assert_eq!(
702 write_record,
703 &MemoryRecord {
704 address_space: bb!(1),
705 pointer: bb!(0),
706 timestamp: 1,
707 prev_timestamp: 0,
708 data: bbvec![1, 2, 3, 4],
709 prev_data: Some(bbvec![0, 0, 0, 0]),
710 }
711 );
712 assert_eq!(memory.timestamp(), 2);
713 assert_eq!(adapter_records.total_records(), 3);
714
715 memory.read(1, 0, 4, &mut adapter_records);
716 let read_record = memory.last_record();
717 assert_eq!(adapter_records.total_records(), 3);
719 assert_eq!(
720 read_record,
721 &MemoryRecord {
722 address_space: bb!(1),
723 pointer: bb!(0),
724 timestamp: 2,
725 prev_timestamp: 1,
726 data: bbvec![1, 2, 3, 4],
727 prev_data: None,
728 }
729 );
730 assert_eq!(memory.timestamp(), 3);
731
732 memory.write(1, 0, bbvec![10, 11], &mut adapter_records);
733 let write_record = memory.last_record();
734 assert_eq!(adapter_records.total_records(), 4);
736 assert_eq!(
737 adapter_records.records_for_n(4).last().unwrap(),
738 &AccessAdapterRecord {
739 timestamp: 2,
740 address_space: bb!(1),
741 start_index: bb!(0),
742 data: bbvec![1, 2, 3, 4],
743 kind: AccessAdapterRecordKind::Split,
744 }
745 );
746
747 assert_eq!(
749 write_record,
750 &MemoryRecord {
751 address_space: bb!(1),
752 pointer: bb!(0),
753 timestamp: 3,
754 prev_timestamp: 2,
755 data: bbvec![10, 11],
756 prev_data: Some(bbvec![1, 2]),
757 }
758 );
759
760 memory.read(1, 0, 4, &mut adapter_records);
761 let read_record = memory.last_record();
762 assert_eq!(adapter_records.total_records(), 5);
763 assert_eq!(
764 adapter_records.records_for_n(4).last().unwrap(),
765 &AccessAdapterRecord {
766 timestamp: 3,
767 address_space: bb!(1),
768 start_index: bb!(0),
769 data: bbvec![10, 11, 3, 4],
770 kind: AccessAdapterRecordKind::Merge {
771 left_timestamp: 3,
772 right_timestamp: 2
773 },
774 }
775 );
776 assert_eq!(
778 read_record,
779 &MemoryRecord {
780 address_space: bb!(1),
781 pointer: bb!(0),
782 timestamp: 4,
783 prev_timestamp: 3,
784 data: bbvec![10, 11, 3, 4],
785 prev_data: None,
786 }
787 );
788 }
789
790 #[test]
791 fn test_records_initial_block_len_8() {
792 let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 8);
793
794 memory.write(1, 0, bbvec![1, 2, 3, 4], &mut adapter_records);
795 let write_record = memory.last_record();
796
797 assert_eq!(adapter_records.total_records(), 1);
799 assert_eq!(
800 adapter_records.records_for_n(8).last().unwrap(),
801 &AccessAdapterRecord {
802 timestamp: 0,
803 address_space: bb!(1),
804 start_index: bb!(0),
805 data: bbvec![0, 0, 0, 0, 0, 0, 0, 0],
806 kind: AccessAdapterRecordKind::Split,
807 }
808 );
809 assert_eq!(
811 write_record,
812 &MemoryRecord {
813 address_space: bb!(1),
814 pointer: bb!(0),
815 timestamp: 1,
816 prev_timestamp: 0,
817 data: bbvec![1, 2, 3, 4],
818 prev_data: Some(bbvec![0, 0, 0, 0]),
819 }
820 );
821 assert_eq!(memory.timestamp(), 2);
822
823 memory.read(1, 0, 4, &mut adapter_records);
824 let read_record = memory.last_record();
825 assert_eq!(adapter_records.total_records(), 1);
827 assert_eq!(
828 read_record,
829 &MemoryRecord {
830 address_space: bb!(1),
831 pointer: bb!(0),
832 timestamp: 2,
833 prev_timestamp: 1,
834 data: bbvec![1, 2, 3, 4],
835 prev_data: None,
836 }
837 );
838 assert_eq!(memory.timestamp(), 3);
839
840 memory.write(1, 0, bbvec![10, 11], &mut adapter_records);
841 let write_record = memory.last_record();
842 assert_eq!(adapter_records.total_records(), 2);
844 assert_eq!(
845 adapter_records.records_for_n(4).last().unwrap(),
846 &AccessAdapterRecord {
847 timestamp: 2,
848 address_space: bb!(1),
849 start_index: bb!(0),
850 data: bbvec![1, 2, 3, 4],
851 kind: AccessAdapterRecordKind::Split,
852 }
853 );
854
855 assert_eq!(
857 write_record,
858 &MemoryRecord {
859 address_space: bb!(1),
860 pointer: bb!(0),
861 timestamp: 3,
862 prev_timestamp: 2,
863 data: bbvec![10, 11],
864 prev_data: Some(bbvec![1, 2]),
865 }
866 );
867
868 memory.read(1, 0, 4, &mut adapter_records);
869 let read_record = memory.last_record();
870 assert_eq!(adapter_records.total_records(), 3);
871 assert_eq!(
872 adapter_records.records_for_n(4).last().unwrap(),
873 &AccessAdapterRecord {
874 timestamp: 3,
875 address_space: bb!(1),
876 start_index: bb!(0),
877 data: bbvec![10, 11, 3, 4],
878 kind: AccessAdapterRecordKind::Merge {
879 left_timestamp: 3,
880 right_timestamp: 2
881 },
882 }
883 );
884 assert_eq!(
886 read_record,
887 &MemoryRecord {
888 address_space: bb!(1),
889 pointer: bb!(0),
890 timestamp: 4,
891 prev_timestamp: 3,
892 data: bbvec![10, 11, 3, 4],
893 prev_data: None,
894 }
895 );
896 }
897
898 #[test]
899 fn test_get_initial_block_len_1() {
900 let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 1);
901
902 memory.write(2, 0, bbvec![4, 3, 2, 1], &mut adapter_records);
903
904 assert_eq!(memory.get(2, 0), BabyBear::from_canonical_u32(4));
905 assert_eq!(memory.get(2, 1), BabyBear::from_canonical_u32(3));
906 assert_eq!(memory.get(2, 2), BabyBear::from_canonical_u32(2));
907 assert_eq!(memory.get(2, 3), BabyBear::from_canonical_u32(1));
908 assert_eq!(memory.get(2, 5), BabyBear::ZERO);
909
910 assert_eq!(memory.get(1, 0), BabyBear::ZERO);
911 }
912
913 #[test]
914 fn test_get_initial_block_len_8() {
915 let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 8);
916
917 memory.write(2, 0, bbvec![4, 3, 2, 1], &mut adapter_records);
918
919 assert_eq!(memory.get(2, 0), BabyBear::from_canonical_u32(4));
920 assert_eq!(memory.get(2, 1), BabyBear::from_canonical_u32(3));
921 assert_eq!(memory.get(2, 2), BabyBear::from_canonical_u32(2));
922 assert_eq!(memory.get(2, 3), BabyBear::from_canonical_u32(1));
923 assert_eq!(memory.get(2, 5), BabyBear::ZERO);
924 assert_eq!(memory.get(2, 9), BabyBear::ZERO);
925 assert_eq!(memory.get(1, 0), BabyBear::ZERO);
926 }
927
928 #[test]
929 fn test_finalize_empty() {
930 let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 4);
931
932 let memory = memory.finalize::<4>(&mut adapter_records);
933 assert_eq!(memory.len(), 0);
934 assert_eq!(adapter_records.total_records(), 0);
935 }
936
937 #[test]
938 fn test_finalize_block_len_8() {
939 let (mut memory, mut adapter_records) = setup_test(MemoryImage::default(), 8);
940 memory.write(1, 0, bbvec![1, 2, 3, 4], &mut adapter_records);
942
943 memory.write(
945 1,
946 16,
947 bbvec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
948 &mut adapter_records,
949 );
950
951 memory.write(2, 64, bbvec![8, 7, 6, 5, 4, 3, 2, 1], &mut adapter_records);
953
954 let num_records_before_finalize = adapter_records.total_records();
955
956 let final_memory = memory.finalize::<8>(&mut adapter_records);
958 assert_eq!(final_memory.len(), 4);
959 assert_eq!(
960 final_memory.get(&(1, 0)),
961 Some(&TimestampedValues {
962 values: bba![1, 2, 3, 4, 0, 0, 0, 0],
963 timestamp: 1,
964 })
965 );
966 assert_eq!(
968 final_memory.get(&(1, 2)),
969 Some(&TimestampedValues {
970 values: bba![1, 1, 1, 1, 1, 1, 1, 1],
971 timestamp: 2,
972 })
973 );
974 assert_eq!(
976 final_memory.get(&(1, 3)),
977 Some(&TimestampedValues {
978 values: bba![1, 1, 1, 1, 1, 1, 1, 1],
979 timestamp: 2,
980 })
981 );
982 assert_eq!(
984 final_memory.get(&(2, 8)),
985 Some(&TimestampedValues {
986 values: bba![8, 7, 6, 5, 4, 3, 2, 1],
987 timestamp: 3,
988 })
989 );
990
991 assert_eq!(
993 adapter_records.total_records() - num_records_before_finalize,
994 2
995 );
996 }
997
998 #[test]
999 fn test_write_read_initial_block_len_8_initial_memory() {
1000 type F = BabyBear;
1001
1002 let mut initial_memory = MemoryImage::default();
1004 for i in 0..8 {
1005 initial_memory.insert(&(1, i), F::from_canonical_u32(i + 1));
1006 initial_memory.insert(&(1, 16 + i), F::from_canonical_u32(i + 1));
1007 }
1008
1009 let (mut memory, mut adapter_records) = setup_test(initial_memory, 8);
1010
1011 memory.read(1, 0, 8, &mut adapter_records);
1013 let initial_read_record_0 = memory.last_record();
1014 assert_eq!(initial_read_record_0.data, bbvec![1, 2, 3, 4, 5, 6, 7, 8]);
1015
1016 memory.read(1, 16, 8, &mut adapter_records);
1018 let initial_read_record_2 = memory.last_record();
1019 assert_eq!(initial_read_record_2.data, bbvec![1, 2, 3, 4, 5, 6, 7, 8]);
1020
1021 memory.write(1, 0, bbvec![9, 9, 9, 9], &mut adapter_records);
1023 memory.read(1, 0, 2, &mut adapter_records);
1024 let partial_read_record = memory.last_record();
1025 assert_eq!(partial_read_record.data, bbvec![9, 9]);
1026
1027 memory.read(1, 0, 8, &mut adapter_records);
1028 let full_read_record_0 = memory.last_record();
1029 assert_eq!(full_read_record_0.data, bbvec![9, 9, 9, 9, 5, 6, 7, 8]);
1030
1031 memory.write(1, 2, bbvec![100], &mut adapter_records);
1033 memory.read(1, 1, 4, &mut adapter_records);
1034 let read_record_4 = memory.last_record();
1035 assert_eq!(read_record_4.data, bbvec![9, 100, 9, 5]);
1036
1037 memory.read(1, 2, 8, &mut adapter_records);
1038 let full_read_record_2 = memory.last_record();
1039 assert_eq!(full_read_record_2.data, bba![100, 9, 5, 6, 7, 8, 0, 0]);
1040
1041 memory.write(1, 23, bbvec![77], &mut adapter_records);
1043 memory.read(1, 23, 2, &mut adapter_records);
1044 let boundary_read_record = memory.last_record();
1045 assert_eq!(boundary_read_record.data, bba![77, 0]); memory.read(1, 10, 4, &mut adapter_records);
1049 let default_read_record = memory.last_record();
1050 assert_eq!(default_read_record.data, bba![0, 0, 0, 0]);
1051
1052 memory.read(1, 100, 4, &mut adapter_records);
1053 let default_read_record = memory.last_record();
1054 assert_eq!(default_read_record.data, bba![0, 0, 0, 0]);
1055
1056 memory.write(
1058 1,
1059 16,
1060 bbvec![50, 50, 50, 50, 50, 50, 50, 50],
1061 &mut adapter_records,
1062 );
1063 memory.read(1, 16, 8, &mut adapter_records);
1064 let overwrite_read_record = memory.last_record();
1065 assert_eq!(
1066 overwrite_read_record.data,
1067 bba![50, 50, 50, 50, 50, 50, 50, 50]
1068 ); }
1070}