use std::num;
pub use config::Config;
pub(crate) use config::plan::{Plan, Ready};
pub use demands::Schedule;
use o3::{base::num::bounded, depot, depot::capacity};
pub use receive::Receive;
pub use resource::Resource;
pub use routes::Routes;
use crate::{
driver::route::{SLOT_MASK, indices::SlotIndex, table},
io::{datagram::payload, socket::option, transfer},
};
pub mod claim;
mod config;
pub mod demands;
pub mod errors;
mod receive;
mod resource;
mod routes;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DomainSum {
current: Domain,
added: Domain,
}
impl DomainSum {
pub const fn parts(&self) -> (&Domain, &Domain) {
(&self.current, &self.added)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct Ring(Buffers);
impl Ring {
pub fn try_round(entries: u32) -> Result<Self, errors::Claim> {
if entries < 2 {
return Err(errors::Config::ReceiveEntriesOutOfRange {
actual: entries,
minimum: 2,
maximum: u16::MAX as u32,
}
.into());
}
let count = entries
.checked_next_power_of_two()
.ok_or(errors::Claim::ReceiveEntriesPowerOfTwoOverflow { requested: entries })?;
if count > u32::from(u16::MAX) {
return Err(errors::Claim::ReceiveEntriesTooLarge {
actual: count as usize,
maximum: u16::MAX as usize,
});
}
Ok(Self(Buffers(
num::NonZero::<u16>::MIN.saturating_add(count as u16 - 1),
)))
}
pub const fn buffers(self) -> Buffers {
self.0
}
}
impl TryFrom<u16> for Ring {
type Error = errors::Config;
fn try_from(entries: u16) -> Result<Self, Self::Error> {
if entries < 2 {
return Err(errors::Config::ReceiveEntriesOutOfRange {
actual: entries as u32,
minimum: 2,
maximum: u16::MAX as u32,
});
}
if !entries.is_power_of_two() {
return Err(errors::Config::ReceiveEntriesNotPowerOfTwo { actual: entries });
}
Ok(Self(Buffers(
num::NonZero::<u16>::MIN.saturating_add(entries - 1),
)))
}
}
const _: () = assert!(std::mem::size_of::<Ring>() == std::mem::size_of::<Buffers>());
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct Datagram(Receive);
impl Datagram {
pub const fn fixed<const ENTRIES: u16, const PAYLOAD: u32>() -> Self {
const {
assert!(ENTRIES >= 2);
assert!(ENTRIES.is_power_of_two());
let len = transfer::NonZero::from_payload(payload::Len::fixed::<PAYLOAD>());
assert!((ENTRIES as u64) * (len.into_nonzero().get() as u64) <= isize::MAX as u64);
let entries = Buffers(num::NonZero::<u16>::MIN.saturating_add(ENTRIES - 1));
Self(Receive::from_parts(entries, len))
}
}
pub const fn new(entries: Ring, payload: payload::Len) -> Self {
let len = transfer::NonZero::from_payload(payload);
Self(Receive::from_parts(entries.buffers(), len))
}
pub const fn receive(self) -> Receive {
self.0
}
}
const _: () = {
assert!(std::mem::size_of::<Datagram>() == std::mem::size_of::<Receive>());
assert!(std::mem::align_of::<Datagram>() == std::mem::align_of::<Receive>());
};
const MIN_SUBMISSION_CAPACITY: u32 = (option::MAX_STREAM_OPTIONS + 1) as u32;
const MAX_SUBMISSION_CAPACITY: u32 = 32_768;
const MAX_COMPLETION_CAPACITY: u32 = 65_536;
pub(crate) const MAX_FILE_CAPACITY: u32 = SLOT_MASK as u32 + 1;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Progress {
Prompt,
BatchedWhenSupported,
}
pub trait Profile {
const QUEUES: QueueLayout;
const SCHEDULER: Schedule = Schedule::DEFAULT;
const COMPLETION_PROGRESS: Progress = Progress::BatchedWhenSupported;
const FILES: FileSlots = FileSlots::fixed::<0, 256>();
const RECEIVE: Receive = Receive::DEFAULT;
const RESIDENT_DOMAINS: Domain = Domain::ZERO;
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(transparent)]
pub struct Buffers(num::NonZero<u16>);
impl Buffers {
pub const fn into_nonzero(self) -> num::NonZero<u16> {
self.0
}
pub(crate) const fn get(self) -> u16 {
self.0.get()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct QueueLayout {
submissions: Submission,
completions: Completion,
}
impl QueueLayout {
pub const fn fixed<const SUBMISSIONS: u32, const COMPLETIONS: u32>() -> Self {
const {
assert!(SUBMISSIONS >= MIN_SUBMISSION_CAPACITY);
assert!(SUBMISSIONS <= MAX_SUBMISSION_CAPACITY);
assert!(SUBMISSIONS.is_power_of_two());
assert!(COMPLETIONS != 0);
assert!(COMPLETIONS <= MAX_COMPLETION_CAPACITY);
assert!(COMPLETIONS.is_power_of_two());
assert!(COMPLETIONS >= SUBMISSIONS);
QueueLayout {
submissions: Submission(bounded::U32::clamp(SUBMISSIONS as usize)),
completions: Completion(bounded::U32::clamp(COMPLETIONS as usize)),
}
}
}
pub fn resolve(self, need: demands::Submit) -> Result<Self, errors::Claim> {
let wanted = self.submissions().max(need.get());
let submit = wanted
.checked_next_power_of_two()
.ok_or(errors::Claim::QueueSubmissionsPowerOfTwoOverflow { requested: wanted })?;
if submit > MAX_SUBMISSION_CAPACITY {
return Err(errors::Config::QueueSubmissionsOutOfRange {
actual: submit,
minimum: MIN_SUBMISSION_CAPACITY,
maximum: MAX_SUBMISSION_CAPACITY,
}
.into());
}
let complete = self.completions().max(submit);
Ok(Self {
submissions: Submission(bounded::U32::clamp(submit as usize)),
completions: Completion(bounded::U32::clamp(complete as usize)),
})
}
pub const fn submission(self) -> Submission {
self.submissions
}
pub(crate) const fn completion(self) -> Completion {
self.completions
}
pub(crate) const fn submissions(self) -> u32 {
self.submissions.get()
}
pub(crate) const fn completions(self) -> u32 {
self.completions.get()
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct FileSlots {
accept: table::Capacity,
table: bounded::U32<1, MAX_FILE_CAPACITY>,
}
impl FileSlots {
pub const fn fixed<const ACCEPT: u32, const OUTBOUND: u32>() -> Self {
const {
assert!(ACCEPT <= u32::MAX - OUTBOUND);
assert!(OUTBOUND != 0);
let total = ACCEPT + OUTBOUND;
assert!(total <= MAX_FILE_CAPACITY);
FileSlots {
accept: table::Capacity(capacity::Count::new(ACCEPT)),
table: bounded::U32::clamp(total as usize),
}
}
}
pub fn resolve(self, need: demands::Files) -> Result<Self, errors::Config> {
let accept = self.accept().raw().max(need.accepted().get());
let outbound = self.outbound().raw().max(need.outbound().get());
let total = accept as u64 + outbound as u64;
let maximum = MAX_FILE_CAPACITY;
if total > maximum as u64 {
return Err(errors::Config::DescriptorCapacityOutOfRange {
accepted: accept,
outbound,
actual: total,
minimum: 1,
maximum,
});
}
Ok(Self {
accept: table::Capacity(capacity::Count::new(accept)),
table: bounded::U32::clamp(total as usize),
})
}
#[must_use]
pub const fn capacity(self) -> table::Capacity {
table::Capacity(capacity::Count::new(self.table.get()))
}
#[must_use]
pub const fn accept(self) -> table::Capacity {
self.accept
}
#[must_use]
pub const fn outbound(self) -> table::Capacity {
table::Capacity(capacity::Count::new(self.table.get() - self.accept.raw()))
}
pub(crate) fn slots(self) -> impl DoubleEndedIterator<Item = SlotIndex> + ExactSizeIterator {
self.capacity()
.into_iter()
.rev()
.take(self.outbound().count().get())
.rev()
}
pub(crate) fn accept_slot(self) -> Option<SlotIndex> {
let span = self.accept.span()?;
Some(SlotIndex::from_field(u64::from(span.get())))
}
pub(crate) const fn bound(self) -> bounded::U32<1, MAX_FILE_CAPACITY> {
self.table
}
}
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(transparent)]
pub struct Domain(u32);
impl Domain {
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self(1);
pub const MAX: Self = Self(u32::MAX);
pub const fn fixed<const COUNT: u32>() -> Self {
Self(COUNT)
}
#[must_use]
pub const fn resolve(self, need: Self) -> Self {
if self.0 >= need.0 { self } else { need }
}
pub const fn combine(self, next: Self) -> Result<Self, DomainSum> {
match self.0.checked_add(next.0) {
Some(total) => Ok(Self(total)),
None => Err(DomainSum {
current: self,
added: next,
}),
}
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
const _: () = {
assert!(std::mem::size_of::<Domain>() == std::mem::size_of::<u32>());
assert!(std::mem::align_of::<Domain>() == std::mem::align_of::<u32>());
};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(transparent)]
pub struct Submission(bounded::U32<MIN_SUBMISSION_CAPACITY, MAX_SUBMISSION_CAPACITY>);
impl Submission {
pub(crate) const fn get(self) -> u32 {
self.0.get()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(transparent)]
pub(crate) struct Completion(pub(crate) bounded::U32<1, MAX_COMPLETION_CAPACITY>);
impl Completion {
pub(crate) const fn get(self) -> u32 {
self.0.get()
}
}
const _: () = {
assert!(std::mem::size_of::<Completion>() == std::mem::size_of::<u32>());
assert!(std::mem::align_of::<Completion>() == std::mem::align_of::<u32>());
};
impl From<FileSlots> for (table::Capacity, table::Capacity) {
fn from(layout: FileSlots) -> Self {
(layout.capacity(), layout.accept)
}
}
unsafe impl depot::raw::Count for Completion {
const MAX: u32 = MAX_COMPLETION_CAPACITY;
fn count(self) -> capacity::Count {
capacity::Count::new(self.0.get())
}
}