extern crate alloc;
use core::ptr::NonNull;
use allocator_api2::alloc::AllocError;
use keleusma_arena::{Arena, ArenaHandle, Stale};
pub fn write_bool(bytes: &mut [u8], offset: usize, value: bool) {
bytes[offset] = u8::from(value);
}
pub fn read_bool(bytes: &[u8], offset: usize) -> bool {
bytes[offset] != 0
}
pub fn write_byte(bytes: &mut [u8], offset: usize, value: u8) {
bytes[offset] = value;
}
pub fn read_byte(bytes: &[u8], offset: usize) -> u8 {
bytes[offset]
}
pub fn write_i64(bytes: &mut [u8], offset: usize, value: i64) {
let le = value.to_le_bytes();
bytes[offset..offset + 8].copy_from_slice(&le);
}
pub fn read_i64(bytes: &[u8], offset: usize) -> i64 {
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[offset..offset + 8]);
i64::from_le_bytes(buf)
}
#[cfg(feature = "floats")]
pub fn write_f64(bytes: &mut [u8], offset: usize, value: f64) {
let le = value.to_le_bytes();
bytes[offset..offset + 8].copy_from_slice(&le);
}
#[cfg(feature = "floats")]
pub fn read_f64(bytes: &[u8], offset: usize) -> f64 {
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[offset..offset + 8]);
f64::from_le_bytes(buf)
}
#[derive(Debug, Clone)]
pub enum FlatComposite {
Arena(ArenaHandle<[u8]>),
}
impl FlatComposite {
pub fn empty() -> Self {
let raw: *mut [u8] =
core::ptr::slice_from_raw_parts_mut(NonNull::<u8>::dangling().as_ptr(), 0);
let nn = unsafe { NonNull::new_unchecked(raw) };
let handle = unsafe { ArenaHandle::from_raw_parts(nn, 0) };
Self::Arena(handle)
}
pub fn ref_epoch(&self) -> u64 {
let Self::Arena(handle) = self;
handle.epoch()
}
pub fn in_arena(self, _arena: &Arena) -> Result<Self, AllocError> {
Ok(self)
}
pub fn build_in_arena(
arena: &Arena,
size: usize,
fill: impl FnOnce(&mut [u8]) -> Result<(), ()>,
) -> Result<Option<Self>, AllocError> {
if size == 0 {
return Ok(Some(Self::empty()));
}
let buffer = arena.alloc_top_bytes(size)?;
let dst_ptr = buffer.as_ptr() as *mut u8;
let dst = unsafe { core::slice::from_raw_parts_mut(dst_ptr, size) };
if fill(dst).is_err() {
return Ok(None);
}
let raw: *mut [u8] = core::ptr::slice_from_raw_parts_mut(dst_ptr, size);
let nn = unsafe { NonNull::new_unchecked(raw) };
let handle = unsafe { ArenaHandle::from_raw_parts(nn, arena.epoch()) };
Ok(Some(Self::Arena(handle)))
}
pub fn nested_view(&self, offset: usize, len: usize, arena: &Arena) -> Result<Self, Stale> {
let Self::Arena(handle) = self;
let base = handle.get(arena)?;
if offset.checked_add(len).is_none_or(|end| end > base.len()) {
return Err(Stale);
}
if len == 0 {
return Ok(Self::empty());
}
let child_ptr = unsafe { base.as_ptr().add(offset) } as *mut u8;
let raw: *mut [u8] = core::ptr::slice_from_raw_parts_mut(child_ptr, len);
let nn = unsafe { NonNull::new_unchecked(raw) };
let child = unsafe { ArenaHandle::from_raw_parts(nn, handle.epoch()) };
Ok(Self::Arena(child))
}
pub fn resolve<'a>(&'a self, arena: &'a Arena) -> Result<&'a [u8], Stale> {
let Self::Arena(handle) = self;
handle.get(arena)
}
pub fn is_valid(&self, arena: &Arena) -> bool {
self.resolve(arena).is_ok()
}
pub fn eq_in_arena(&self, other: &Self, arena: &Arena) -> bool {
match (self.resolve(arena), other.resolve(arena)) {
(Ok(a), Ok(b)) => a == b,
_ => false,
}
}
pub fn byte_len(&self) -> usize {
let Self::Arena(handle) = self;
handle.len()
}
pub fn inline_bytes(&self) -> Option<&[u8]> {
match self {
Self::Arena(handle) if handle.is_empty() => Some(&[]),
Self::Arena(_) => None,
}
}
}
impl PartialEq for FlatComposite {
fn eq(&self, other: &Self) -> bool {
self.byte_len() == 0 && other.byte_len() == 0
}
}
impl Eq for FlatComposite {}
#[cfg(test)]
mod tests {
use super::*;
use crate::value_layout::{LayoutDescriptor, ScalarKind};
use alloc::boxed::Box;
use alloc::string::ToString;
const I64_BYTES: usize = 8;
const F64_BYTES: usize = 8;
#[test]
fn bool_roundtrip_true() {
let mut bytes = [0u8; 1];
write_bool(&mut bytes, 0, true);
assert_eq!(bytes[0], 1);
assert!(read_bool(&bytes, 0));
}
#[test]
fn bool_roundtrip_false() {
let mut bytes = [0xFFu8; 1];
write_bool(&mut bytes, 0, false);
assert_eq!(bytes[0], 0);
assert!(!read_bool(&bytes, 0));
}
#[test]
fn bool_read_accepts_any_nonzero() {
let bytes = [0x42u8; 1];
assert!(read_bool(&bytes, 0));
}
#[test]
fn byte_roundtrip() {
let mut bytes = [0u8; 4];
write_byte(&mut bytes, 2, 0xABu8);
assert_eq!(bytes[2], 0xAB);
assert_eq!(read_byte(&bytes, 2), 0xAB);
}
#[test]
fn i64_roundtrip_positive() {
let mut bytes = [0u8; 8];
write_i64(&mut bytes, 0, 0x1234_5678_9ABC_DEF0);
assert_eq!(read_i64(&bytes, 0), 0x1234_5678_9ABC_DEF0);
}
#[test]
fn i64_roundtrip_negative() {
let mut bytes = [0u8; 8];
write_i64(&mut bytes, 0, -42);
assert_eq!(read_i64(&bytes, 0), -42);
}
#[test]
fn i64_roundtrip_boundary_values() {
let mut bytes = [0u8; 8];
write_i64(&mut bytes, 0, i64::MIN);
assert_eq!(read_i64(&bytes, 0), i64::MIN);
write_i64(&mut bytes, 0, i64::MAX);
assert_eq!(read_i64(&bytes, 0), i64::MAX);
write_i64(&mut bytes, 0, 0);
assert_eq!(read_i64(&bytes, 0), 0);
}
#[test]
fn i64_writes_little_endian() {
let mut bytes = [0u8; 8];
write_i64(&mut bytes, 0, 0x01);
assert_eq!(bytes, [0x01, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn i64_roundtrip_at_offset() {
let mut bytes = [0u8; 24];
write_i64(&mut bytes, 8, 0x1234_5678_9ABC_DEF0);
assert_eq!(read_i64(&bytes, 8), 0x1234_5678_9ABC_DEF0);
assert_eq!(read_i64(&bytes, 0), 0);
assert_eq!(read_i64(&bytes, 16), 0);
}
#[cfg(feature = "floats")]
#[test]
fn f64_roundtrip_positive() {
let mut bytes = [0u8; 8];
write_f64(&mut bytes, 0, core::f64::consts::PI);
assert_eq!(read_f64(&bytes, 0), core::f64::consts::PI);
}
#[cfg(feature = "floats")]
#[test]
fn f64_roundtrip_negative_and_zero() {
let mut bytes = [0u8; 8];
write_f64(&mut bytes, 0, -1.5);
assert_eq!(read_f64(&bytes, 0), -1.5);
write_f64(&mut bytes, 0, 0.0);
assert_eq!(read_f64(&bytes, 0), 0.0);
}
#[cfg(feature = "floats")]
#[test]
fn f64_roundtrip_boundary_values() {
let mut bytes = [0u8; 8];
write_f64(&mut bytes, 0, f64::MIN);
assert_eq!(read_f64(&bytes, 0), f64::MIN);
write_f64(&mut bytes, 0, f64::MAX);
assert_eq!(read_f64(&bytes, 0), f64::MAX);
write_f64(&mut bytes, 0, f64::EPSILON);
assert_eq!(read_f64(&bytes, 0), f64::EPSILON);
}
#[cfg(feature = "floats")]
#[test]
fn f64_roundtrip_infinity_and_nan() {
let mut bytes = [0u8; 8];
write_f64(&mut bytes, 0, f64::INFINITY);
assert_eq!(read_f64(&bytes, 0), f64::INFINITY);
write_f64(&mut bytes, 0, f64::NEG_INFINITY);
assert_eq!(read_f64(&bytes, 0), f64::NEG_INFINITY);
write_f64(&mut bytes, 0, f64::NAN);
assert!(read_f64(&bytes, 0).is_nan());
}
fn test_arena() -> Arena {
Arena::with_capacity(4096)
}
fn arena_body(arena: &Arena, bytes: &[u8]) -> FlatComposite {
FlatComposite::build_in_arena(arena, bytes.len(), |dst| {
dst.copy_from_slice(bytes);
Ok(())
})
.unwrap()
.unwrap()
}
#[test]
fn flat_composite_construction_size_matches_layout() {
let arena = test_arena();
let layout = LayoutDescriptor::Tuple(alloc::vec![
LayoutDescriptor::Scalar(ScalarKind::Int),
LayoutDescriptor::Scalar(ScalarKind::Bool),
]);
let size = layout.size_in_bytes(I64_BYTES, F64_BYTES);
let comp = FlatComposite::build_in_arena(&arena, size, |dst| {
dst.fill(0);
Ok(())
})
.unwrap()
.unwrap();
let bytes = comp.resolve(&arena).unwrap();
assert_eq!(bytes.len(), 8 + 1);
assert!(bytes.iter().all(|&b| b == 0));
}
#[test]
fn flat_composite_struct() {
let arena = test_arena();
let layout = LayoutDescriptor::Struct {
type_name: "Point".to_string(),
fields: alloc::vec![
("x".to_string(), LayoutDescriptor::Scalar(ScalarKind::Int)),
("y".to_string(), LayoutDescriptor::Scalar(ScalarKind::Int)),
],
};
let size = layout.size_in_bytes(I64_BYTES, F64_BYTES);
assert_eq!(size, 16);
let x_off = layout
.struct_field_offset("x", I64_BYTES, F64_BYTES)
.unwrap();
let y_off = layout
.struct_field_offset("y", I64_BYTES, F64_BYTES)
.unwrap();
let comp = FlatComposite::build_in_arena(&arena, size, |dst| {
dst.fill(0);
write_i64(dst, x_off, 3);
write_i64(dst, y_off, 4);
Ok(())
})
.unwrap()
.unwrap();
let bytes = comp.resolve(&arena).unwrap();
assert_eq!(read_i64(bytes, x_off), 3);
assert_eq!(read_i64(bytes, y_off), 4);
}
#[test]
fn flat_composite_array() {
let arena = test_arena();
let layout = LayoutDescriptor::Array {
element: Box::new(LayoutDescriptor::Scalar(ScalarKind::Int)),
count: 4,
};
let size = layout.size_in_bytes(I64_BYTES, F64_BYTES);
assert_eq!(size, 32);
let offsets: alloc::vec::Vec<usize> = (0..4)
.map(|i| layout.field_offset(i, I64_BYTES, F64_BYTES).unwrap())
.collect();
let comp = FlatComposite::build_in_arena(&arena, size, |dst| {
dst.fill(0);
for (i, &off) in offsets.iter().enumerate() {
write_i64(dst, off, (i as i64) * 10);
}
Ok(())
})
.unwrap()
.unwrap();
let bytes = comp.resolve(&arena).unwrap();
for (i, &off) in offsets.iter().enumerate() {
assert_eq!(read_i64(bytes, off), (i as i64) * 10);
}
}
#[test]
fn flat_composite_mixed_field_types() {
let arena = test_arena();
let layout = LayoutDescriptor::Tuple(alloc::vec![
LayoutDescriptor::Scalar(ScalarKind::Bool),
LayoutDescriptor::Scalar(ScalarKind::Int),
LayoutDescriptor::Scalar(ScalarKind::Byte),
]);
let size = layout.size_in_bytes(I64_BYTES, F64_BYTES);
assert_eq!(size, 1 + 8 + 1);
let off_bool = layout.field_offset(0, I64_BYTES, F64_BYTES).unwrap();
let off_int = layout.field_offset(1, I64_BYTES, F64_BYTES).unwrap();
let off_byte = layout.field_offset(2, I64_BYTES, F64_BYTES).unwrap();
let comp = FlatComposite::build_in_arena(&arena, size, |dst| {
dst.fill(0);
write_bool(dst, off_bool, true);
write_i64(dst, off_int, -123);
write_byte(dst, off_byte, 0xAB);
Ok(())
})
.unwrap()
.unwrap();
let bytes = comp.resolve(&arena).unwrap();
assert!(read_bool(bytes, off_bool));
assert_eq!(read_i64(bytes, off_int), -123);
assert_eq!(read_byte(bytes, off_byte), 0xAB);
}
#[test]
fn flat_composite_nested_view() {
let arena = test_arena();
let outer = arena_body(&arena, &[0, 0, 1, 2, 3, 4, 0, 0, 0, 0]);
let inner = outer.nested_view(2, 4, &arena).unwrap();
assert!(matches!(inner, FlatComposite::Arena(_)));
assert_eq!(inner.resolve(&arena).unwrap(), &[1, 2, 3, 4]);
assert_eq!(
outer.resolve(&arena).unwrap(),
&[0, 0, 1, 2, 3, 4, 0, 0, 0, 0]
);
}
#[test]
fn nested_view_out_of_bounds_faults_not_ub() {
let arena = test_arena();
let outer = arena_body(&arena, &[0, 1, 2, 3]); assert!(outer.nested_view(2, 4, &arena).is_err()); assert!(outer.nested_view(5, 0, &arena).is_err()); assert!(outer.nested_view(usize::MAX, 1, &arena).is_err()); assert!(outer.nested_view(1, 2, &arena).is_ok());
}
#[test]
fn arena_body_resolves_to_its_bytes() {
let arena = test_arena();
let body = arena_body(&arena, &[1, 2, 3, 4]);
assert!(matches!(body, FlatComposite::Arena(_)));
assert_eq!(body.resolve(&arena).unwrap(), &[1, 2, 3, 4]);
assert!(body.is_valid(&arena));
}
#[test]
fn arena_equality_is_content_not_handle() {
let arena = test_arena();
let a = arena_body(&arena, &[7, 8, 9]);
let b = arena_body(&arena, &[7, 8, 9]);
let c = arena_body(&arena, &[7, 8, 0]);
assert!(a.eq_in_arena(&b, &arena));
assert!(!a.eq_in_arena(&c, &arena));
}
#[test]
fn reset_makes_an_arena_body_stale() {
let mut arena = test_arena();
let a = arena_body(&arena, &[1, 2, 3]);
let b = arena_body(&arena, &[1, 2, 3]);
assert!(a.eq_in_arena(&b, &arena));
arena.reset().unwrap();
assert!(!a.is_valid(&arena));
assert!(a.resolve(&arena).is_err());
assert!(!a.eq_in_arena(&b, &arena));
}
#[test]
fn empty_body_is_the_empty_sentinel() {
let arena = test_arena();
let empty = FlatComposite::build_in_arena(&arena, 0, |_| Ok(()))
.unwrap()
.unwrap();
assert_eq!(empty.resolve(&arena).unwrap(), &[] as &[u8]);
assert!(empty.is_valid(&arena));
assert_eq!(empty.byte_len(), 0);
assert_eq!(empty.inline_bytes(), Some(&[] as &[u8]));
assert_eq!(FlatComposite::empty().byte_len(), 0);
assert_eq!(FlatComposite::empty(), FlatComposite::empty());
}
#[test]
fn value_is_thirty_two_bytes() {
assert_eq!(core::mem::size_of::<crate::bytecode::Value>(), 32);
}
}