#![cfg_attr(doccfg, feature(doc_auto_cfg))]
#![cfg_attr(doccfg, feature(doc_cfg))]
#![no_std]
#[cfg(feature = "serde")]
mod serde;
mod builder;
pub use builder::FamBoxBuilder;
extern crate alloc;
use core::{
any,
marker::PhantomData,
mem::{self, align_of, align_of_val, size_of, size_of_val, ManuallyDrop},
ops::ControlFlow,
ptr::NonNull,
};
#[repr(C)]
#[derive(Debug)]
struct __IncompleteArrayField<T>([T; 0]);
impl<T> __IncompleteArrayField<T> {
#[allow(dead_code)]
const fn new() -> Self {
Self([])
}
}
pub unsafe trait FamHeader {
type Element;
fn fam_len(&self) -> usize;
#[inline]
fn total_size(&self) -> usize {
debug_assert_eq!(
(align_of_val(&self) + size_of_val(&self)) % align_of::<Self::Element>(),
0,
"FamHeader implemented for {} but its align + size doesn't match trait requirement 2",
any::type_name_of_val(&self)
);
self.fam_len() * mem::size_of::<Self::Element>() + mem::size_of_val(self)
}
}
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Owned;
#[derive(Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct BorrowedMut<'a>(core::marker::PhantomData<&'a ()>);
impl<'a> core::fmt::Debug for BorrowedMut<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BorrowedMut").finish()
}
}
#[derive(Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct BorrowedShared<'a>(core::marker::PhantomData<&'a ()>);
impl<'a> core::fmt::Debug for BorrowedShared<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BorrowedShared").finish()
}
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::Owned {}
impl<'a> Sealed for super::BorrowedMut<'a> {}
impl<'a> Sealed for super::BorrowedShared<'a> {}
}
pub trait Owner: sealed::Sealed {
const OWNED: bool;
}
impl Owner for Owned {
const OWNED: bool = true;
}
impl<'a> Owner for BorrowedMut<'a> {
const OWNED: bool = false;
}
impl<'a> Owner for BorrowedShared<'a> {
const OWNED: bool = false;
}
pub trait Exclusive: Owner {}
impl Exclusive for Owned {}
impl<'a> Exclusive for BorrowedMut<'a> {}
pub trait Borrowed: Owner {}
impl<'a> Borrowed for BorrowedMut<'a> {}
impl<'a> Borrowed for BorrowedShared<'a> {}
pub struct FamBox<H: FamHeader, O: Owner> {
ptr: NonNull<u8>,
ty: PhantomData<(H, O)>,
}
impl<H: FamHeader + Clone> Clone for FamBox<H, Owned>
where
H::Element: Clone,
{
#[inline]
fn clone(&self) -> Self {
self.into_owned()
}
}
impl<H: FamHeader + Default> Default for FamBox<H, Owned>
where
H::Element: Default,
{
#[inline]
fn default() -> Self {
Self::from_fn(H::default(), |_| H::Element::default())
}
}
impl<H: FamHeader> FamBox<H, Owned> {
pub fn from_fn<F>(header: H, mut cb: F) -> Self
where
F: FnMut(usize) -> H::Element,
{
let mut builder = match FamBoxBuilder::new(header) {
ControlFlow::Continue(builder) => builder,
ControlFlow::Break(finished) => return finished,
};
let mut i = 0;
loop {
builder = match builder.add_element(cb(i)) {
ControlFlow::Continue(unfinished) => unfinished,
ControlFlow::Break(finished) => return finished,
};
i += 1;
}
}
pub fn leak(self) -> NonNull<H> {
ManuallyDrop::new(self).ptr.cast()
}
}
impl<'a, H: FamHeader> FamBox<H, BorrowedMut<'a>> {
#[inline]
pub unsafe fn from_slice_mut(slice: &'a mut [u8]) -> Self {
debug_assert!(
matches!(slice.as_ptr().align_offset(align_of::<H>()), 0 | usize::MAX),
"alignment doesn't match H"
);
Self {
ty: PhantomData,
ptr: NonNull::from(slice).cast(),
}
}
}
impl<'a, H: FamHeader> Clone for FamBox<H, BorrowedShared<'a>> {
#[inline]
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
ty: self.ty,
}
}
}
impl<'a, H: FamHeader> FamBox<H, BorrowedShared<'a>> {
#[inline]
pub unsafe fn from_slice(slice: &'a [u8]) -> Self {
debug_assert!(
matches!(slice.as_ptr().align_offset(align_of::<H>()), 0 | usize::MAX),
"alignment doesn't match H"
);
Self {
ty: PhantomData,
ptr: NonNull::from(slice).cast(),
}
}
}
impl<'a, H: FamHeader + PartialEq> PartialEq for FamBox<H, BorrowedShared<'a>>
where
H::Element: PartialEq,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr || self.as_parts() == other.as_parts()
}
}
impl<H: FamHeader + PartialEq, O: Exclusive> PartialEq for FamBox<H, O>
where
H::Element: PartialEq,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_parts() == other.as_parts()
}
}
impl<H: FamHeader, O: Exclusive> AsMut<H> for FamBox<H, O> {
#[inline]
fn as_mut(&mut self) -> &mut H {
self.header_mut()
}
}
impl<H: FamHeader, O: Exclusive> AsMut<[H::Element]> for FamBox<H, O> {
#[inline]
fn as_mut(&mut self) -> &mut [H::Element] {
self.fam_mut()
}
}
impl<H: FamHeader, O: Exclusive> FamBox<H, O> {
#[inline]
pub fn header_mut(&mut self) -> &mut H {
unsafe { self.ptr.cast().as_mut() }
}
#[inline]
pub fn fam_mut(&mut self) -> &mut [H::Element] {
let fam_len = self.header().fam_len();
unsafe {
let fam = self.ptr.as_ptr().add(size_of::<H>()).cast();
core::slice::from_raw_parts_mut(fam, fam_len)
}
}
#[inline]
pub fn as_parts_mut(&mut self) -> (&mut H, &mut [H::Element]) {
let mut header = self.ptr.cast();
let fam = self.fam_mut();
(unsafe { header.as_mut() }, fam)
}
}
impl<H: FamHeader + core::fmt::Debug, O: Owner + core::fmt::Debug + Default> core::fmt::Debug
for FamBox<H, O>
where
H::Element: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("FamBox")
.field("ty", &O::default())
.field("header", self.header())
.field("fam", &self.fam())
.finish()
}
}
impl<H: FamHeader + Eq, O: Owner> Eq for FamBox<H, O>
where
H::Element: Eq,
Self: PartialEq,
{
}
impl<Header: FamHeader + core::hash::Hash, O: Owner> core::hash::Hash for FamBox<Header, O>
where
Header::Element: core::hash::Hash,
{
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_parts().hash(state);
}
}
impl<H: FamHeader + PartialOrd, O: Owner> PartialOrd for FamBox<H, O>
where
H::Element: PartialOrd,
Self: PartialEq,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.as_parts().partial_cmp(&other.as_parts())
}
}
impl<H: FamHeader + Ord, O: Owner> Ord for FamBox<H, O>
where
H::Element: Ord,
Self: PartialEq,
{
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_parts().cmp(&other.as_parts())
}
}
impl<H: FamHeader, O: Owner> AsRef<H> for FamBox<H, O> {
#[inline]
fn as_ref(&self) -> &H {
self.header()
}
}
impl<H: FamHeader, O: Owner> AsRef<[H::Element]> for FamBox<H, O> {
#[inline]
fn as_ref(&self) -> &[H::Element] {
self.fam()
}
}
impl<H: FamHeader, O: Owner> FamBox<H, O> {
pub unsafe fn from_raw(ptr: NonNull<H>) -> Self {
Self {
ty: PhantomData,
ptr: ptr.cast(),
}
}
pub fn into_owned(&self) -> FamBox<H, Owned>
where
H: Clone,
H::Element: Clone,
{
let (header, fam) = self.as_parts();
FamBox::from_fn(header.clone(), |i| fam[i].clone())
}
#[inline]
pub unsafe fn buffer(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.header().total_size()) }
}
#[inline]
pub fn header(&self) -> &H {
unsafe { self.ptr.cast().as_ref() }
}
#[inline]
pub fn fam(&self) -> &[H::Element] {
let fam_len = self.header().fam_len();
unsafe {
let fam = self.ptr.as_ptr().add(size_of::<H>()).cast();
core::slice::from_raw_parts(fam, fam_len)
}
}
#[inline]
pub fn as_parts(&self) -> (&H, &[H::Element]) {
(unsafe { self.ptr.cast().as_ref() }, self.fam())
}
}
impl<H: FamHeader, O: Owner> Drop for FamBox<H, O> {
#[inline]
fn drop(&mut self) {
if !O::OWNED {
return;
}
drop(unsafe { FamBoxBuilder::from_built(self.ptr.cast::<H>()) });
}
}
unsafe impl<H: FamHeader + Send> Send for FamBox<H, Owned> where H::Element: Send {}
unsafe impl<'a, H: FamHeader + Send> Send for FamBox<H, BorrowedMut<'a>> where H::Element: Send {}
unsafe impl<H: FamHeader + Sync> Sync for FamBox<H, Owned> where H::Element: Sync {}
unsafe impl<'a, H: FamHeader + Sync> Sync for FamBox<H, BorrowedMut<'a>> where H::Element: Sync {}
unsafe impl<'a, H: FamHeader + Sync> Send for FamBox<H, BorrowedShared<'a>> where H::Element: Sync {}
unsafe impl<'a, H: FamHeader + Sync> Sync for FamBox<H, BorrowedShared<'a>> where H::Element: Sync {}
pub type FamBoxOwned<H> = FamBox<H, Owned>;
pub type FamBoxMut<'a, H> = FamBox<H, BorrowedMut<'a>>;
pub type FamBoxShared<'a, H> = FamBox<H, BorrowedShared<'a>>;
#[cfg(test)]
mod tests {
use super::*;
use alloc::{rc::Rc, string::String};
use core::{
cell::Cell, fmt::Write, ops::ControlFlow, panic::AssertUnwindSafe, sync::atomic::AtomicBool,
};
#[repr(C)]
#[derive(Debug)]
struct MsgHeader {
this: u128,
len: u64,
that: u8,
val: __IncompleteArrayField<u16>,
}
#[repr(C)]
#[derive(Debug)]
struct MsgNoPadding {
this: u128, len: u64, that: u8, padding: [u8; 7], val: __IncompleteArrayField<u16>, }
impl Clone for MsgHeader {
fn clone(&self) -> Self {
Self {
this: self.this.clone(),
len: self.len.clone(),
that: self.that.clone(),
val: __IncompleteArrayField::new(),
}
}
}
impl PartialEq for MsgHeader {
fn eq(&self, other: &Self) -> bool {
self.this == other.this && self.len == other.len && self.that == other.that
}
}
impl Eq for MsgHeader {}
impl PartialOrd for MsgHeader {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
match self.this.partial_cmp(&other.this) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.len.partial_cmp(&other.len) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
self.that.partial_cmp(&other.that)
}
}
unsafe impl FamHeader for MsgHeader {
type Element = u16;
#[inline]
fn fam_len(&self) -> usize {
let bytes_in_fam = self.len as usize - size_of::<Self>();
bytes_in_fam / size_of::<u16>()
}
}
impl Clone for MsgNoPadding {
fn clone(&self) -> Self {
Self {
this: self.this.clone(),
len: self.len.clone(),
that: self.that.clone(),
padding: self.padding.clone(),
val: __IncompleteArrayField::new(),
}
}
}
impl PartialEq for MsgNoPadding {
fn eq(&self, other: &Self) -> bool {
self.this == other.this && self.len == other.len && self.that == other.that
}
}
impl Eq for MsgNoPadding {}
unsafe impl FamHeader for MsgNoPadding {
type Element = u16;
#[inline]
fn fam_len(&self) -> usize {
let bytes_in_fam = self.len as usize - size_of::<Self>();
bytes_in_fam / size_of::<u16>()
}
}
const TEST_MSG: MsgHeader = MsgHeader {
this: 42,
len: size_of::<MsgHeader>() as u64 + 13,
that: 55,
val: __IncompleteArrayField::new(),
};
const TEST_MSG_NO_PADDING: MsgNoPadding = MsgNoPadding {
this: 42,
len: size_of::<MsgHeader>() as u64 + 13,
that: 55,
padding: [0; 7],
val: __IncompleteArrayField::new(),
};
unsafe fn copy_for_test(buffer: &[u8]) -> &'static mut [u8] {
let layout =
alloc::alloc::Layout::from_size_align(buffer.len(), align_of::<MsgHeader>()).unwrap();
let ptr = alloc::alloc::alloc(layout);
core::ptr::copy(buffer.as_ptr(), ptr, buffer.len());
core::slice::from_raw_parts_mut(ptr, buffer.len())
}
unsafe fn free_for_test(ptr: *mut u8, len: usize) {
let layout = alloc::alloc::Layout::from_size_align(len, align_of::<MsgHeader>()).unwrap();
alloc::alloc::dealloc(ptr, layout);
}
#[test]
fn fambox_builder() {
let ControlFlow::Continue(mut builder) = FamBoxBuilder::new(TEST_MSG) else {
panic!("done early")
};
let mut i = 0;
let mut next = builder.add_element(0);
while let ControlFlow::Continue(x) = next {
i += 1;
builder = x;
next = builder.add_element(i);
}
let ControlFlow::Break(_fambox) = next else {
panic!("loop ended")
};
}
#[test]
fn fambox_builder_no_elements() {
struct H(u8, [u32; 0]);
unsafe impl FamHeader for H {
type Element = u32;
fn fam_len(&self) -> usize {
self.0.into()
}
}
assert!(FamBoxBuilder::new(H(0, [])).is_break())
}
#[test]
fn fambox_builder_zst_header_and_some_elements() {
struct H([u32; 0]);
unsafe impl FamHeader for H {
type Element = u32;
fn fam_len(&self) -> usize {
5
}
}
assert!(FamBoxBuilder::new(H([])).is_continue())
}
#[test]
fn fambox_builder_zst() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ZST;
unsafe impl FamHeader for ZST {
type Element = ();
fn fam_len(&self) -> usize {
0
}
}
let ControlFlow::Break(_fambox) = FamBoxBuilder::new(ZST) else {
panic!("done late")
};
}
#[test]
fn fambox_builder_drop() {
let ControlFlow::Continue(builder) = FamBoxBuilder::new(TEST_MSG) else {
panic!("done early")
};
let next = builder.add_element(0);
drop(next)
}
#[test]
fn parts_with_padding() {
let own = FamBox::from_fn(TEST_MSG, |i| i as _);
let (header, fam) = own.as_parts();
assert_eq!(*header, TEST_MSG);
assert_eq!(fam, core::array::from_fn::<_, 6, _>(|i| i as _));
}
#[test]
fn parts_eq() {
let own = FamBox::from_fn(TEST_MSG_NO_PADDING, |i| i as _);
let share = unsafe { FamBox::<MsgNoPadding, _>::from_slice(own.buffer()) };
let buffer_mut = unsafe { copy_for_test(own.buffer()) };
let exl = unsafe { FamBox::<MsgNoPadding, _>::from_slice_mut(buffer_mut) };
assert_eq!(own.as_parts(), share.as_parts());
assert_eq!(own.as_parts(), exl.as_parts());
drop(exl);
unsafe { free_for_test(buffer_mut.as_mut_ptr(), buffer_mut.len()) };
}
#[test]
fn debug_impls() {
let own = FamBox::from_fn(TEST_MSG_NO_PADDING, |i| i as _);
let share = unsafe { FamBox::<MsgNoPadding, _>::from_slice(own.buffer()) };
let buffer_mut = unsafe { copy_for_test(own.buffer()) };
let exl = unsafe { FamBox::<MsgNoPadding, _>::from_slice_mut(buffer_mut) };
let mut s = String::new();
writeln!(s, "{own:?}").unwrap();
writeln!(s, "{share:?}").unwrap();
writeln!(s, "{exl:?}").unwrap();
drop(exl);
unsafe { free_for_test(buffer_mut.as_mut_ptr(), buffer_mut.len()) };
}
#[test]
fn clone_impls() {
let own = FamBox::from_fn(TEST_MSG_NO_PADDING, |i| i as _);
let share = unsafe { FamBox::<MsgNoPadding, _>::from_slice(own.buffer()) };
assert_eq!(own.clone().as_parts(), share.clone().as_parts());
assert_eq!(unsafe { own.clone().buffer() }, unsafe {
share.clone().buffer()
});
assert_eq!(own, own.clone());
assert_eq!(share, share.clone());
}
#[test]
fn zst() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ZST;
unsafe impl FamHeader for ZST {
type Element = ();
fn fam_len(&self) -> usize {
0
}
}
let own = FamBox::from_fn(ZST, |_| ());
let share = unsafe { FamBox::<ZST, _>::from_slice(own.buffer()) };
let mut buf = unsafe { own.buffer() }.to_vec();
let exl = unsafe { FamBox::<ZST, _>::from_slice_mut(&mut buf) };
assert_eq!(unsafe { own.buffer() }.len(), 0);
assert_eq!(own.as_parts(), share.as_parts());
assert_eq!(own.as_parts(), exl.as_parts());
let mut s = String::new();
writeln!(s, "{own:?}").unwrap();
writeln!(s, "{share:?}").unwrap();
writeln!(s, "{exl:?}").unwrap();
}
#[test]
fn zst_fam_element() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ZST(u8);
unsafe impl FamHeader for ZST {
type Element = ();
fn fam_len(&self) -> usize {
0
}
}
let own = FamBox::from_fn(ZST(0), |_| ());
let share = unsafe { FamBox::<ZST, _>::from_slice(own.buffer()) };
let mut buf = unsafe { own.buffer() }.to_vec();
let exl = unsafe { FamBox::<ZST, _>::from_slice_mut(&mut buf) };
assert_eq!(unsafe { own.buffer() }.len(), 1);
assert_eq!(own.as_parts(), share.as_parts());
assert_eq!(own.as_parts(), exl.as_parts());
let mut s = String::new();
writeln!(s, "{own:?}").unwrap();
writeln!(s, "{share:?}").unwrap();
writeln!(s, "{exl:?}").unwrap();
}
#[test]
fn zst_header() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ZST;
unsafe impl FamHeader for ZST {
type Element = u8;
fn fam_len(&self) -> usize {
3
}
}
let own = FamBox::from_fn(ZST, |i| i as _);
let share = unsafe { FamBox::<ZST, _>::from_slice(own.buffer()) };
let mut buf = unsafe { own.buffer() }.to_vec();
let exl = unsafe { FamBox::<ZST, _>::from_slice_mut(&mut buf) };
assert_eq!(unsafe { own.buffer() }.len(), 3);
assert_eq!(own.as_parts(), share.as_parts());
assert_eq!(own.as_parts(), exl.as_parts());
let mut s = String::new();
writeln!(s, "{own:?}").unwrap();
writeln!(s, "{share:?}").unwrap();
writeln!(s, "{exl:?}").unwrap();
}
#[test]
fn leak_from_raw() {
let own = FamBox::from_fn(TEST_MSG, |i| i as _);
let own_clone = own.clone();
let from_leak = unsafe { FamBox::from_raw(own.leak()) };
assert_eq!(own_clone, from_leak);
}
#[test]
fn drop_cnt() {
struct H(u8, [DropCnt; 0]);
unsafe impl FamHeader for H {
type Element = DropCnt;
fn fam_len(&self) -> usize {
self.0.into()
}
}
static H_DROPPED: AtomicBool = AtomicBool::new(false);
impl Drop for H {
fn drop(&mut self) {
H_DROPPED.store(true, core::sync::atomic::Ordering::Relaxed);
}
}
#[derive(Debug, Clone)]
struct DropCnt(Rc<Cell<u8>>);
impl Drop for DropCnt {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
let item = DropCnt(Rc::new(Cell::new(0)));
let own = FamBox::from_fn(H(13, []), |_| item.clone());
drop(own);
assert_eq!(item.0.get(), 13);
assert!(H_DROPPED.load(core::sync::atomic::Ordering::Relaxed));
}
#[test]
fn callback_panic() {
struct H(u8, Rc<Cell<u8>>, [DropCnt; 0]);
unsafe impl FamHeader for H {
type Element = DropCnt;
fn fam_len(&self) -> usize {
self.0.into()
}
}
static H_DROPPED: AtomicBool = AtomicBool::new(false);
impl Drop for H {
fn drop(&mut self) {
H_DROPPED.store(true, core::sync::atomic::Ordering::Relaxed);
self.1.set(self.1.get() + 1);
}
}
#[derive(Debug, Clone)]
struct DropCnt(Rc<Cell<u8>>);
impl Drop for DropCnt {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
extern crate std;
let item = DropCnt(Rc::new(Cell::new(0)));
let h_drop = Rc::new(Cell::new(0));
if !std::panic::catch_unwind(AssertUnwindSafe(|| {
drop(FamBox::from_fn(H(4, h_drop.clone(), []), |i| {
if i > 2 {
panic!("oops")
} else {
item.clone()
}
}))
}))
.is_err()
{
panic!("didn't panic when constructing")
}
assert_eq!(item.0.get(), 3);
assert!(H_DROPPED.load(core::sync::atomic::Ordering::Relaxed));
assert_eq!(h_drop.get(), 1);
}
#[test]
fn as_ref_as_mut() {
let mut own = FamBox::from_fn(TEST_MSG, |i| i as _);
let _header: &MsgHeader = own.as_ref();
let _fam: &[<MsgHeader as FamHeader>::Element] = own.as_ref();
let header_mut: &mut MsgHeader = own.as_mut();
header_mut.that += 1;
let fam_mut: &mut [<MsgHeader as FamHeader>::Element] = own.as_mut();
fam_mut[3] = 4;
}
#[test]
fn eq_test() {
let mut own = FamBox::from_fn(TEST_MSG, |i| i as _);
let own2 = FamBox::from_fn(TEST_MSG, |i| i as _);
assert_eq!(own, own2);
own.fam_mut()[3] += 1;
assert_ne!(own, own2);
own.fam_mut()[3] -= 1;
assert_eq!(own, own2);
own.header_mut().that += 1;
assert_ne!(own, own2);
own.header_mut().that -= 1;
assert_eq!(own, own2);
}
#[test]
fn ord_test() {
let mut own = FamBox::from_fn(TEST_MSG, |i| i as _);
let mut own2 = FamBox::from_fn(TEST_MSG, |i| i as _);
assert_eq!(own, own2);
own.fam_mut()[3] += 1;
assert!(own > own2);
own2.header_mut().that += 1;
assert!(own < own2);
}
}