Copy records out untyped instead of through a byte slice

This commit is contained in:
Dennis Kobert
2026-09-07 15:38:53 +00:00
parent 79750fb52d
commit 369f7db992
2 changed files with 44 additions and 2 deletions

View File

@@ -100,7 +100,14 @@ impl OwnedRecord {
/// # Safety
/// `rec` must be a live record of `layout`.
pub unsafe fn copy_out(layout: &Layout, rec: Rec<'_>) -> OwnedRecord {
let bytes: Box<[u8]> = unsafe { std::slice::from_raw_parts(rec.ptr(), layout.size) }.into();
// The element-to-field seam is never written, so the copy stays untyped:
// a `&[u8]` over the frame would read those bytes.
let mut staged = Vec::<u8>::with_capacity(layout.size);
let bytes = unsafe {
std::ptr::copy_nonoverlapping(rec.ptr(), staged.as_mut_ptr(), layout.size);
staged.set_len(layout.size);
staged.into_boxed_slice()
};
let element = layout.element.parked.then(|| unsafe { (layout.element.clone_out)(rec.ptr()) });
let fields = layout
.fields
@@ -142,6 +149,34 @@ mod tests {
use crate::record::frames::FrameArena;
use crate::record::layout::{FieldWrite, element_write};
#[test]
fn a_padded_layout_copies_out_without_reading_its_seam() {
// A 4-byte element before an 8-aligned field leaves [4, 8) unwritten.
let layout = Layout::default().with_writes(0, element_write::<u32>(), &[FieldWrite::of::<crate::attribute::Opacity>(0)]);
let offset = layout.offset_of("opacity", 0).unwrap();
assert_eq!((layout.element.size, offset), (4, 8), "the fixture needs the element-to-field seam");
let arena = crate::arena::Arena::new(1024).unwrap();
let scratch = arena.alloc_scratch::<u64>(layout.frame_bytes().div_ceil(8)).unwrap();
let base: *mut u8 = scratch.as_mut_ptr().cast();
unsafe { write_element(base, 7u32, &arena) }.unwrap();
unsafe { write_field::<f64>(base, offset, 0.5) };
let copy = unsafe { OwnedRecord::copy_out(&layout, Rec::new(base.cast_const())) };
let replay_arena = crate::arena::Arena::new(1024).unwrap();
let mut frame_arena = FrameArena::new();
frame_arena.reserve(layout.frame_bytes());
let frames = frame_arena.frames();
let mut slot = frames.claim(&layout);
copy.replay_into(&mut slot, &replay_arena).unwrap();
// SAFETY: the replay completes the record in the claimed frame.
let value = unsafe { slot.finish() };
let rec = layout.rec(&value);
assert_eq!(unsafe { read_element::<u32>(rec) }, 7);
assert_eq!(unsafe { rec.read::<f64>(offset) }, 0.5);
}
#[test]
fn owned_records_replay_re_parked_payloads_after_the_source_dies() {
let layout = Layout::default().with_writes(0, element_write::<String>(), &[FieldWrite::of::<crate::attribute::Name>(0)]);

View File

@@ -279,7 +279,14 @@ impl<'e> GroupItem<'e> {
let frames = self.frames();
// SAFETY: the constructors store `len` lanes of `layout` at the
// layout's stride, and the erased glue reads each lane's own region.
let bytes: Box<[u8]> = unsafe { std::slice::from_raw_parts(frames, self.len * stride) }.into();
// A lane's seam and stride tail are never written, so the copy stays
// untyped: a `&[u8]` over the frames would read those bytes.
let mut staged = Vec::<u8>::with_capacity(self.len * stride);
let bytes = unsafe {
std::ptr::copy_nonoverlapping(frames, staged.as_mut_ptr(), self.len * stride);
staged.set_len(self.len * stride);
staged.into_boxed_slice()
};
let elements = self
.layout
.element