use std::{mem, num};
use dope::net::{self, wire::domain};
use o3::{base::num::bounded, buffer::pool};
use crate::staging;
const DEFAULT_STAGED_RECORDS: usize = 64;
const DEFAULT_CIPHERTEXT_SLOTS: usize = 128;
const MAX_STAGED_RECORDS: u32 = u32::MAX - 1;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Error {
Staged { actual: usize, maximum: usize },
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct Staged(bounded::U32<0, MAX_STAGED_RECORDS>);
impl Staged {
pub const fn fixed<const COUNT: u32>() -> Self {
Self(bounded::U32::exact::<COUNT>())
}
const fn get(self) -> usize {
self.0.into_usize()
}
}
impl TryFrom<usize> for Staged {
type Error = Error;
fn try_from(actual: usize) -> Result<Self, Self::Error> {
match bounded::U32::from_usize(actual) {
Some(count) => Ok(Self(count)),
None => Err(Error::Staged {
actual,
maximum: MAX_STAGED_RECORDS as usize,
}),
}
}
}
#[derive(Clone, Copy)]
pub struct Plan {
staged: Option<Staged>,
send: num::NonZero<usize>,
growing: Option<num::NonZero<u32>>,
}
impl Plan {
pub(super) const fn new() -> Self {
Self {
staged: None,
send: num::NonZero::<usize>::MIN.saturating_add(DEFAULT_CIPHERTEXT_SLOTS - 1),
growing: None,
}
}
pub(super) const fn with_staged(mut self, max: Staged) -> Self {
self.staged = Some(max);
self
}
pub(super) const fn with_send(mut self, max: num::NonZero<usize>) -> Self {
self.send = max;
self
}
pub(super) const fn with_growth(mut self, warm: num::NonZero<u32>) -> Self {
self.growing = Some(warm);
self
}
pub fn layout(self, limits: domain::Limits) -> Result<Layout, net::Resource> {
let cap = limits.connections().nonzero();
let held = match self.staged {
Some(staged) => staged.get(),
None => DEFAULT_STAGED_RECORDS.min(cap.get()).max(
limits
.resident()
.into_raw()
.div_ceil(staging::MAX_PLAINTEXT),
),
};
let recv = held + 1;
let send = self.send.min(cap).get();
Layout::try_new(recv, send, self.growing)
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Layout {
pub(super) recv: pool::Layout,
pub(super) send: pool::Layout,
pub(super) growing: Option<num::NonZero<u32>>,
}
impl Layout {
fn try_new(
rslots: usize,
sslots: usize,
growing: Option<num::NonZero<u32>>,
) -> Result<Self, net::Resource> {
let recv = pool::Layout::new(rslots, staging::MAX_PLAINTEXT).map_err(|cause| {
net::Resource::BufferLayout(net::BufferLayout::new(net::BufferKind::Receive, cause))
})?;
let send = pool::Layout::new(sslots, staging::TLS_STAGING_CAP).map_err(|cause| {
net::Resource::BufferLayout(net::BufferLayout::new(net::BufferKind::Send, cause))
})?;
Ok(Self {
recv,
send,
growing,
})
}
pub fn recv_slots(self) -> u32 {
self.recv.slots()
}
pub fn send_slots(self) -> u32 {
self.send.slots()
}
pub fn recv_cap(self) -> usize {
staging::MAX_PLAINTEXT
}
pub fn send_cap(self) -> usize {
staging::TLS_STAGING_CAP
}
pub fn payload(self) -> usize {
self.recv.slots() as usize * staging::MAX_PLAINTEXT
+ self.send.slots() as usize * staging::TLS_STAGING_CAP
}
}
const _: () = assert!(mem::size_of::<Staged>() == mem::size_of::<u32>());
const _: () = {
assert!(staging::MAX_PLAINTEXT > 1);
assert!(
u32::MAX as u128 * (staging::MAX_PLAINTEXT as u128 + staging::TLS_STAGING_CAP as u128)
<= usize::MAX as u128
);
};