use crate::helpers::{AsBytes, FromBytes};
use core::alloc::Layout;
use core::{mem, ptr, slice};
#[repr(C, packed)]
pub struct Blob<T: BlobLayout>(T::Header, [u8]);
pub trait BlobLayout {
type Header: AsBytes + FromBytes;
}
impl<'a, T: BlobLayout> Blob<T> {
pub fn header(&self) -> &T::Header {
unsafe { &self.0 }
}
pub(crate) fn tail(&self) -> &[u8] {
&self.1
}
pub fn as_bytes(&self) -> &[u8] {
AsBytes::as_bytes(self)
}
#[allow(clippy::wrong_self_convention)]
pub fn into_bytes(self: Box<Self>) -> Box<[u8]> {
AsBytes::into_bytes(self)
}
pub fn from_boxed(boxed: Box<[u8]>) -> Box<Self> {
FromBytes::from_boxed(boxed)
}
pub(crate) unsafe fn ref_cast<U: BlobLayout>(&self) -> &Blob<U> {
Blob::<U>::from_bytes(self.as_bytes())
}
}
unsafe impl<T: BlobLayout> AsBytes for Blob<T> {}
unsafe impl<T: BlobLayout> FromBytes for Blob<T> {
unsafe fn min_layout() -> Layout {
Layout::new::<T::Header>()
}
unsafe fn ptr_cast(source: *const [u8]) -> *const Self {
assert_ne!(source as *const (), ptr::null());
let len = (&*source).len();
let tail_len = len - mem::size_of::<T::Header>();
let slice = slice::from_raw_parts(source as *const T::Header, tail_len);
slice as *const [T::Header] as *const _
}
}
#[macro_export]
macro_rules! blob {
(
$(#[$wrapper_meta:meta])*
enum $wrapper_ident: ident {},
header: $header: ty,
$(#[$outer:meta])*
view: struct ref $tail_ident: ident {
$(
$(#[$meta:meta])*
$field: ident [$($len: tt)*],
)*
}
) => {
$(#[$wrapper_meta])*
pub enum $wrapper_ident {}
$(#[$outer])*
pub struct $tail_ident<'a> {
$(
$(#[$meta])*
pub $field: &'a [u8],
)*
}
impl<'a> $crate::helpers::BlobLayout for $wrapper_ident {
type Header = $header;
}
impl $crate::helpers::Blob<$wrapper_ident> {
#[allow(unused_assignments)]
pub fn clone_from_parts(header: &$header, tail: &$tail_ident) -> Box<Self> {
let header_len = core::mem::size_of_val(header);
let tail_len: usize = 0 $( + blob! { size: header, $($len)*} )*;
let mut boxed = vec![0u8; header_len + tail_len].into_boxed_slice();
let header_bytes = $crate::helpers::AsBytes::as_bytes(header);
&mut boxed[..header_len].copy_from_slice(header_bytes);
let mut offset = header_len;
$(
let field_len = blob! { size: header, $($len)*};
&mut boxed[offset..offset + field_len].copy_from_slice(tail.$field);
offset += field_len;
)*
Self::from_boxed(boxed)
}
}
impl $crate::helpers::Blob<$wrapper_ident> {
blob! { fields: ;
$(
$(#[$meta])*
$field [$($len)*],
)*
}
}
};
(
fields: $($prev: ident,)* ;
$(#[$curr_meta:meta])*
$curr: ident [$($curr_len: tt)*],
$(
$(#[$field_meta:meta])*
$field: ident [$($field_len: tt)*],
)*
) => {
$(#[$curr_meta])*
#[inline(always)]
pub fn $curr(&self) -> &[u8] {
let size: usize = blob! { size: self.header(), $($curr_len)* };
let offset = 0 $(+ self.$prev().len())*;
&self.tail()[offset..offset + size]
}
blob! {
fields: $($prev,)* $curr, ;
$(
$(#[$field_meta])*
$field [$($field_len)*],
)*
}
};
(fields: $($prev: ident,)* ; ) => {};
(size: $this: expr, $ident: ident) => { $this.$ident as usize };
(size: $this: expr, $expr: expr) => { $expr };
}
#[cfg(test)]
mod tests {
use super::*;
use crate::helpers::bytes::Pod;
#[test]
fn test() {
#[repr(C)]
pub struct Header {
count: u16,
}
blob! {
enum MyDynStruct {},
header: Header,
view: struct ref TailView {
some_member[count], }
}
unsafe impl Pod for Header {}
let inline = Blob::<MyDynStruct>::clone_from_parts(
&Header { count: 4 },
&TailView {
some_member: &[1u8, 2, 3, 4],
},
);
assert_eq!(6, mem::size_of_val(&*inline));
let inline = Blob::<MyDynStruct>::clone_from_parts(
&Header { count: 5 },
&TailView {
some_member: &[1u8, 2, 3, 4, 5],
},
);
assert_eq!(7, mem::size_of_val(&*inline));
}
}