use core::{
alloc::{AllocError, Layout},
marker::PhantomCovariantLifetime,
ptr::NonNull,
};
use crate::{GuardedBuilder, guard::GuardTrait, guarded::GuardedBuilderInner};
#[derive(Debug)]
struct LinkedList<'builder> {
first: *mut GuardedBuilderInner,
last: *mut GuardedBuilderInner,
_pd: PhantomCovariantLifetime<'builder>,
}
pub struct Subscriber<'guard, 'builder> {
guard: &'guard mut dyn GuardTrait,
align: usize,
size: usize,
linked_list: Option<LinkedList<'builder>>,
}
impl<'guard, 'builder> Subscriber<'guard, 'builder> {
pub(crate) fn new(guard: &'guard mut impl GuardTrait) -> Self {
Self {
guard,
align: 1,
size: 0,
linked_list: None,
}
}
pub fn subscribe<T>(mut self, gsb: &'builder mut GuardedBuilder<'guard, T>) -> Self
where
T: ?Sized,
{
let GuardedBuilder { inner: node, .. } = gsb;
self.size = self.size.div_ceil(node.align) * node.align;
self.align = self.align.max(node.align);
self.size += node.size;
if let Some(linked_list) = &mut self.linked_list {
unsafe {
(*linked_list.last).next = Some(node);
}
linked_list.last = node;
} else {
self.linked_list = Some(LinkedList {
first: node,
last: node,
_pd: Default::default(),
})
}
self
}
pub fn allocate(self) -> Result<(), AllocError> {
let Self {
guard,
align,
size,
linked_list,
} = self;
let Some(linked_list) = linked_list else {
return Ok(());
};
let layout = Layout::from_size_align(size, align).unwrap();
let ptr = unsafe { guard.allocate(layout)? };
let ptr: NonNull<u8> = ptr.cast();
let mut ptr = ptr.as_ptr();
let mut it = Some(linked_list.first);
while let Some(node) = it {
let node = unsafe { &mut *node };
ptr = ptr.wrapping_add(ptr.align_offset(node.align));
node.ptr = Some(NonNull::new(ptr).unwrap());
it = node.next;
ptr = ptr.wrapping_add(node.size);
}
Ok(())
}
}