1use std::{
2 borrow::BorrowMut,
3 io::Cursor,
4 marker::PhantomData,
5 ptr::{copy_nonoverlapping, slice_from_raw_parts_mut},
6};
7
8use openvm_circuit_primitives::utils::next_power_of_two_or_zero;
9use openvm_stark_backend::{
10 p3_field::{Field, PrimeField32},
11 p3_matrix::dense::RowMajorMatrix,
12};
13
14pub trait Arena {
15 fn with_capacity(height: usize, width: usize) -> Self;
17
18 fn is_empty(&self) -> bool;
19
20 #[cfg(feature = "metrics")]
25 fn current_trace_height(&self) -> usize {
26 0
27 }
28
29 #[cfg(feature = "metrics")]
30 fn allocated_bytes(&self) -> Option<usize> {
31 None
32 }
33}
34
35pub trait RecordArena<'a, Layout, RecordMut> {
39 fn alloc(&'a mut self, layout: Layout) -> RecordMut;
43}
44
45pub trait RowMajorMatrixArena<F>: Arena {
47 fn set_capacity(&mut self, trace_height: usize);
49 fn width(&self) -> usize;
50 fn trace_offset(&self) -> usize;
51 fn into_matrix(self) -> RowMajorMatrix<F>;
52}
53
54pub trait SizedRecord<Layout> {
57 fn size(layout: &Layout) -> usize;
60 fn alignment(layout: &Layout) -> usize;
63}
64
65impl<Layout, Record> SizedRecord<Layout> for &mut Record
66where
67 Record: Sized,
68{
69 fn size(_layout: &Layout) -> usize {
70 size_of::<Record>()
71 }
72
73 fn alignment(_layout: &Layout) -> usize {
74 align_of::<Record>()
75 }
76}
77
78#[derive(Default)]
81pub struct MatrixRecordArena<F> {
82 pub trace_buffer: Vec<F>,
83 pub width: usize,
84 pub trace_offset: usize,
85 pub(super) allow_truncate: bool,
90}
91
92impl<F: Field> MatrixRecordArena<F> {
93 pub fn alloc_single_row(&mut self) -> &mut [u8] {
94 self.alloc_buffer(1)
95 }
96
97 pub fn alloc_buffer(&mut self, num_rows: usize) -> &mut [u8] {
98 let start = self.trace_offset;
99 self.trace_offset += num_rows * self.width;
100 let row_slice = &mut self.trace_buffer[start..self.trace_offset];
101 let size = size_of_val(row_slice);
102 let ptr = row_slice as *mut [F] as *mut u8;
103 unsafe { &mut *std::ptr::slice_from_raw_parts_mut(ptr, size) }
108 }
109
110 pub fn force_matrix_dimensions(&mut self) {
111 self.allow_truncate = false;
112 }
113}
114
115impl<F: Field> Arena for MatrixRecordArena<F> {
116 fn with_capacity(height: usize, width: usize) -> Self {
117 let height = next_power_of_two_or_zero(height);
118 let trace_buffer = F::zero_vec(height * width);
119 Self {
120 trace_buffer,
121 width,
122 trace_offset: 0,
123 allow_truncate: true,
124 }
125 }
126
127 fn is_empty(&self) -> bool {
128 self.trace_offset == 0
129 }
130
131 #[cfg(feature = "metrics")]
132 fn current_trace_height(&self) -> usize {
133 self.trace_offset / self.width
134 }
135}
136
137impl<F: Field> RowMajorMatrixArena<F> for MatrixRecordArena<F> {
138 fn set_capacity(&mut self, trace_height: usize) {
139 let size = trace_height * self.width;
140 self.trace_buffer.resize(size, F::ZERO);
142 }
143
144 fn width(&self) -> usize {
145 self.width
146 }
147
148 fn trace_offset(&self) -> usize {
149 self.trace_offset
150 }
151
152 fn into_matrix(mut self) -> RowMajorMatrix<F> {
153 let width = self.width();
154 assert_eq!(self.trace_offset() % width, 0);
155 let rows_used = self.trace_offset() / width;
156 let height = next_power_of_two_or_zero(rows_used);
157 assert!(height.checked_mul(width).unwrap() <= self.trace_buffer.len());
159 if self.allow_truncate {
160 self.trace_buffer.truncate(height * width);
161 } else {
162 assert_eq!(self.trace_buffer.len() % width, 0);
163 let height = self.trace_buffer.len() / width;
164 assert!(height.is_power_of_two() || height == 0);
165 }
166 RowMajorMatrix::new(self.trace_buffer, self.width)
167 }
168}
169
170pub struct DenseRecordArena {
171 pub records_buffer: Cursor<Vec<u8>>,
172}
173
174const MAX_ALIGNMENT: usize = 32;
175
176impl DenseRecordArena {
177 pub fn with_byte_capacity(size_bytes: usize) -> Self {
179 let buffer = vec![0; size_bytes + MAX_ALIGNMENT];
180 let offset = (MAX_ALIGNMENT - (buffer.as_ptr() as usize % MAX_ALIGNMENT)) % MAX_ALIGNMENT;
181 let mut cursor = Cursor::new(buffer);
182 cursor.set_position(offset as u64);
183 Self {
184 records_buffer: cursor,
185 }
186 }
187
188 pub fn set_byte_capacity(&mut self, size_bytes: usize) {
189 let buffer = vec![0; size_bytes + MAX_ALIGNMENT];
190 let offset = (MAX_ALIGNMENT - (buffer.as_ptr() as usize % MAX_ALIGNMENT)) % MAX_ALIGNMENT;
191 let mut cursor = Cursor::new(buffer);
192 cursor.set_position(offset as u64);
193 self.records_buffer = cursor;
194 }
195
196 pub fn capacity(&self) -> usize {
200 self.records_buffer.get_ref().len()
201 }
202
203 pub fn alloc_bytes<'a>(&mut self, count: usize) -> &'a mut [u8] {
205 let begin = self.records_buffer.position();
206 debug_assert!(
207 begin as usize + count <= self.records_buffer.get_ref().len(),
208 "failed to allocate {count} bytes from {begin} when the capacity is {}",
209 self.records_buffer.get_ref().len()
210 );
211 self.records_buffer.set_position(begin + count as u64);
212 unsafe {
216 std::slice::from_raw_parts_mut(
217 self.records_buffer
218 .get_mut()
219 .as_mut_ptr()
220 .add(begin as usize),
221 count,
222 )
223 }
224 }
225
226 pub fn allocated(&self) -> &[u8] {
227 let size = self.records_buffer.position() as usize;
228 let offset = (MAX_ALIGNMENT
229 - (self.records_buffer.get_ref().as_ptr() as usize % MAX_ALIGNMENT))
230 % MAX_ALIGNMENT;
231 &self.records_buffer.get_ref()[offset..size]
232 }
233
234 pub fn allocated_mut(&mut self) -> &mut [u8] {
235 let size = self.records_buffer.position() as usize;
236 let offset = (MAX_ALIGNMENT
237 - (self.records_buffer.get_ref().as_ptr() as usize % MAX_ALIGNMENT))
238 % MAX_ALIGNMENT;
239 &mut self.records_buffer.get_mut()[offset..size]
240 }
241
242 pub fn align_to(&mut self, alignment: usize) {
243 debug_assert!(MAX_ALIGNMENT.is_multiple_of(alignment));
244 let offset =
245 (alignment - (self.records_buffer.get_ref().as_ptr() as usize % alignment)) % alignment;
246 self.records_buffer.set_position(offset as u64);
247 }
248
249 pub fn get_record_seeker<R, L>(&mut self) -> RecordSeeker<'_, DenseRecordArena, R, L> {
251 RecordSeeker::new(self.allocated_mut())
252 }
253}
254
255impl Arena for DenseRecordArena {
256 fn with_capacity(height: usize, width: usize) -> Self {
258 let size_bytes = height * (width * size_of::<u32>());
259 Self::with_byte_capacity(size_bytes)
260 }
261
262 fn is_empty(&self) -> bool {
263 self.allocated().is_empty()
264 }
265
266 #[cfg(feature = "metrics")]
267 fn allocated_bytes(&self) -> Option<usize> {
268 Some(self.allocated().len())
269 }
270}
271
272pub unsafe fn get_record_from_slice<'a, T, F, L>(slice: &mut &'a mut [F], layout: L) -> T
280where
281 [u8]: CustomBorrow<'a, T, L>,
282{
283 let record_buffer =
285 &mut *slice_from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of_val::<[F]>(*slice));
286 let record: T = record_buffer.custom_borrow(layout);
287 record
288}
289
290pub trait CustomBorrow<'a, T, L> {
293 fn custom_borrow(&'a mut self, layout: L) -> T;
294
295 unsafe fn extract_layout(&self) -> L;
304}
305
306pub struct RecordSeeker<'a, RA, RecordMut, Layout> {
308 pub buffer: &'a mut [u8], _phantom: PhantomData<(RA, RecordMut, Layout)>,
310}
311
312impl<'a, RA, RecordMut, Layout> RecordSeeker<'a, RA, RecordMut, Layout> {
313 pub fn new(record_buffer: &'a mut [u8]) -> Self {
314 Self {
315 buffer: record_buffer,
316 _phantom: PhantomData,
317 }
318 }
319}
320
321impl<'a, R, M> RecordSeeker<'a, DenseRecordArena, R, MultiRowLayout<M>>
324where
325 [u8]: CustomBorrow<'a, R, MultiRowLayout<M>>,
326 R: SizedRecord<MultiRowLayout<M>>,
327 M: MultiRowMetadata + Clone,
328{
329 pub fn get_layout_at(offset: &mut usize, buffer: &[u8]) -> MultiRowLayout<M> {
332 let buffer = &buffer[*offset..];
333 unsafe { buffer.extract_layout() }
335 }
336
337 pub fn get_record_at(offset: &mut usize, buffer: &'a mut [u8]) -> R {
340 let layout = Self::get_layout_at(offset, buffer);
341 let buffer = &mut buffer[*offset..];
342 let record_size = R::size(&layout);
343 let record_alignment = R::alignment(&layout);
344 let aligned_record_size = record_size.next_multiple_of(record_alignment);
345 let record: R = buffer.custom_borrow(layout);
346 *offset += aligned_record_size;
347 record
348 }
349
350 pub fn extract_records(&'a mut self) -> Vec<R> {
352 let mut records = Vec::new();
353 let len = self.buffer.len();
354 let buff = &mut self.buffer[..];
355 let mut offset = 0;
356 while offset < len {
357 let record: R = {
358 let buff = unsafe { &mut *slice_from_raw_parts_mut(buff.as_mut_ptr(), len) };
363 Self::get_record_at(&mut offset, buff)
364 };
365 records.push(record);
366 }
367 records
368 }
369
370 pub fn transfer_to_matrix_arena<F: PrimeField32>(
372 &'a mut self,
373 arena: &mut MatrixRecordArena<F>,
374 ) {
375 let len = self.buffer.len();
376 arena.trace_offset = 0;
377 let mut offset = 0;
378 while offset < len {
379 let layout = Self::get_layout_at(&mut offset, self.buffer);
380 let record_size = R::size(&layout);
381 let record_alignment = R::alignment(&layout);
382 let aligned_record_size = record_size.next_multiple_of(record_alignment);
383 let src_ptr = unsafe { self.buffer.as_ptr().add(offset) };
385 let dst_ptr = arena
386 .alloc_buffer(layout.metadata.get_num_rows())
387 .as_mut_ptr();
388 unsafe { copy_nonoverlapping(src_ptr, dst_ptr, aligned_record_size) };
392 offset += aligned_record_size;
393 }
394 }
395}
396
397impl<'a, A, C, M> RecordSeeker<'a, DenseRecordArena, (A, C), AdapterCoreLayout<M>>
401where
402 [u8]: CustomBorrow<'a, A, AdapterCoreLayout<M>> + CustomBorrow<'a, C, AdapterCoreLayout<M>>,
403 A: SizedRecord<AdapterCoreLayout<M>>,
404 C: SizedRecord<AdapterCoreLayout<M>>,
405 M: AdapterCoreMetadata + Clone,
406{
407 pub fn get_aligned_sizes(layout: &AdapterCoreLayout<M>) -> (usize, usize) {
409 let adapter_alignment = A::alignment(layout);
410 let core_alignment = C::alignment(layout);
411 let adapter_size = A::size(layout);
412 let aligned_adapter_size = adapter_size.next_multiple_of(core_alignment);
413 let core_size = C::size(layout);
414 let aligned_core_size = (aligned_adapter_size + core_size)
415 .next_multiple_of(adapter_alignment)
416 - aligned_adapter_size;
417 (aligned_adapter_size, aligned_core_size)
418 }
419
420 pub fn get_aligned_record_size(layout: &AdapterCoreLayout<M>) -> usize {
422 let (adapter_size, core_size) = Self::get_aligned_sizes(layout);
423 adapter_size + core_size
424 }
425
426 pub fn get_record_at(
429 offset: &mut usize,
430 buffer: &'a mut [u8],
431 layout: AdapterCoreLayout<M>,
432 ) -> (A, C) {
433 let buffer = &mut buffer[*offset..];
434 let (adapter_size, core_size) = Self::get_aligned_sizes(&layout);
435 let (adapter_buffer, core_buffer) = unsafe { buffer.split_at_mut_unchecked(adapter_size) };
439 let adapter_record: A = adapter_buffer.custom_borrow(layout.clone());
440 let core_record: C = core_buffer.custom_borrow(layout);
441 *offset += adapter_size + core_size;
442 (adapter_record, core_record)
443 }
444
445 pub fn extract_records(&'a mut self, layout: AdapterCoreLayout<M>) -> Vec<(A, C)> {
447 let mut records = Vec::new();
448 let len = self.buffer.len();
449 let buff = &mut self.buffer[..];
450 let mut offset = 0;
451 while offset < len {
452 let record: (A, C) = {
453 let buff = unsafe { &mut *slice_from_raw_parts_mut(buff.as_mut_ptr(), len) };
458 Self::get_record_at(&mut offset, buff, layout.clone())
459 };
460 records.push(record);
461 }
462 records
463 }
464
465 pub fn transfer_to_matrix_arena<F: PrimeField32>(
467 &'a mut self,
468 arena: &mut MatrixRecordArena<F>,
469 layout: AdapterCoreLayout<M>,
470 ) {
471 let len = self.buffer.len();
472 arena.trace_offset = 0;
473 let mut offset = 0;
474 let (adapter_size, core_size) = Self::get_aligned_sizes(&layout);
475 while offset < len {
476 let dst_buffer = arena.alloc_single_row();
477 let (adapter_buf, core_buf) =
481 unsafe { dst_buffer.split_at_mut_unchecked(M::get_adapter_width()) };
482 unsafe {
483 let src_ptr = self.buffer.as_ptr().add(offset);
484 copy_nonoverlapping(src_ptr, adapter_buf.as_mut_ptr(), adapter_size);
485 copy_nonoverlapping(src_ptr.add(adapter_size), core_buf.as_mut_ptr(), core_size);
486 }
487 offset += adapter_size + core_size;
488 }
489 }
490}
491
492#[derive(Debug, Clone, Default, derive_new::new)]
501pub struct MultiRowLayout<M> {
502 pub metadata: M,
503}
504
505pub trait MultiRowMetadata {
507 fn get_num_rows(&self) -> usize;
508}
509
510#[derive(Debug, Clone, Default, derive_new::new)]
512pub struct EmptyMultiRowMetadata {}
513
514impl MultiRowMetadata for EmptyMultiRowMetadata {
515 #[inline(always)]
516 fn get_num_rows(&self) -> usize {
517 1
518 }
519}
520
521pub type EmptyMultiRowLayout = MultiRowLayout<EmptyMultiRowMetadata>;
523
524impl<'a, T: Sized, L: Default> CustomBorrow<'a, &'a mut T, L> for [u8]
527where
528 [u8]: BorrowMut<T>,
529{
530 fn custom_borrow(&'a mut self, _layout: L) -> &'a mut T {
531 self.borrow_mut()
532 }
533
534 unsafe fn extract_layout(&self) -> L {
535 L::default()
536 }
537}
538
539impl<'a, F: Field, M: MultiRowMetadata, R> RecordArena<'a, MultiRowLayout<M>, R>
542 for MatrixRecordArena<F>
543where
544 [u8]: CustomBorrow<'a, R, MultiRowLayout<M>>,
545{
546 fn alloc(&'a mut self, layout: MultiRowLayout<M>) -> R {
547 let buffer = self.alloc_buffer(layout.metadata.get_num_rows());
548 let record: R = buffer.custom_borrow(layout);
549 record
550 }
551}
552
553impl<'a, R, M> RecordArena<'a, MultiRowLayout<M>, R> for DenseRecordArena
556where
557 [u8]: CustomBorrow<'a, R, MultiRowLayout<M>>,
558 R: SizedRecord<MultiRowLayout<M>>,
559{
560 fn alloc(&'a mut self, layout: MultiRowLayout<M>) -> R {
561 let record_size = R::size(&layout);
562 let record_alignment = R::alignment(&layout);
563 let aligned_record_size = record_size.next_multiple_of(record_alignment);
564 let buffer = self.alloc_bytes(aligned_record_size);
565 let record: R = buffer.custom_borrow(layout);
566 record
567 }
568}
569
570#[derive(Debug, Clone, Default)]
580pub struct AdapterCoreLayout<M> {
581 pub metadata: M,
582}
583
584pub trait AdapterCoreMetadata {
587 fn get_adapter_width() -> usize;
588}
589
590impl<M> AdapterCoreLayout<M> {
591 pub fn new() -> Self
592 where
593 M: Default,
594 {
595 Self::default()
596 }
597
598 pub fn with_metadata(metadata: M) -> Self {
599 Self { metadata }
600 }
601}
602
603pub struct AdapterCoreEmptyMetadata<F, AS> {
607 _phantom: PhantomData<(F, AS)>,
608}
609
610impl<F, AS> Clone for AdapterCoreEmptyMetadata<F, AS> {
611 fn clone(&self) -> Self {
612 Self {
613 _phantom: PhantomData,
614 }
615 }
616}
617
618impl<F, AS> AdapterCoreEmptyMetadata<F, AS> {
619 pub fn new() -> Self {
620 Self {
621 _phantom: PhantomData,
622 }
623 }
624}
625
626impl<F, AS> Default for AdapterCoreEmptyMetadata<F, AS> {
627 fn default() -> Self {
628 Self {
629 _phantom: PhantomData,
630 }
631 }
632}
633
634impl<F, AS> AdapterCoreMetadata for AdapterCoreEmptyMetadata<F, AS>
635where
636 AS: super::AdapterTraceExecutor<F>,
637{
638 #[inline(always)]
639 fn get_adapter_width() -> usize {
640 AS::WIDTH * size_of::<F>()
641 }
642}
643
644pub type EmptyAdapterCoreLayout<F, AS> = AdapterCoreLayout<AdapterCoreEmptyMetadata<F, AS>>;
647
648impl<'a, F: Field, A, C, M: AdapterCoreMetadata> RecordArena<'a, AdapterCoreLayout<M>, (A, C)>
651 for MatrixRecordArena<F>
652where
653 [u8]: CustomBorrow<'a, A, AdapterCoreLayout<M>> + CustomBorrow<'a, C, AdapterCoreLayout<M>>,
654 M: Clone,
655{
656 fn alloc(&'a mut self, layout: AdapterCoreLayout<M>) -> (A, C) {
657 let adapter_width = M::get_adapter_width();
658 let buffer = self.alloc_single_row();
659 let (adapter_buffer, core_buffer) = unsafe { buffer.split_at_mut_unchecked(adapter_width) };
664
665 let adapter_record: A = adapter_buffer.custom_borrow(layout.clone());
666 let core_record: C = core_buffer.custom_borrow(layout);
667
668 (adapter_record, core_record)
669 }
670}
671
672impl<'a, A, C, M> RecordArena<'a, AdapterCoreLayout<M>, (A, C)> for DenseRecordArena
675where
676 [u8]: CustomBorrow<'a, A, AdapterCoreLayout<M>> + CustomBorrow<'a, C, AdapterCoreLayout<M>>,
677 M: Clone,
678 A: SizedRecord<AdapterCoreLayout<M>>,
679 C: SizedRecord<AdapterCoreLayout<M>>,
680{
681 fn alloc(&'a mut self, layout: AdapterCoreLayout<M>) -> (A, C) {
682 let adapter_alignment = A::alignment(&layout);
683 let core_alignment = C::alignment(&layout);
684 let adapter_size = A::size(&layout);
685 let aligned_adapter_size = adapter_size.next_multiple_of(core_alignment);
686 let core_size = C::size(&layout);
687 let aligned_core_size = (aligned_adapter_size + core_size)
688 .next_multiple_of(adapter_alignment)
689 - aligned_adapter_size;
690 debug_assert_eq!(MAX_ALIGNMENT % adapter_alignment, 0);
691 debug_assert_eq!(MAX_ALIGNMENT % core_alignment, 0);
692 let buffer = self.alloc_bytes(aligned_adapter_size + aligned_core_size);
693 let (adapter_buffer, core_buffer) =
698 unsafe { buffer.split_at_mut_unchecked(aligned_adapter_size) };
699
700 let adapter_record: A = adapter_buffer.custom_borrow(layout.clone());
701 let core_record: C = core_buffer.custom_borrow(layout);
702
703 (adapter_record, core_record)
704 }
705}