sark-h2 0.28.0

Fiber-free HTTP/2 protocol primitives and manifold server for sark
use std::{marker, num};

use o3::buffer::bytes::window::reserve;

use crate::{
    Endpoint,
    conn::{self, config},
    flow, frame,
};

#[derive(Clone, Copy)]
pub(crate) struct Capacity {
    storage: reserve::Plan<{ Capacity::TERMINAL_SLACK }>,
}

#[derive(Clone, Copy)]
pub(crate) struct Block {
    list: usize,
}

#[repr(transparent)]
#[derive(Clone, Copy)]
pub(crate) struct Wire(num::NonZero<usize>);

#[derive(Clone, Copy)]
pub(crate) struct Initial<R: crate::Role> {
    capacity: Capacity,
    settings: config::Wire,
    target: flow::Target,
    marker: marker::PhantomData<fn() -> R>,
}

impl Capacity {
    pub(crate) const RESET_SLACK: usize = frame::HEADER_LEN + 4;
    pub(crate) const TERMINAL_SLACK: usize = frame::HEADER_LEN + 8;
    const STORAGE_SLACK: usize = Self::RESET_SLACK + Self::TERMINAL_SLACK;
    pub(crate) const BLOCK_SLACK: usize = 12;
    const MIN_FRAME: usize = 16_384;

    pub(crate) fn new(logical: usize) -> Result<Self, config::Error> {
        let Some(logical) = num::NonZero::<usize>::new(logical) else {
            return Err(config::Error::ZeroCapacity(config::Field::Outbound));
        };
        let Some(reset) = logical.checked_add(Self::RESET_SLACK) else {
            return Err(config::Error::OutboundCapacityOverflow {
                logical,
                source: reserve::Error {
                    capacity: logical,
                    reserved: Self::RESET_SLACK,
                },
            });
        };
        let storage = reserve::Plan::new(reset)
            .map_err(|source| config::Error::OutboundCapacityOverflow { logical, source })?;
        Ok(Self { storage })
    }

    pub(super) const fn logical(self) -> usize {
        self.storage.capacity().get() - Self::STORAGE_SLACK
    }

    pub(super) const fn storage(self) -> reserve::Plan<{ Self::TERMINAL_SLACK }> {
        self.storage
    }

    pub(crate) fn wire(list: usize) -> Result<(Block, Wire), config::Error> {
        let wire = list
            .checked_add(Self::BLOCK_SLACK)
            .ok_or(config::Error::HpackBlockOverflow { list })?;
        let frames = wire.max(1).div_ceil(Self::MIN_FRAME);
        let Some(capacity) = frames
            .checked_mul(frame::HEADER_LEN)
            .and_then(|heads| wire.checked_add(heads))
            .and_then(num::NonZero::<usize>::new)
        else {
            return Err(config::Error::HpackWireOverflow { list });
        };
        Ok((Block { list }, Wire(capacity)))
    }

    pub(crate) const fn slots(self, wire: Wire) -> usize {
        self.logical() / wire.owner() + 1
    }
}

impl Block {
    pub(super) const fn list(self) -> usize {
        self.list
    }
}

impl Wire {
    pub(crate) const fn bytes(self) -> usize {
        self.0.get()
    }

    const fn owner(self) -> usize {
        self.bytes() / 2 + self.bytes() % 2
    }
}

impl<R: crate::Role> Initial<R> {
    pub(crate) fn new(
        capacity: Capacity,
        settings: config::Wire,
        target: flow::Target,
    ) -> Result<Self, config::Error> {
        let required = Self::required(settings, target);
        if required > capacity.logical() {
            return Err(config::Error::OutboundTooSmall {
                required,
                actual: capacity.logical(),
            });
        }
        Ok(Self {
            capacity,
            settings,
            target,
            marker: marker::PhantomData,
        })
    }

    pub(super) fn into_parts(self) -> (Capacity, config::Wire, flow::Target) {
        (self.capacity, self.settings, self.target)
    }

    fn required(settings: config::Wire, target: flow::Target) -> usize {
        frame::HEADER_LEN
            + settings.len()
            + match R::ENDPOINT {
                Endpoint::Client => conn::CLIENT_PREFACE.len(),
                Endpoint::Server => 0,
            }
            + if target.increment().is_some() {
                frame::HEADER_LEN + 4
            } else {
                0
            }
    }
}

const _: () = assert!(core::mem::size_of::<Capacity>() == core::mem::size_of::<usize>());
const _: () = assert!(core::mem::size_of::<Block>() == core::mem::size_of::<usize>());
const _: () = assert!(core::mem::size_of::<Wire>() == core::mem::size_of::<usize>());