use core::{cell::UnsafeCell, marker::PhantomData};
use crate::{rendezvous::waiter::WaiterSlot, session::types::SessionId};
use super::AssocTable;
#[derive(Clone, Copy)]
struct AssocStorageParts {
entry_sids: *mut SessionId,
entry_lanes: *mut u8,
entry_states: *mut u8,
waiters: *mut WaiterSlot,
}
impl AssocTable {
pub(in crate::rendezvous) unsafe fn init_empty(dst: *mut Self) {
unsafe {
core::ptr::addr_of_mut!((*dst).lane_base).write(0);
core::ptr::addr_of_mut!((*dst).lane_slots).write(0);
core::ptr::addr_of_mut!((*dst).assoc_slots).write(0);
core::ptr::addr_of_mut!((*dst).entry_sids)
.write(UnsafeCell::new(core::ptr::null_mut()));
core::ptr::addr_of_mut!((*dst).entry_lanes)
.write(UnsafeCell::new(core::ptr::null_mut()));
core::ptr::addr_of_mut!((*dst).entry_states)
.write(UnsafeCell::new(core::ptr::null_mut()));
core::ptr::addr_of_mut!((*dst).inline_waiter)
.write(UnsafeCell::new(WaiterSlot::empty()));
core::ptr::addr_of_mut!((*dst).waiters).write(UnsafeCell::new(core::ptr::null_mut()));
core::ptr::addr_of_mut!((*dst)._no_send_sync).write(PhantomData);
}
}
#[inline]
pub(in crate::rendezvous) const fn storage_align() -> usize {
let entry_align = core::mem::align_of::<SessionId>();
let waiter_align = core::mem::align_of::<WaiterSlot>();
if entry_align > waiter_align {
entry_align
} else {
waiter_align
}
}
#[inline]
const fn align_up(value: usize, align: usize) -> usize {
if align == 0 {
crate::invariant();
}
let mask = align - 1;
if value > usize::MAX - mask {
crate::invariant();
}
(value + mask) & !mask
}
#[inline]
const fn checked_add_usize(lhs: usize, rhs: usize) -> usize {
if lhs > usize::MAX - rhs {
crate::invariant();
}
lhs + rhs
}
#[inline]
const fn checked_mul_usize(lhs: usize, rhs: usize) -> usize {
if lhs != 0 && rhs > usize::MAX / lhs {
crate::invariant();
}
lhs * rhs
}
#[inline]
const fn checked_sub_usize(lhs: usize, rhs: usize) -> usize {
if lhs < rhs {
crate::invariant();
}
lhs - rhs
}
#[inline]
const fn overflow_waiter_slots(assoc_slots: usize) -> usize {
assoc_slots.saturating_sub(1)
}
#[inline]
pub(in crate::rendezvous) const fn storage_bytes(assoc_slots: usize) -> usize {
let sid_bytes = Self::checked_mul_usize(assoc_slots, core::mem::size_of::<SessionId>());
let lane_offset = Self::align_up(sid_bytes, core::mem::align_of::<u8>());
let lane_bytes = Self::checked_mul_usize(assoc_slots, core::mem::size_of::<u8>());
let state_offset = Self::align_up(
Self::checked_add_usize(lane_offset, lane_bytes),
core::mem::align_of::<u8>(),
);
let state_bytes = Self::checked_mul_usize(assoc_slots, core::mem::size_of::<u8>());
let waiter_offset = Self::align_up(
Self::checked_add_usize(state_offset, state_bytes),
core::mem::align_of::<WaiterSlot>(),
);
Self::checked_add_usize(
waiter_offset,
Self::checked_mul_usize(
Self::overflow_waiter_slots(assoc_slots),
core::mem::size_of::<WaiterSlot>(),
),
)
}
unsafe fn storage_parts(storage: *mut u8, assoc_slots: usize) -> AssocStorageParts {
let entry_sids = storage.cast::<SessionId>();
let lane_offset = Self::checked_sub_usize(
Self::align_up(
Self::checked_add_usize(
storage as usize,
Self::checked_mul_usize(assoc_slots, core::mem::size_of::<SessionId>()),
),
core::mem::align_of::<u8>(),
),
storage as usize,
);
let state_offset = Self::checked_sub_usize(
Self::align_up(
Self::checked_add_usize(
Self::checked_add_usize(storage as usize, lane_offset),
Self::checked_mul_usize(assoc_slots, core::mem::size_of::<u8>()),
),
core::mem::align_of::<u8>(),
),
storage as usize,
);
let waiter_offset = Self::checked_sub_usize(
Self::align_up(
Self::checked_add_usize(
Self::checked_add_usize(storage as usize, state_offset),
Self::checked_mul_usize(assoc_slots, core::mem::size_of::<u8>()),
),
core::mem::align_of::<WaiterSlot>(),
),
storage as usize,
);
let (entry_lanes, entry_states, waiters) =
unsafe {
(
storage.add(lane_offset).cast::<u8>(),
storage.add(state_offset).cast::<u8>(),
storage.add(waiter_offset).cast::<WaiterSlot>(),
)
};
AssocStorageParts {
entry_sids,
entry_lanes,
entry_states,
waiters,
}
}
#[inline]
pub(super) fn waiter_ptr(&self, idx: usize) -> *mut WaiterSlot {
if idx >= self.assoc_slots() {
crate::invariant();
}
if idx == 0 {
self.inline_waiter.get()
} else {
self.waiters_ptr().wrapping_add(idx - 1)
}
}
pub(super) unsafe fn remove_entry(&self, idx: usize) {
if idx >= self.assoc_slots() {
crate::invariant();
}
let lanes = self.entry_lanes_ptr();
let sids = self.entry_sids_ptr();
let states = self.entry_states_ptr();
unsafe {
let mut last = self.assoc_slots();
loop {
if last == 0 {
crate::invariant();
}
last -= 1;
if Self::entry_count(*states.add(last)) != 0 {
break;
}
}
(*self.waiter_ptr(idx)).clear();
if last != idx {
lanes.add(idx).write(*lanes.add(last));
sids.add(idx).write(*sids.add(last));
states.add(idx).write(*states.add(last));
if let Some(waker) = (*self.waiter_ptr(last)).take() {
(*self.waiter_ptr(idx)).set_owned(waker);
}
}
lanes.add(last).write(0);
sids.add(last).write(SessionId::new(0));
states.add(last).write(Self::EMPTY_ENTRY_STATE);
(*self.waiter_ptr(last)).clear();
}
}
unsafe fn bind_storage(
&mut self,
lane_base: u32,
lane_slots: usize,
assoc_slots: usize,
parts: AssocStorageParts,
) {
if lane_slots > usize::from(u16::MAX) || assoc_slots > usize::from(u16::MAX) {
crate::invariant();
}
unsafe {
let mut idx = 0usize;
while idx < assoc_slots {
parts.entry_sids.add(idx).write(SessionId::new(0));
parts.entry_lanes.add(idx).write(0);
parts.entry_states.add(idx).write(Self::EMPTY_ENTRY_STATE);
idx += 1;
}
let mut waiter_idx = 0usize;
while waiter_idx < Self::overflow_waiter_slots(assoc_slots) {
WaiterSlot::init_empty(parts.waiters.add(waiter_idx));
waiter_idx += 1;
}
}
self.lane_base = lane_base;
self.lane_slots = lane_slots as u16;
self.assoc_slots = assoc_slots as u16;
*self.inline_waiter.get_mut() = WaiterSlot::empty();
*self.entry_sids.get_mut() = parts.entry_sids;
*self.entry_lanes.get_mut() = parts.entry_lanes;
*self.entry_states.get_mut() = parts.entry_states;
*self.waiters.get_mut() = parts.waiters;
}
pub(in crate::rendezvous) unsafe fn bind_from_storage(
&mut self,
storage: *mut u8,
lane_base: u32,
lane_slots: usize,
assoc_slots: usize,
) {
let parts = unsafe {
Self::storage_parts(storage, assoc_slots)
};
unsafe {
self.bind_storage(lane_base, lane_slots, assoc_slots, parts);
}
}
#[inline]
pub(in crate::rendezvous) fn is_bound(&self) -> bool {
!self.entry_sids_ptr().is_null()
}
pub(in crate::rendezvous) unsafe fn init_replacement_storage(
&self,
storage: *mut u8,
lane_base: u32,
lane_slots: usize,
assoc_slots: usize,
) {
let source_base = self.lane_base;
let source_slots = self.assoc_slots();
let source_lanes = self.entry_lanes_ptr();
let source_sids = self.entry_sids_ptr();
let source_states = self.entry_states_ptr();
let parts = unsafe {
Self::storage_parts(storage, assoc_slots)
};
unsafe {
let mut idx = 0usize;
while idx < assoc_slots {
parts.entry_sids.add(idx).write(SessionId::new(0));
parts.entry_lanes.add(idx).write(0);
parts.entry_states.add(idx).write(Self::EMPTY_ENTRY_STATE);
idx += 1;
}
let mut waiter_idx = 0usize;
while waiter_idx < Self::overflow_waiter_slots(assoc_slots) {
WaiterSlot::init_empty(parts.waiters.add(waiter_idx));
waiter_idx += 1;
}
}
let mut source_idx = 0usize;
let mut dst_idx = 0usize;
while source_idx < source_slots {
let source_state = unsafe {
*source_states.add(source_idx)
};
if Self::entry_count(source_state) == 0 {
source_idx += 1;
continue;
}
let lane = source_base
+ unsafe {
*source_lanes.add(source_idx)
} as u32;
if lane >= lane_base {
let new_offset = lane - lane_base;
if (new_offset as usize) < lane_slots {
if new_offset > u8::MAX as u32 || dst_idx >= assoc_slots {
crate::invariant();
}
unsafe {
parts
.entry_sids
.add(dst_idx)
.write(*source_sids.add(source_idx));
parts.entry_lanes.add(dst_idx).write(new_offset as u8);
parts.entry_states.add(dst_idx).write(source_state);
if dst_idx == 0 {
if source_idx != 0 {
crate::invariant();
}
} else {
WaiterSlot::init_clone_from(
parts.waiters.add(dst_idx - 1),
&*self.waiter_ptr(source_idx),
);
}
}
dst_idx += 1;
}
}
source_idx += 1;
}
}
pub(in crate::rendezvous) fn clear_current_overflow_waiters(&self) {
let waiters = self.waiters_ptr();
let mut idx = 0usize;
while idx < Self::overflow_waiter_slots(self.assoc_slots()) {
unsafe {
(*waiters.add(idx)).clear();
}
idx += 1;
}
}
pub(in crate::rendezvous) unsafe fn commit_storage(
&mut self,
storage: *mut u8,
lane_base: u32,
lane_slots: usize,
assoc_slots: usize,
) {
let parts = unsafe {
Self::storage_parts(storage, assoc_slots)
};
if lane_slots > usize::from(u16::MAX) || assoc_slots > usize::from(u16::MAX) {
crate::invariant();
}
self.lane_base = lane_base;
self.lane_slots = lane_slots as u16;
self.assoc_slots = assoc_slots as u16;
*self.entry_sids.get_mut() = parts.entry_sids;
*self.entry_lanes.get_mut() = parts.entry_lanes;
*self.entry_states.get_mut() = parts.entry_states;
*self.waiters.get_mut() = parts.waiters;
}
}