use std::num;
use o3::base::num::bounded;
use crate::{Errno, kernel};
pub unsafe trait Source {
fn read() -> i32;
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Code(num::NonZero<i32>);
impl Code {
pub unsafe fn last<S: Source + ?Sized>() -> Self {
unsafe { Self::new(S::read()) }
}
pub const unsafe fn new(raw: i32) -> Self {
Self(unsafe { num::NonZero::new_unchecked(raw) })
}
pub unsafe fn from_native(raw: libc::intptr_t) -> Option<Self> {
num::NonZero::new(raw as i32).map(Self)
}
}
impl From<Code> for Errno {
fn from(code: Code) -> Self {
Self(code.0)
}
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Status<const MIN: libc::c_int, const MAX: libc::c_int>(libc::c_int);
impl<const MIN: libc::c_int, const MAX: libc::c_int> Status<MIN, MAX> {
pub const unsafe fn new(raw: libc::c_int) -> Self {
Self(raw)
}
}
pub(crate) fn try_retry<T, E, const RETRY: bool>(
mut call: impl FnMut() -> Result<T, Errno>,
classify: impl FnOnce(Errno) -> E,
) -> Result<T, E> {
loop {
match call() {
Ok(value) => return Ok(value),
Err(error) if RETRY && error.is_intr() => continue,
Err(error) => return Err(classify(error)),
}
}
}
impl Status<-1, 0> {
pub unsafe fn try_zero<E: From<Errno>>(self, errno: impl FnOnce() -> Errno) -> Result<(), E> {
unsafe { Exact::<0>(self.0).try_admit(errno) }
}
}
impl Status<-1, { i32::MAX }> {
pub const unsafe fn from_count(raw: libc::ssize_t) -> Self {
Self(raw as libc::c_int)
}
pub fn try_value<E: From<Errno>>(
self,
errno: impl FnOnce() -> Errno,
) -> Result<kernel::Value, E> {
if self.0 == -1 {
Err(E::from(errno()))
} else {
Ok(kernel::Value(self.0))
}
}
pub unsafe fn try_count<E: From<Errno>>(
self,
errno: impl FnOnce() -> Errno,
) -> Result<Count, E> {
Ok(Count::new(self.try_value(errno)?))
}
pub const unsafe fn admit_exact<const VALUE: libc::c_int>(self) -> Exact<VALUE> {
Exact(self.0)
}
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Exact<const VALUE: libc::c_int>(libc::c_int);
impl<const VALUE: libc::c_int> Exact<VALUE> {
pub unsafe fn try_admit<E: From<Errno>>(self, errno: impl FnOnce() -> Errno) -> Result<(), E> {
if self.0 == VALUE {
Ok(())
} else {
Err(E::from(errno()))
}
}
}
impl Status<0, { i32::MAX }> {
pub fn try_pthread(self) -> Result<(), Errno> {
if self.0 == 0 {
Ok(())
} else {
Err(unsafe { Code::new(self.0) }.into())
}
}
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Descriptor(bounded::U32<0, { i32::MAX as u32 }>);
impl Descriptor {
pub const unsafe fn new(raw: libc::c_int) -> Self {
Self(unsafe { bounded::raw::Proof::assume(raw as u32) }.retain())
}
pub const fn get(self) -> libc::c_int {
self.0.get() as libc::c_int
}
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Count(bounded::U32<0, { i32::MAX as u32 }>);
impl Count {
const fn new(value: kernel::Value) -> Self {
Self(unsafe { bounded::raw::Proof::assume(value.get() as u32) }.retain())
}
pub const fn get(self) -> usize {
self.0.into_usize()
}
pub const fn bounded(self) -> bounded::U32<0, { i32::MAX as u32 }> {
self.0
}
}