use std::{mem, num::NonZero, time};
use dope_sys::io::socket::option::{Kind, Value, tuning};
use crate::{
Engine, driver,
driver::{life::native::Apply, retained::flight, route, route::kind},
io::{fd::handles, socket::pending},
};
mod kinds;
pub(super) use kinds::Mask;
pub(crate) use kinds::NonEmpty;
pub(crate) const MAX_STREAM_OPTIONS: usize = 7;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Switch {
Off,
On,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct Buffer(Value);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct Period(NonZero<u32>);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct Retries(NonZero<u32>);
impl Switch {
pub(crate) const fn value(self) -> Value {
match self {
Self::Off => Value::ZERO,
Self::On => Value::from_enabled(true),
}
}
}
impl Buffer {
const fn value(self) -> Value {
self.0
}
}
impl Period {
const fn value(self) -> Value {
Value::new(self.0.get() as i32)
}
}
impl Retries {
const fn value(self) -> Value {
Value::new(self.0.get() as i32)
}
}
#[derive(Clone, Copy)]
pub struct Set {
recv_buffer: Value,
send_buffer: Value,
keep_alive_idle: Value,
keep_alive_interval: Value,
keep_alive_retries: Value,
present: Mask,
enabled: Mask,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Error {
Buffer {
actual: usize,
maximum: usize,
},
Period {
actual: time::Duration,
maximum: time::Duration,
},
Retries {
actual: u32,
maximum: u32,
},
}
impl Default for Set {
fn default() -> Self {
Self::EMPTY
}
}
pub enum Tuning<'d> {
Applied(handles::Descriptor<'d>),
Pending(pending::Tuning<'d>),
}
impl Set {
pub fn new(
recv: Option<Buffer>,
send: Option<Buffer>,
no_delay: Option<Switch>,
alive: Option<Switch>,
idle: Option<Period>,
interval: Option<Period>,
retries: Option<Retries>,
) -> Self {
let mut set = Self::EMPTY;
if let Some(value) = recv {
set.set(Kind::RecvBuffer, value.value());
}
if let Some(value) = send {
set.set(Kind::SendBuffer, value.value());
}
if let Some(value) = no_delay {
set.set(Kind::NoDelay, value.value());
}
if let Some(value) = alive {
set.set(Kind::KeepAlive, value.value());
}
if let Some(value) = idle {
set.set(Kind::KeepAliveIdle, value.value());
}
if let Some(value) = interval {
set.set(Kind::KeepAliveInterval, value.value());
}
if let Some(value) = retries {
set.set(Kind::KeepAliveRetries, value.value());
}
set
}
pub const EMPTY: Self = Self {
recv_buffer: Value::ZERO,
send_buffer: Value::ZERO,
keep_alive_idle: Value::ZERO,
keep_alive_interval: Value::ZERO,
keep_alive_retries: Value::ZERO,
present: Mask::EMPTY,
enabled: Mask::EMPTY,
};
pub(crate) fn non_empty(&self) -> Option<NonEmpty<'_>> {
NonEmpty::new(self, self.present)
}
fn value(&self, kind: Kind) -> Value {
match kind {
Kind::NoDelay | Kind::KeepAlive => Value::from_enabled(self.enabled.contains(kind)),
Kind::RecvBuffer => self.recv_buffer,
Kind::SendBuffer => self.send_buffer,
Kind::KeepAliveIdle => self.keep_alive_idle,
Kind::KeepAliveInterval => self.keep_alive_interval,
Kind::KeepAliveRetries => self.keep_alive_retries,
}
}
fn set(&mut self, kind: Kind, value: Value) {
self.present.insert(kind);
match kind {
Kind::NoDelay | Kind::KeepAlive if !value.is_zero() => self.enabled.insert(kind),
Kind::NoDelay | Kind::KeepAlive => {}
Kind::RecvBuffer => self.recv_buffer = value,
Kind::SendBuffer => self.send_buffer = value,
Kind::KeepAliveIdle => self.keep_alive_idle = value,
Kind::KeepAliveInterval => self.keep_alive_interval = value,
Kind::KeepAliveRetries => self.keep_alive_retries = value,
}
}
}
impl TryFrom<usize> for Buffer {
type Error = Error;
fn try_from(actual: usize) -> Result<Self, Self::Error> {
let maximum = i32::MAX as usize;
if actual <= maximum {
Ok(Self(Value::new(actual as i32)))
} else {
Err(Error::Buffer { actual, maximum })
}
}
}
impl TryFrom<time::Duration> for Period {
type Error = Error;
fn try_from(actual: time::Duration) -> Result<Self, Self::Error> {
let maximum = time::Duration::from_secs(i32::MAX as u64);
if actual.is_zero() || actual > maximum {
return Err(Error::Period { actual, maximum });
}
let seconds = actual.as_secs() + u64::from(actual.subsec_nanos() != 0);
Ok(Self(NonZero::<u32>::MIN.saturating_add(seconds as u32 - 1)))
}
}
impl TryFrom<u32> for Retries {
type Error = Error;
fn try_from(actual: u32) -> Result<Self, Self::Error> {
let maximum = i32::MAX as u32;
if actual == 0 || actual > maximum {
return Err(Error::Retries { actual, maximum });
}
Ok(Self(NonZero::<u32>::MIN.saturating_add(actual - 1)))
}
}
const _: () = {
assert!(mem::size_of::<Switch>() == mem::size_of::<bool>());
assert!(mem::align_of::<Switch>() == mem::align_of::<bool>());
assert!(mem::size_of::<Buffer>() == mem::size_of::<Value>());
assert!(mem::size_of::<Period>() == mem::size_of::<Value>());
assert!(mem::size_of::<Retries>() == mem::size_of::<Value>());
assert!(mem::size_of::<Option<Period>>() == mem::size_of::<Period>());
assert!(mem::size_of::<Option<Retries>>() == mem::size_of::<Retries>());
assert!(mem::size_of::<Set>() == 24);
};
impl<'d> Tuning<'d> {
pub fn try_new<Tag: route::Tag + route::Select<kind::Tuning>>(
driver: &mut driver::Context<'_, 'd>,
slots: &flight::Slots<'d, Tag>,
target: route::Target<'d, Tag>,
fd: handles::Descriptor<'d>,
opts: Set,
) -> Result<Self, pending::Rejection<'d>> {
use tuning::device::{Owner as _, Source};
let owner = Apply::new(driver.native(), slots, target, fd, opts);
let request = <crate::Backend as Source>::Socket::new(owner);
unsafe { tuning::raw::Apply::<Engine, _, { MAX_STREAM_OPTIONS }>::try_apply(request) }
}
}