openvm_circuit/arch/
record_arena.rs

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    /// Currently `width` always refers to the main trace width.
16    fn with_capacity(height: usize, width: usize) -> Self;
17
18    fn is_empty(&self) -> bool;
19
20    /// Only used for metric collection purposes. Intended usage is that for a record arena that
21    /// corresponds to a single trace matrix, this function can extract the current number of used
22    /// rows of the corresponding trace matrix. This is currently expected to work only for
23    /// [MatrixRecordArena].
24    #[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
35/// Given some minimum layout of type `Layout`, the `RecordArena` should allocate a buffer, of
36/// size possibly larger than the record, and then return mutable pointers to the record within the
37/// buffer.
38pub trait RecordArena<'a, Layout, RecordMut> {
39    /// Allocates underlying buffer and returns a mutable reference `RecordMut`.
40    /// Note that calling this function may not call an underlying memory allocation as the record
41    /// arena may be virtual.
42    fn alloc(&'a mut self, layout: Layout) -> RecordMut;
43}
44
45/// Helper trait for arenas backed by row-major matrices.
46pub trait RowMajorMatrixArena<F>: Arena {
47    /// Set the arena's capacity based on the projected trace height.
48    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
54/// `SizedRecord` is a trait that provides additional information about the size and alignment
55/// requirements of a record. Should be implemented on RecordMut types
56pub trait SizedRecord<Layout> {
57    /// The minimal size in bytes that the RecordMut requires to be properly constructed
58    /// given the layout.
59    fn size(layout: &Layout) -> usize;
60    /// The minimal alignment required for the RecordMut to be properly constructed
61    /// given the layout.
62    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// =================== Arena Implementations =========================
79
80#[derive(Default)]
81pub struct MatrixRecordArena<F> {
82    pub trace_buffer: Vec<F>,
83    pub width: usize,
84    pub trace_offset: usize,
85    /// The arena is created with a specified capacity, but may be truncated before being converted
86    /// into a [RowMajorMatrix] if `allow_truncate == true`. If `allow_truncate == false`, then the
87    /// matrix will never be truncated. The latter is used if the trace matrix must have fixed
88    /// dimensions (e.g., for a static verifier).
89    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        // SAFETY:
104        // - `ptr` is non-null
105        // - `size` is correct
106        // - alignment of `u8` is always satisfied
107        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        // PERF: use memset
141        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        // This should be automatic since trace_buffer's height is a power of two:
158        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    /// Creates a new [DenseRecordArena] with the given capacity in bytes.
178    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    /// Returns the allocated size of the arena in bytes.
197    ///
198    /// **Note**: This may include additional bytes for alignment.
199    pub fn capacity(&self) -> usize {
200        self.records_buffer.get_ref().len()
201    }
202
203    /// Allocates `count` bytes and returns as a mutable slice.
204    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        // SAFETY:
213        // - `begin` is within bounds and caller must ensure `count` bytes are available
214        // - The resulting slice is valid for the lifetime of self
215        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    // Returns a [RecordSeeker] on the allocated buffer
250    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    // TODO[jpw]: treat `width` as AIR width in number of columns for now
257    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
272// =================== Helper Functions =================================
273
274/// Converts a field element slice into a record type.
275/// This function transmutes the `&mut [F]` to raw bytes,
276/// then uses the `CustomBorrow` trait to transmute to the desired record type `T`.
277/// ## Safety
278/// `slice` must satisfy the requirements of the `CustomBorrow` trait.
279pub 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    // The alignment of `[u8]` is always satisfiedÆ’
284    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
290/// A trait that allows for custom implementation of `borrow` given the necessary information
291/// This is useful for record structs that have dynamic size
292pub trait CustomBorrow<'a, T, L> {
293    fn custom_borrow(&'a mut self, layout: L) -> T;
294
295    /// Given `&self` as a valid starting pointer of a reference that has already been previously
296    /// allocated and written to, extracts and returns the corresponding layout.
297    /// This must work even if `T` is not sized.
298    ///
299    /// # Safety
300    /// - `&self` must be a valid starting pointer on which `custom_borrow` has already been called
301    /// - The data underlying `&self` has already been written to and is self-describing, so layout
302    ///   can be extracted
303    unsafe fn extract_layout(&self) -> L;
304}
305
306// This is a helper struct that implements a few utility methods
307pub struct RecordSeeker<'a, RA, RecordMut, Layout> {
308    pub buffer: &'a mut [u8], // The buffer that the records are written to
309    _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
321// `RecordSeeker` implementation for [DenseRecordArena], with [MultiRowLayout]
322// **NOTE** Assumes that `layout` can be extracted from the record alone
323impl<'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    // Returns the layout at the given offset in the buffer
330    // **SAFETY**: `offset` has to be a valid offset, pointing to the start of a record
331    pub fn get_layout_at(offset: &mut usize, buffer: &[u8]) -> MultiRowLayout<M> {
332        let buffer = &buffer[*offset..];
333        // SAFETY: buffer points to the start of a valid record with proper layout information
334        unsafe { buffer.extract_layout() }
335    }
336
337    // Returns a record at the given offset in the buffer
338    // **SAFETY**: `offset` has to be a valid offset, pointing to the start of a record
339    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    // Returns a vector of all the records in the buffer
351    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                // SAFETY:
359                // - buff.as_mut_ptr() is valid for len bytes
360                // - len matches original buffer size
361                // - Bypasses borrow checker for multiple mutable accesses within loop
362                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    // Transfers the records in the buffer to a [MatrixRecordArena], used in testing
371    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            // SAFETY: offset < len, pointer within buffer bounds
384            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            // SAFETY:
389            // - src_ptr points to valid memory with at least aligned_record_size bytes
390            // - dst_ptr points to freshly allocated memory with sufficient size
391            unsafe { copy_nonoverlapping(src_ptr, dst_ptr, aligned_record_size) };
392            offset += aligned_record_size;
393        }
394    }
395}
396
397// `RecordSeeker` implementation for [DenseRecordArena], with [AdapterCoreLayout]
398// **NOTE** Assumes that `layout` is the same for all the records, so it is expected to be passed as
399// a parameter
400impl<'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    // Returns the aligned sizes of the adapter and core records given their layout
408    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    // Returns the aligned size of a single record given its layout
421    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    // Returns a record at the given offset in the buffer
427    // **SAFETY**: `offset` has to be a valid offset, pointing to the start of a record
428    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        // SAFETY:
436        // - adapter_size is calculated to be within the buffer bounds
437        // - The buffer has sufficient size for both adapter and core records
438        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    // Returns a vector of all the records in the buffer
446    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                // SAFETY:
454                // - buff.as_mut_ptr() is valid for len bytes
455                // - len matches original buffer size
456                // - Bypasses borrow checker for multiple mutable accesses within loop
457                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    // Transfers the records in the buffer to a [MatrixRecordArena], used in testing
466    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            // SAFETY:
478            // - dst_buffer has sufficient size (allocated for a full row)
479            // - M::get_adapter_width() is within bounds of the allocated buffer
480            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// ============================== MultiRowLayout =======================================
493
494/// Minimal layout information that [RecordArena] requires for record allocation
495/// in scenarios involving chips that:
496/// - can have multiple rows per record, and
497/// - have possibly variable length records
498///
499/// **NOTE**: `M` is the metadata type that implements `MultiRowMetadata`
500#[derive(Debug, Clone, Default, derive_new::new)]
501pub struct MultiRowLayout<M> {
502    pub metadata: M,
503}
504
505/// `Metadata` types need to implement this trait to be used with `MultiRowLayout`
506pub trait MultiRowMetadata {
507    fn get_num_rows(&self) -> usize;
508}
509
510/// Empty metadata that implements `MultiRowMetadata` with `get_num_rows` always returning 1
511#[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
521/// Empty metadata that implements `MultiRowMetadata`
522pub type EmptyMultiRowLayout = MultiRowLayout<EmptyMultiRowMetadata>;
523
524/// If a struct implements `BorrowMut<T>`, then the same implementation can be used for
525/// `CustomBorrow::custom_borrow` with any layout
526impl<'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
539/// [RecordArena] implementation for [MatrixRecordArena], with [MultiRowLayout]
540/// **NOTE**: `R` is the RecordMut type
541impl<'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
553/// [RecordArena] implementation for [DenseRecordArena], with [MultiRowLayout]
554/// **NOTE**: `R` is the RecordMut type
555impl<'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// ============================== AdapterCoreLayout =======================================
571// This is for integration_api usage
572
573/// Minimal layout information that [RecordArena] requires for record allocation
574/// in scenarios involving chips that:
575/// - have a single row per record, and
576/// - have trace row = [adapter_row, core_row]
577///
578/// **NOTE**: `M` is the metadata type that implements `AdapterCoreMetadata`
579#[derive(Debug, Clone, Default)]
580pub struct AdapterCoreLayout<M> {
581    pub metadata: M,
582}
583
584/// `Metadata` types need to implement this trait to be used with `AdapterCoreLayout`
585/// **NOTE**: get_adapter_width returns the size in bytes
586pub 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
603/// Empty metadata that implements `AdapterCoreMetadata`
604/// **NOTE**: `AS` is the adapter type that implements `AdapterTraceExecutor`
605/// **WARNING**: `AS::WIDTH` is the number of field elements, not the size in bytes
606pub 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
644/// AdapterCoreLayout with empty metadata that can be used by chips that have record type
645/// (&mut A, &mut C) where `A` and `C` are `Sized`
646pub type EmptyAdapterCoreLayout<F, AS> = AdapterCoreLayout<AdapterCoreEmptyMetadata<F, AS>>;
647
648/// [RecordArena] implementation for [MatrixRecordArena], with [AdapterCoreLayout]
649/// **NOTE**: `A` is the adapter RecordMut type and `C` is the core RecordMut type
650impl<'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        // Doing a unchecked split here for perf
660        // SAFETY:
661        // - buffer is a freshly allocated row with sufficient size
662        // - adapter_width is guaranteed to be less than the total buffer size
663        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
672/// [RecordArena] implementation for [DenseRecordArena], with [AdapterCoreLayout]
673/// **NOTE**: `A` is the adapter RecordMut type and `C` is the core record type
674impl<'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        // Doing an unchecked split here for perf
694        // SAFETY:
695        // - buffer has exactly aligned_adapter_size + aligned_core_size bytes
696        // - aligned_adapter_size is within bounds by construction
697        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}