use std::mem;
use dope_core::io::transfer;
use o3::buffer::bytes::bounded;
mod proof;
pub(crate) use proof::Proof;
pub trait Policy: Proof {
type AckState: Default;
type Credit: Copy;
fn empty() -> Self::Credit;
fn submitted(
state: &Self::AckState,
credit: Self::Credit,
) -> Result<(Self::AckState, Submission), Overflow>;
fn unsubmitted(
state: &Self::AckState,
credit: Self::Credit,
) -> Result<(Self::AckState, transfer::Len), Overflow>;
fn complete(state: &mut Self::AckState) -> Completion;
fn abort(state: &mut Self::AckState) -> Abort;
fn has_pending(state: &Self::AckState) -> bool;
fn immediate(credit: Self::Credit) -> transfer::Len;
fn advance(credit: Self::Credit) -> Advance;
fn direct() -> Direct;
}
pub enum OnSubmit {}
pub enum OnComplete {}
#[derive(Clone, Copy, Default)]
pub struct Exact;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Overflow {
pub pending: transfer::Len,
pub consumed: transfer::Len,
pub cause: bounded::Overflow,
}
pub struct Pending(transfer::Len);
pub enum Submission {
Reclaim(transfer::Len),
Retain,
}
pub enum Completion {
Submitted(transfer::Len),
Retained,
}
pub enum Abort {
Submitted,
Retained,
}
#[derive(Clone, Copy, Eq, PartialEq)]
#[must_use]
#[repr(u8)]
pub enum Direct {
Independent,
Retained,
}
#[must_use]
#[repr(u8)]
pub enum Advance {
Idle,
Ready,
}
impl Proof for OnSubmit {}
impl Proof for OnComplete {}
impl Default for Pending {
fn default() -> Self {
Self(transfer::Len::ZERO)
}
}
impl Pending {
fn add(&self, consumed: transfer::Len) -> Result<Self, Overflow> {
match self.0.checked_add(consumed) {
Ok(next) => Ok(Self(next)),
Err(cause) => Err(Overflow {
pending: self.0,
consumed,
cause,
}),
}
}
}
impl Policy for OnSubmit {
type AckState = Pending;
type Credit = transfer::Len;
fn empty() -> Self::Credit {
transfer::Len::ZERO
}
fn submitted(
state: &Self::AckState,
consumed: transfer::Len,
) -> Result<(Self::AckState, Submission), Overflow> {
Ok((state.add(consumed)?, Submission::Reclaim(consumed)))
}
fn unsubmitted(
state: &Self::AckState,
consumed: transfer::Len,
) -> Result<(Self::AckState, transfer::Len), Overflow> {
Ok((state.add(consumed)?, consumed))
}
fn complete(state: &mut Self::AckState) -> Completion {
Completion::Submitted(mem::take(state).0)
}
fn abort(state: &mut Self::AckState) -> Abort {
mem::take(state);
Abort::Submitted
}
fn has_pending(state: &Self::AckState) -> bool {
!state.0.is_zero()
}
fn immediate(consumed: transfer::Len) -> transfer::Len {
consumed
}
fn advance(consumed: transfer::Len) -> Advance {
if consumed.is_zero() {
Advance::Idle
} else {
Advance::Ready
}
}
fn direct() -> Direct {
Direct::Independent
}
}
impl Policy for OnComplete {
type AckState = Exact;
type Credit = Exact;
fn empty() -> Self::Credit {
Exact
}
fn submitted(
Exact: &Self::AckState,
Exact: Self::Credit,
) -> Result<(Self::AckState, Submission), Overflow> {
Ok((Exact, Submission::Retain))
}
fn unsubmitted(
Exact: &Self::AckState,
Exact: Self::Credit,
) -> Result<(Self::AckState, transfer::Len), Overflow> {
Ok((Exact, transfer::Len::ZERO))
}
fn complete(Exact: &mut Self::AckState) -> Completion {
Completion::Retained
}
fn abort(Exact: &mut Self::AckState) -> Abort {
Abort::Retained
}
fn has_pending(Exact: &Self::AckState) -> bool {
false
}
fn immediate(Exact: Self::Credit) -> transfer::Len {
transfer::Len::ZERO
}
fn advance(Exact: Self::Credit) -> Advance {
Advance::Idle
}
fn direct() -> Direct {
Direct::Retained
}
}
const _: () = {
assert!(mem::size_of::<<OnSubmit as Policy>::AckState>() == mem::size_of::<transfer::Len>());
assert!(mem::size_of::<<OnComplete as Policy>::AckState>() == 0);
assert!(mem::size_of::<<OnSubmit as Policy>::Credit>() == mem::size_of::<transfer::Len>());
assert!(mem::size_of::<<OnComplete as Policy>::Credit>() == 0);
};