use std::num::NonZero;
use derive_deftly::Deftly;
use tor_bytes::{EncodeResult, Error, Reader, Writer};
use tor_memquota::derive_deftly_template_HasMemoryCost;
use crate::relaycell::msg::Body;
#[derive(Clone, Debug, Deftly)]
#[derive_deftly(HasMemoryCost)]
pub struct Xon {
version: FlowCtrlVersion,
kbps_ewma: XonKbpsEwma,
}
#[derive(Clone, Debug, Deftly)]
#[derive_deftly(HasMemoryCost)]
pub struct Xoff {
version: FlowCtrlVersion,
}
impl Xon {
pub fn new(version: FlowCtrlVersion, kbps_ewma: XonKbpsEwma) -> Self {
Self { version, kbps_ewma }
}
pub fn version(&self) -> FlowCtrlVersion {
self.version
}
pub fn kbps_ewma(&self) -> XonKbpsEwma {
self.kbps_ewma
}
}
impl Body for Xon {
fn decode_from_reader(r: &mut Reader<'_>) -> tor_bytes::Result<Self> {
let version = r.take_u8()?;
let version = match FlowCtrlVersion::new(version) {
Ok(x) => x,
Err(UnrecognizedVersionError) => {
return Err(Error::InvalidMessage("Unrecognized XON version.".into()));
}
};
let kbps_ewma = XonKbpsEwma::decode(r.take_u32()?);
Ok(Self::new(version, kbps_ewma))
}
fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
w.write_u8(*self.version);
w.write_u32(self.kbps_ewma.encode());
Ok(())
}
}
impl Xoff {
pub fn new(version: FlowCtrlVersion) -> Self {
Self { version }
}
pub fn version(&self) -> FlowCtrlVersion {
self.version
}
}
impl Body for Xoff {
fn decode_from_reader(r: &mut Reader<'_>) -> tor_bytes::Result<Self> {
let version = r.take_u8()?;
let version = match FlowCtrlVersion::new(version) {
Ok(x) => x,
Err(UnrecognizedVersionError) => {
return Err(Error::InvalidMessage("Unrecognized XOFF version.".into()));
}
};
Ok(Self::new(version))
}
fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
w.write_u8(*self.version);
Ok(())
}
}
#[derive(Copy, Clone, Debug, Deftly)]
#[derive_deftly(HasMemoryCost)]
pub struct FlowCtrlVersion(u8);
impl FlowCtrlVersion {
pub const V0: Self = Self(0);
pub const fn new(version: u8) -> Result<Self, UnrecognizedVersionError> {
if version != 0 {
return Err(UnrecognizedVersionError);
}
Ok(Self(version))
}
}
impl TryFrom<u8> for FlowCtrlVersion {
type Error = UnrecognizedVersionError;
fn try_from(x: u8) -> Result<Self, Self::Error> {
Self::new(x)
}
}
impl std::ops::Deref for FlowCtrlVersion {
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct UnrecognizedVersionError;
#[derive(Copy, Clone, Debug, Deftly)]
#[derive_deftly(HasMemoryCost)]
#[allow(clippy::exhaustive_enums)]
pub enum XonKbpsEwma {
Limited(NonZero<u32>),
Unlimited,
}
impl XonKbpsEwma {
fn decode(kbps_ewma: u32) -> Self {
match NonZero::new(kbps_ewma) {
Some(x) => Self::Limited(x),
None => Self::Unlimited,
}
}
fn encode(&self) -> u32 {
match self {
Self::Limited(x) => x.get(),
Self::Unlimited => 0,
}
}
}
impl std::fmt::Display for XonKbpsEwma {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Limited(rate) => write!(f, "{rate} kbps"),
Self::Unlimited => write!(f, "unlimited"),
}
}
}