use crate::error::{CapError, CapResult};
use ruvix_types::{CapHandle, CapRights, Capability, ObjectType, TaskHandle};
const CACHE_LINE_SIZE: usize = 64;
#[derive(Clone, Copy)]
#[repr(C, align(64))]
pub struct OptimizedCapSlot {
pub capability: Capability,
pub generation: u32,
pub flags: u32,
pub owner: TaskHandle,
pub depth: u8,
_reserved: [u8; 15],
}
impl OptimizedCapSlot {
pub const FLAG_VALID: u32 = 1 << 0;
pub const FLAG_ROOT: u32 = 1 << 1;
pub const FLAG_REVOKED: u32 = 1 << 2;
#[inline]
#[must_use]
pub const fn empty() -> Self {
Self {
capability: Capability::new(0, ObjectType::Task, CapRights::NONE, 0, 0),
generation: 0,
flags: 0,
owner: TaskHandle::null(),
depth: 0,
_reserved: [0; 15],
}
}
#[inline]
#[must_use]
pub const fn new_root(capability: Capability, generation: u32, owner: TaskHandle) -> Self {
Self {
capability,
generation,
flags: Self::FLAG_VALID | Self::FLAG_ROOT,
owner,
depth: 0,
_reserved: [0; 15],
}
}
#[inline]
#[must_use]
pub const fn new_derived(
capability: Capability,
generation: u32,
owner: TaskHandle,
depth: u8,
) -> Self {
Self {
capability,
generation,
flags: Self::FLAG_VALID,
owner,
depth,
_reserved: [0; 15],
}
}
#[inline]
#[must_use]
pub const fn is_valid(&self) -> bool {
(self.flags & Self::FLAG_VALID) != 0
}
#[inline]
#[must_use]
pub const fn is_root(&self) -> bool {
(self.flags & Self::FLAG_ROOT) != 0
}
#[inline]
#[must_use]
pub const fn is_revoked(&self) -> bool {
(self.flags & Self::FLAG_REVOKED) != 0
}
#[inline]
#[must_use]
pub const fn handle(&self, index: u32) -> CapHandle {
CapHandle::new(index, self.generation)
}
#[inline]
#[must_use]
pub const fn matches_handle(&self, handle: CapHandle) -> bool {
self.is_valid() && self.generation == handle.raw().generation
}
#[inline]
pub fn invalidate(&mut self) {
self.flags = 0;
self.generation = self.generation.wrapping_add(1);
}
#[inline]
pub fn revoke(&mut self) {
self.flags |= Self::FLAG_REVOKED;
self.flags &= !Self::FLAG_VALID;
}
}
impl Default for OptimizedCapSlot {
fn default() -> Self {
Self::empty()
}
}
const _: () = assert!(core::mem::size_of::<OptimizedCapSlot>() == CACHE_LINE_SIZE);
const MAX_BITMAP_CHUNKS: usize = 4;
pub struct OptimizedCapTable<const N: usize = 256> {
slots: [OptimizedCapSlot; N],
count: usize,
free_bitmap: [u64; MAX_BITMAP_CHUNKS],
}
impl<const N: usize> OptimizedCapTable<N> {
const BITMAP_CHUNKS: usize = if N <= 64 { 1 } else if N <= 128 { 2 } else if N <= 192 { 3 } else { 4 };
#[must_use]
pub fn new() -> Self {
assert!(N <= 256, "OptimizedCapTable supports max 256 slots");
let mut free_bitmap = [0u64; MAX_BITMAP_CHUNKS];
for i in 0..MAX_BITMAP_CHUNKS {
let remaining = N.saturating_sub(i * 64);
if remaining >= 64 {
free_bitmap[i] = u64::MAX;
} else if remaining > 0 {
free_bitmap[i] = (1u64 << remaining) - 1;
}
}
Self {
slots: [OptimizedCapSlot::empty(); N],
count: 0,
free_bitmap,
}
}
#[inline]
#[must_use]
pub const fn capacity(&self) -> usize {
N
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.count
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
#[inline]
#[must_use]
pub const fn is_full(&self) -> bool {
self.count >= N
}
#[inline]
pub fn get(&self, handle: CapHandle) -> CapResult<&Capability> {
let index = handle.raw().id as usize;
if index >= N {
return Err(CapError::InvalidHandle);
}
let slot = &self.slots[index];
if !slot.is_valid() {
return Err(CapError::InvalidHandle);
}
if slot.generation != handle.raw().generation {
return Err(CapError::StaleHandle);
}
Ok(&slot.capability)
}
#[inline]
pub fn get_slot(&self, handle: CapHandle) -> CapResult<&OptimizedCapSlot> {
let index = handle.raw().id as usize;
if index >= N {
return Err(CapError::InvalidHandle);
}
let slot = &self.slots[index];
if !slot.is_valid() {
return Err(CapError::InvalidHandle);
}
if slot.generation != handle.raw().generation {
return Err(CapError::StaleHandle);
}
Ok(slot)
}
pub fn allocate_root(
&mut self,
capability: Capability,
owner: TaskHandle,
) -> CapResult<CapHandle> {
let index = self.find_free_slot_ctz()?;
let slot = &mut self.slots[index];
let generation = slot.generation;
*slot = OptimizedCapSlot::new_root(capability, generation, owner);
self.mark_slot_used(index);
self.count += 1;
Ok(CapHandle::new(index as u32, generation))
}
pub fn allocate_derived(
&mut self,
capability: Capability,
owner: TaskHandle,
depth: u8,
) -> CapResult<CapHandle> {
let index = self.find_free_slot_ctz()?;
let slot = &mut self.slots[index];
let generation = slot.generation;
*slot = OptimizedCapSlot::new_derived(capability, generation, owner, depth);
self.mark_slot_used(index);
self.count += 1;
Ok(CapHandle::new(index as u32, generation))
}
pub fn deallocate(&mut self, handle: CapHandle) -> CapResult<()> {
let index = handle.raw().id as usize;
if index >= N {
return Err(CapError::InvalidHandle);
}
let slot = &mut self.slots[index];
if !slot.is_valid() {
return Err(CapError::InvalidHandle);
}
if slot.generation != handle.raw().generation {
return Err(CapError::StaleHandle);
}
slot.invalidate();
self.mark_slot_free(index);
self.count -= 1;
Ok(())
}
#[inline]
fn find_free_slot_ctz(&self) -> CapResult<usize> {
for (chunk_idx, &chunk) in self.free_bitmap.iter().enumerate() {
if chunk != 0 {
let bit_pos = chunk.trailing_zeros() as usize;
let slot_idx = chunk_idx * 64 + bit_pos;
if slot_idx < N {
return Ok(slot_idx);
}
}
}
Err(CapError::TableFull)
}
#[inline]
fn mark_slot_used(&mut self, index: usize) {
let chunk_idx = index / 64;
let bit_pos = index % 64;
self.free_bitmap[chunk_idx] &= !(1u64 << bit_pos);
}
#[inline]
fn mark_slot_free(&mut self, index: usize) {
let chunk_idx = index / 64;
let bit_pos = index % 64;
self.free_bitmap[chunk_idx] |= 1u64 << bit_pos;
}
pub fn iter(&self) -> impl Iterator<Item = (CapHandle, &OptimizedCapSlot)> {
self.slots
.iter()
.enumerate()
.filter(|(_, s)| s.is_valid())
.map(|(i, s)| (s.handle(i as u32), s))
}
}
impl<const N: usize> Default for OptimizedCapTable<N> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_optimized_cap_slot_size() {
assert_eq!(core::mem::size_of::<OptimizedCapSlot>(), 64);
assert_eq!(core::mem::align_of::<OptimizedCapSlot>(), 64);
}
#[test]
fn test_optimized_table_allocate_root() {
let mut table = OptimizedCapTable::<64>::new();
let owner = TaskHandle::new(1, 0);
let cap = Capability::new(100, ObjectType::VectorStore, CapRights::ALL, 0, 0);
let handle = table.allocate_root(cap, owner).unwrap();
assert_eq!(table.len(), 1);
let retrieved = table.get(handle).unwrap();
assert_eq!(retrieved.object_id, 100);
}
#[test]
fn test_optimized_table_deallocate() {
let mut table = OptimizedCapTable::<64>::new();
let owner = TaskHandle::new(1, 0);
let cap = Capability::new(100, ObjectType::Region, CapRights::READ, 0, 0);
let handle = table.allocate_root(cap, owner).unwrap();
assert_eq!(table.len(), 1);
table.deallocate(handle).unwrap();
assert_eq!(table.len(), 0);
assert!(matches!(table.get(handle), Err(CapError::InvalidHandle)));
}
#[test]
fn test_optimized_table_generation_counter() {
let mut table = OptimizedCapTable::<64>::new();
let owner = TaskHandle::new(1, 0);
let cap = Capability::new(100, ObjectType::Queue, CapRights::WRITE, 0, 0);
let handle1 = table.allocate_root(cap, owner).unwrap();
table.deallocate(handle1).unwrap();
let handle2 = table.allocate_root(cap, owner).unwrap();
assert_eq!(handle1.raw().id, handle2.raw().id);
assert_ne!(handle1.raw().generation, handle2.raw().generation);
assert!(matches!(table.get(handle1), Err(CapError::StaleHandle)));
}
#[test]
fn test_optimized_table_full() {
let mut table = OptimizedCapTable::<4>::new();
let owner = TaskHandle::new(1, 0);
let cap = Capability::new(100, ObjectType::Timer, CapRights::READ, 0, 0);
for _ in 0..4 {
table.allocate_root(cap, owner).unwrap();
}
assert!(table.is_full());
assert!(matches!(
table.allocate_root(cap, owner),
Err(CapError::TableFull)
));
}
#[test]
fn test_optimized_table_ctz_allocation() {
let mut table = OptimizedCapTable::<128>::new();
let owner = TaskHandle::new(1, 0);
let cap = Capability::new(100, ObjectType::Region, CapRights::READ, 0, 0);
let mut handles = [CapHandle::null(); 64];
for (i, handle) in handles.iter_mut().enumerate() {
*handle = table.allocate_root(cap, owner).unwrap();
assert_eq!(handle.raw().id as usize, i);
}
table.deallocate(handles[32]).unwrap();
let new_handle = table.allocate_root(cap, owner).unwrap();
assert_eq!(new_handle.raw().id, 32);
}
#[test]
fn test_slot_flags() {
let owner = TaskHandle::new(1, 0);
let cap = Capability::new(100, ObjectType::Region, CapRights::READ, 0, 0);
let root_slot = OptimizedCapSlot::new_root(cap, 0, owner);
assert!(root_slot.is_valid());
assert!(root_slot.is_root());
assert!(!root_slot.is_revoked());
let derived_slot = OptimizedCapSlot::new_derived(cap, 0, owner, 1);
assert!(derived_slot.is_valid());
assert!(!derived_slot.is_root());
assert!(!derived_slot.is_revoked());
let mut slot = root_slot;
slot.revoke();
assert!(!slot.is_valid());
assert!(slot.is_revoked());
}
}