//! Native fixed-capacity handle table. See os-handles/CONTRACT.md.
//! A caller must bind `P` to a live kernel process ID before invoking this
//! table. `Object` IDs are kernel-owned names, never user pointers.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Handle(u64);
impl Handle {
pub const fn from_raw(raw: u64) -> Self { Self(raw) }
pub const fn raw(self) -> u64 { self.0 }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Object {
File(u64),
Channel(u64),
Task(u64),
}
impl Object {
const fn id(self) -> u64 {
match self {
Self::File(id) | Self::Channel(id) | Self::Task(id) => id,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
BadFd,
InvalidObject,
Full,
IdExhausted,
}
impl Error {
pub const fn errno(self) -> u64 {
match self {
Self::BadFd => 9,
Self::InvalidObject => 22,
Self::Full => 24,
Self::IdExhausted => 75,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Entry
{
handle: Handle,
owner: P,
object: Object,
}
/// Complete scalar audit state. It does not expose a mutable table entry.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Snapshot
{
pub next: u64,
pub entries: [Option<(u64, P, Object)>; N],
}
pub struct Table {
entries: [Option>; N],
next: u64,
}
impl Table {
pub const fn new() -> Self {
Self { entries: [None; N], next: 1 }
}
pub fn len(&self) -> usize {
self.entries.iter().filter(|entry| entry.is_some()).count()
}
pub fn owned(&self, owner: P) -> usize {
self.entries.iter().flatten().filter(|entry| entry.owner == owner).count()
}
/// Validate all fallible conditions before publishing the entry.
pub fn open(&mut self, owner: P, object: Object) -> Result {
if object.id() == 0 { return Err(Error::InvalidObject); }
let slot = self.entries.iter().position(Option::is_none).ok_or(Error::Full)?;
let following = self.next.checked_add(1).ok_or(Error::IdExhausted)?;
let handle = Handle(self.next);
self.entries[slot] = Some(Entry { handle, owner, object });
self.next = following;
Ok(handle)
}
fn object(&self, owner: P, handle: Handle) -> Result