#[cfg(feature = "derive")]
pub use dyn_struct_derive::DynStruct;
use std::mem::{align_of, size_of, MaybeUninit};
#[repr(C)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DynStruct<Header, Tail> {
pub header: Header,
pub tail: [Tail],
}
impl<Header, Tail> DynStruct<Header, Tail> {
#[inline]
pub fn new<I>(header: Header, tail: I) -> Box<Self>
where
I: IntoIterator<Item = Tail>,
I::IntoIter: ExactSizeIterator,
{
let tail = tail.into_iter();
let mut writer = BoxWriter::<Header, Tail>::new(header, tail.len());
for value in tail {
writer.write_tail::<I::IntoIter>(value);
}
writer.finish::<I::IntoIter>()
}
pub fn from_slice(header: Header, tail: &[Tail]) -> Box<Self>
where
Tail: Copy,
{
let mut writer = BoxWriter::<Header, Tail>::new(header, tail.len());
unsafe {
writer.write_slice(tail);
}
writer.finish::<()>()
}
#[inline]
fn align() -> usize {
usize::max(align_of::<Header>(), align_of::<Tail>())
}
#[inline]
fn size(len: usize) -> usize {
let header = size_of::<Header>();
let tail_align = align_of::<Tail>();
let padding = if header % tail_align == 0 {
0
} else {
tail_align - header % tail_align
};
let tail = size_of::<Tail>() * len;
header + padding + tail
}
}
struct BoxWriter<Header, Tail> {
raw: *mut DynStruct<Header, MaybeUninit<Tail>>,
written: usize,
}
impl<Header, Tail> BoxWriter<Header, Tail> {
#[inline]
pub fn new(header: Header, len: usize) -> Self {
let total_size = DynStruct::<Header, Tail>::size(len);
let align = DynStruct::<Header, Tail>::align();
let fat_ptr = if total_size == 0 {
let slice: Box<[()]> = Box::from(slice_with_len(len));
std::mem::forget(header);
Box::into_raw(slice) as *mut DynStruct<Header, MaybeUninit<Tail>>
} else {
unsafe {
let layout = std::alloc::Layout::from_size_align(total_size, align).unwrap();
let raw = std::alloc::alloc(layout);
if raw.is_null() {
std::alloc::handle_alloc_error(layout)
}
raw.cast::<Header>().write(header);
let slice = std::slice::from_raw_parts_mut(raw as *mut (), len);
slice as *mut [()] as *mut DynStruct<Header, MaybeUninit<Tail>>
}
};
BoxWriter {
raw: fat_ptr,
written: 0,
}
}
#[inline]
fn write_tail<I>(&mut self, value: Tail) {
let written = self.written;
let tail = &mut self.as_mut().tail;
assert!(
written < tail.len(),
"got more items than expected. Probable bug in `ExactSizeIterator` for `{}`?",
std::any::type_name::<I>(),
);
tail[written].write(value);
self.written += 1;
}
#[inline]
fn finish<I>(mut self) -> Box<DynStruct<Header, Tail>> {
let len = self.as_mut().tail.len();
assert_eq!(
self.written,
len,
"got fewer items than expected. Probable bug in `ExactSizeIterator` for `{}`?",
std::any::type_name::<I>(),
);
let init = self.raw as *mut DynStruct<Header, Tail>;
std::mem::forget(self);
unsafe { Box::from_raw(init) }
}
fn as_mut(&mut self) -> &mut DynStruct<Header, MaybeUninit<Tail>> {
unsafe { &mut *self.raw }
}
unsafe fn write_slice(&mut self, values: &[Tail])
where
Tail: Copy,
{
self.as_mut()
.tail
.as_mut_ptr()
.copy_from_nonoverlapping(values.as_ptr().cast(), values.len());
self.written = values.len();
}
}
impl<Header, Tail> Drop for BoxWriter<Header, Tail> {
fn drop(&mut self) {
unsafe {
std::ptr::drop_in_place(self.raw.cast::<Header>());
let initialized = self.written;
let tail = &mut self.as_mut().tail;
for i in 0..initialized {
tail[i].as_mut_ptr().drop_in_place();
}
}
}
}
impl<T> DynStruct<T, T> {
pub fn slice_view(values: &[T]) -> &Self {
assert!(
!values.is_empty(),
"attempted to create `{}` from empty slice (needs at least 1 element)",
std::any::type_name::<Self>()
);
let slice = &values[..values.len() - 1];
unsafe { &*(slice as *const [T] as *const Self) }
}
}
impl<T, const N: usize> DynStruct<[T; N], T> {
pub fn slice_view(values: &[T]) -> &Self {
assert!(
values.len() >= N,
"attempted to create `{}` from empty slice (needs at least {} elements)",
std::any::type_name::<Self>(),
N,
);
let slice = &values[..values.len() - N];
unsafe { &*(slice as *const [T] as *const Self) }
}
}
fn slice_with_len(len: usize) -> &'static [()] {
static ARBITRARY: [(); usize::MAX] = [(); usize::MAX];
&ARBITRARY[..len]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mixed_types() {
let mixed = DynStruct::new((true, 32u16), [1u64, 2, 3, 4]);
assert_eq!(mixed.header, (true, 32u16));
assert_eq!(&mixed.tail, &[1, 2, 3, 4]);
}
#[test]
fn zero_sized_types() {
let zero = DynStruct::new((), [(), ()]);
assert_eq!(zero.header, ());
assert_eq!(&zero.tail, &[(), ()]);
}
#[test]
fn from_slice() {
let slice = DynStruct::from_slice((true, 32u16), &[1, 2, 3]);
assert_eq!(slice.header, (true, 32u16));
assert_eq!(&slice.tail, &[1, 2, 3]);
let array = DynStruct::<[u32; 3], u32>::slice_view(&[1, 2, 3, 4, 5]);
assert_eq!(array.header, [1, 2, 3]);
assert_eq!(&array.tail, &[4, 5]);
}
#[test]
fn slice_view() {
let same = DynStruct::<u32, u32>::slice_view(&[1, 2, 3]);
assert_eq!(same.header, 1);
assert_eq!(&same.tail, &[2, 3]);
let array = DynStruct::<[u32; 3], u32>::slice_view(&[1, 2, 3, 4, 5]);
assert_eq!(array.header, [1, 2, 3]);
assert_eq!(&array.tail, &[4, 5]);
}
}