use std::{
cell, marker,
mem::{MaybeUninit, offset_of, size_of},
ops, ptr,
};
use provided::account;
use crate::{
buffer::{
resident,
storage::{provided, provided::arena},
},
mem::raw::{self, Edge},
};
#[repr(transparent)]
#[derive(Default)]
pub(super) struct Counter(cell::Cell<u32>);
#[derive(Debug)]
#[repr(C)]
pub(in crate::buffer::storage::provided) struct Registration<'d> {
count: raw::Count,
pub(super) value: cell::UnsafeCell<MaybeUninit<resident::Charge<'d>>>,
}
#[derive(Clone, Copy)]
enum State {
Empty,
Ready,
External,
}
#[repr(C, align(8))]
pub(in crate::buffer::storage::provided) struct Entry<'d> {
state: cell::Cell<State>,
pub(super) resident: cell::Cell<MaybeUninit<u32>>,
pub(super) domain: &'d arena::State<'d>,
pub(super) next: arena::nodes::Link<'d>,
registration: Registration<'d>,
}
impl<'d> Entry<'d> {
pub(in crate::buffer::storage::provided) const fn new(
domain: &'d arena::State<'d>,
resident: MaybeUninit<u32>,
) -> Self {
Self {
state: cell::Cell::new(State::Empty),
resident: cell::Cell::new(resident),
domain,
next: cell::Cell::new(None),
registration: Registration {
count: raw::Count::empty(),
value: cell::UnsafeCell::new(MaybeUninit::uninit()),
},
}
}
pub(super) fn initialize(node: arena::Empty<'d>) -> arena::Ready<'d> {
unsafe { node.entry.as_ref() }.state.set(State::Ready);
arena::Ready { entry: node.entry }
}
pub(super) fn activate<T, L>(ready: arena::Returned<'d, T, L>) -> provided::Owner<'d> {
let entry = ready.node.entry;
unsafe { entry.as_ref() }.state.set(State::External);
provided::Owner { entry }
}
pub(in crate::buffer::storage::provided) unsafe fn resident(
pointer: ptr::NonNull<Self>,
) -> u32 {
unsafe { pointer.as_ref().resident.get().assume_init() }
}
pub(in crate::buffer::storage::provided) fn try_account(
owner: &provided::Owner<'d>,
budget: resident::Budget<'d>,
) -> Result<provided::Account<'d>, account::Cause> {
let pointer = owner.entry;
let entry: &'d Self = unsafe { pointer.as_ref() };
let registration = &entry.registration;
if !registration.is_empty() {
let charge = unsafe { (*registration.value.get()).assume_init_ref() };
if !ptr::eq(charge.budget().core(), budget.core()) {
return Err(account::Cause::Foreign);
}
unsafe { registration.try_retain() }.map_err(account::Cause::Retain)?;
} else {
let domain = entry.domain;
let count = domain.accounted.0.get();
let limit = domain.limit;
if count >= limit {
return Err(account::Cause::Exhausted { count, limit });
}
let charge = budget
.try_charge(unsafe { Self::resident(pointer) } as usize)
.map_err(account::Cause::Budget)?;
domain.accounted.0.set(count + 1);
unsafe {
(*registration.value.get()).write(charge);
registration.activate();
}
}
Ok(provided::Account {
registration: unsafe {
raw::Owner::from_raw(pointer.byte_add(offset_of!(Self, registration)).cast())
},
})
}
unsafe fn unaccount(&self) -> &'d arena::State<'d> {
let domain = self.domain;
domain.accounted.0.set(domain.accounted.0.get() - 1);
unsafe { (*self.registration.value.get()).assume_init_drop() };
domain
}
pub(super) unsafe fn release(owner: &mut provided::Owner<'d>) {
let entry = unsafe { owner.entry.as_ref() };
entry.state.set(State::Ready);
if entry.registration.is_empty() {
entry.domain.finish(arena::Ready { entry: owner.entry });
}
}
pub(super) fn recycle(node: arena::Ready<'d>) {
let entry = unsafe { node.entry.as_ref() };
entry.state.set(State::Empty);
entry.domain.push_free(arena::Empty { entry: node.entry });
}
pub(in crate::buffer::storage::provided) unsafe fn revoke<T, L>(
slot: &'d arena::Slot<'d, T, L>,
) -> ops::ControlFlow<(), arena::Returned<'d, T, L>> {
let pointer = ptr::NonNull::from(slot).cast::<Self>();
let entry = &slot.entry;
match entry.state.get() {
State::Empty => ops::ControlFlow::Break(()),
State::Ready | State::External => {
if !entry.registration.is_empty() {
unsafe { entry.unaccount() };
}
ops::ControlFlow::Continue(arena::Returned {
node: arena::Ready { entry: pointer },
domain: marker::PhantomData,
})
}
}
}
}
unsafe impl raw::Edge for Registration<'_> {
fn count(&self) -> &raw::Count {
&self.count
}
}
unsafe impl<'d> raw::Target for Registration<'d> {
unsafe fn destroy(pointer: ptr::NonNull<Self>) {
let pointer =
unsafe { pointer.byte_sub(offset_of!(Entry<'d>, registration)) }.cast::<Entry<'d>>();
let entry = unsafe { pointer.as_ref() };
let domain = unsafe { entry.unaccount() };
if matches!(entry.state.get(), State::Ready) {
domain.finish(arena::Ready { entry: pointer });
}
}
}
const _: () = {
assert!(size_of::<Registration<'_>>() == 3 * size_of::<usize>());
assert!(size_of::<Entry<'_>>() == 6 * size_of::<usize>());
assert!(align_of::<Entry<'_>>() == 8);
};