dope-core 0.15.0

The manifold runtime
Documentation
use std::{io, marker, mem, process};

use o3::collections::fixed::index;

use crate::{
    driver::{self, route, schedule::ready, settings},
    io::fd::handles,
};

type Invariant<'d> = marker::PhantomData<fn(&'d ()) -> &'d ()>;

pub(crate) struct Slots {
    floor: u32,
    pool: index::Pool,
}

impl Slots {
    pub(crate) fn new(layout: settings::FileSlots) -> io::Result<Self> {
        let capacity = layout.outbound();
        Ok(Self {
            floor: layout.accept(),
            pool: index::Pool::try_with_capacity(capacity)?,
        })
    }

    pub(crate) fn alloc_slot<'d>(
        &mut self,
        _driver: driver::Reference<'d>,
    ) -> io::Result<Slot<'d>> {
        self.take().map(|slot| Slot {
            slot,
            _brand: marker::PhantomData,
        })
    }

    pub(crate) fn alloc<'d>(
        &mut self,
        len: u32,
        _driver: driver::Reference<'d>,
    ) -> io::Result<Reservation<'d>> {
        use o3::collections::BoxSliceExt;

        if len == 0 || len > self.pool.available() {
            return Err(exhausted());
        }
        let slots =
            BoxSliceExt::try_box_with(len as usize, |_| self.take_available()).map_err(|_| {
                io::Error::new(
                    io::ErrorKind::OutOfMemory,
                    "dope: fixed-file reservation storage unavailable",
                )
            })?;
        Ok(Reservation {
            allocation: Allocation(slots),
            _brand: marker::PhantomData,
        })
    }

    pub(crate) fn release(&mut self, reservation: Reservation<'_>) {
        for slot in reservation.allocation.0.iter().copied() {
            self.release_allocated(slot);
        }
    }

    pub(crate) fn release_slot(&mut self, slot: Slot<'_>) {
        self.release_allocated(slot.slot);
    }

    fn take(&mut self) -> io::Result<handles::FixedSlot> {
        match self.pool.take() {
            Some(relative) => Ok(self.slot_for_relative(relative)),
            None => Err(exhausted()),
        }
    }

    fn take_available(&mut self) -> handles::FixedSlot {
        let Some(relative) = self.pool.take() else {
            process::abort();
        };
        self.slot_for_relative(relative)
    }

    fn slot_for_relative(&self, relative: u32) -> handles::FixedSlot {
        let raw = self.floor + relative;
        handles::FixedSlot::from_index(route::SlotIndex::from_bounded(raw))
    }

    fn release_allocated(&mut self, slot: handles::FixedSlot) {
        debug_assert!(slot.raw() >= self.floor);
        let relative = slot.raw().wrapping_sub(self.floor);
        if !self.pool.release(relative) {
            process::abort();
        }
    }
}

fn exhausted() -> io::Error {
    io::Error::new(
        io::ErrorKind::OutOfMemory,
        "dope: fixed-file slots exhausted",
    )
}

#[repr(transparent)]
pub(crate) struct Slot<'d> {
    pub(super) slot: handles::FixedSlot,
    pub(super) _brand: Invariant<'d>,
}

#[repr(transparent)]
pub(crate) struct Retirement<'d> {
    pub(super) slot: handles::FixedSlot,
    pub(super) _brand: Invariant<'d>,
}

#[repr(transparent)]
pub(crate) struct Allocation(pub(super) Box<[handles::FixedSlot]>);

#[repr(transparent)]
pub(crate) struct Reservation<'d> {
    pub(super) allocation: Allocation,
    pub(super) _brand: Invariant<'d>,
}

impl Allocation {
    pub(crate) fn len(&self) -> u32 {
        self.0.len() as u32
    }

    pub(crate) fn get(&self, local: route::SlotIndex) -> Option<handles::FixedSlot> {
        self.0.get(local.raw() as usize).copied()
    }
}

impl<'d> Slot<'d> {
    pub(crate) fn fixed(&self) -> handles::FixedSlot {
        self.slot
    }

    pub(crate) fn retire(self) -> Retirement<'d> {
        Retirement {
            slot: self.slot,
            _brand: marker::PhantomData,
        }
    }

    pub(crate) fn into_claimed(self, key: ready::FixedKey<'d>) -> handles::FixedSlot {
        debug_assert_eq!(self.slot.raw(), key.index());
        self.slot
    }
}

impl<'d> Retirement<'d> {
    pub(crate) fn from_release(released: ready::FixedRelease<'d>) -> Self {
        Self {
            slot: released.into_slot(),
            _brand: marker::PhantomData,
        }
    }

    pub(crate) fn into_slot(self) -> Slot<'d> {
        Slot {
            slot: self.slot,
            _brand: marker::PhantomData,
        }
    }

    pub(crate) fn into_raw(self) -> super::raw::Retirement {
        super::raw::Retirement::new(self.slot)
    }
}

impl Reservation<'_> {
    pub(crate) fn into_allocation(self) -> Allocation {
        self.allocation
    }

    pub(crate) fn len(&self) -> u32 {
        self.allocation.len()
    }
}

impl<'d> Reservation<'d> {
    pub(crate) fn from_retired(allocation: Allocation, _retired: driver::RetiredSlots<'d>) -> Self {
        Self {
            allocation,
            _brand: marker::PhantomData,
        }
    }
}

const _: () = assert!(mem::size_of::<Allocation>() == mem::size_of::<Box<[handles::FixedSlot]>>());
const _: () = assert!(mem::size_of::<Slots>() == mem::size_of::<(Box<[u32]>, [u32; 3])>());
const _: () = assert!(mem::size_of::<Slot<'static>>() == mem::size_of::<handles::FixedSlot>());
const _: () =
    assert!(mem::size_of::<Retirement<'static>>() == mem::size_of::<handles::FixedSlot>());
const _: () =
    assert!(mem::size_of::<Reservation<'static>>() == mem::size_of::<Box<[handles::FixedSlot]>>());