use o3::buffer::Complete as _;
use std::{mem, ops};
use direct::source::Cursor;
use dope::{
core::io::{recv, transfer},
net::wire::receive::mode::access,
};
use o3::{
base::num::bounded,
buffer::{
self, bytes, pool,
pool::owners,
storage::{self, range},
write::consume,
},
};
use crate::{
error,
state::direct::{self, source::Head},
};
pub struct Retained<'w, 'd> {
staged: Option<storage::Shared<owners::Edge<owners::Plain, pool::Borrowed<'w>>>>,
plain: Option<Cursor<'d>>,
remaining: transfer::Len,
}
impl<'w, 'd> Retained<'w, 'd> {
pub(in crate::tls::receive) fn try_new(
staged: Option<storage::Shared<owners::Edge<owners::Plain, pool::Borrowed<'w>>>>,
plain: Cursor<'d>,
) -> Result<Option<Self>, error::Error> {
let staged = staged.filter(|chunk| !chunk.is_empty());
let body = plain.remaining();
let plain = (body != 0).then_some(plain);
let stage = staged.as_ref().map_or(0, buffer::Source::remaining);
let len = bounded::Usize::<{ transfer::MAX_BYTES }>::ZERO
.try_add(stage)
.map_err(error::Error::Staged)?
.try_add(body)
.map_err(error::Error::Retained)?;
if len.get() == 0 {
return Ok(None);
}
Ok(Some(Self {
staged,
plain,
remaining: transfer::Len::from_count(len),
}))
}
}
impl<'w, 'd> buffer::Source for Retained<'w, 'd> {
type View<'input>
= Retention<'input, 'w, 'd>
where
Self: 'input;
fn remaining(&self) -> usize {
self.remaining.into_raw()
}
}
impl transfer::Length for Retained<'_, '_> {
fn len(&self) -> transfer::Len {
self.remaining
}
}
impl AsRef<[u8]> for Retained<'_, '_> {
fn as_ref(&self) -> &[u8] {
if let Some(staged) = self.staged.as_ref().filter(|head| !head.is_empty()) {
staged.as_ref()
} else {
self.plain.as_ref().map_or(&[], Cursor::as_slice)
}
}
}
impl<'w, 'd> buffer::Complete for Retained<'w, 'd> {
fn view(&self) -> Self::View<'_> {
let backing = if let Some(staged) = self.staged.as_ref().filter(|head| !head.is_empty()) {
Backing::Staged(staged.view())
} else if let Some(plain) = &self.plain {
Backing::Plain(plain.view())
} else {
Backing::Empty
};
Retention { backing }
}
}
impl<'w, 'd> buffer::Cursor for Retained<'w, 'd> {
fn try_get<'input>(
&'input self,
range: ops::Range<usize>,
) -> Result<Self::View<'input>, range::Error> {
let backing = match self.view().backing {
Backing::Staged(view) => Backing::Staged(view.try_get(range)?),
Backing::Plain(view) => Backing::Plain(view.try_get(range)?),
Backing::Empty => {
<[u8] as buffer::Cursor>::try_get(&[], range)?;
Backing::Empty
}
};
Ok(Retention { backing })
}
}
impl consume::Consume for Retained<'_, '_> {
fn remaining(&self) -> usize {
self.as_ref().len()
}
fn try_consume(&mut self, amount: usize) -> Result<(), consume::Error> {
let Some(mut head) = access::Sequence::next(self) else {
return consume::Error::check(amount, 0);
};
head.try_consume(amount)?;
access::Head::commit(head);
Ok(())
}
fn consume_max(&mut self, max: usize) -> usize {
let Some(mut head) = access::Sequence::next(self) else {
return 0;
};
let amount = head.consume_max(max);
access::Head::commit(head);
amount
}
}
impl<'w, 'd> access::Sequence<'d> for Retained<'w, 'd> {
type Head<'head>
= Current<'head, 'w, 'd>
where
Self: 'head;
fn next(&mut self) -> Option<Self::Head<'_>> {
let Self {
staged,
plain,
remaining: left,
} = self;
if let Some(staged) = staged.as_mut().filter(|staged| !staged.is_empty()) {
return Some(Current::staged(staged, left));
}
let head = plain.as_mut()?.head()?;
Some(Current::plain(head, left))
}
}
const _: () = assert!(
mem::size_of::<Retained<'_, '_>>()
<= mem::size_of::<Option<storage::Shared<owners::Edge<owners::Plain, pool::Borrowed<'_>>>>>(
) + mem::size_of::<Option<Cursor<'_>>>()
+ mem::size_of::<usize>()
);
#[doc(hidden)]
pub struct Retention<'input, 'w: 'input, 'd: 'input> {
pub(super) backing: Backing<'input, 'w, 'd>,
}
pub(super) enum Backing<'input, 'w: 'input, 'd: 'input> {
Empty,
Staged(
<storage::Shared<owners::Edge<owners::Plain, pool::Borrowed<'w>>> as buffer::Source>::View<
'input,
>,
),
Plain(<recv::Shared<'d> as buffer::Source>::View<'input>),
}
#[doc(hidden)]
pub struct Current<'input, 'w: 'input, 'd: 'input> {
pub(super) backing: HeadBacking<'input, 'w, 'd>,
left: &'input mut transfer::Len,
}
pub(super) enum HeadBacking<'input, 'w: 'input, 'd: 'input> {
Staged(bytes::Cursor<'input, 'w>),
Plain(Head<'input, 'd>),
}
impl<'input, 'w: 'input, 'd: 'input> Current<'input, 'w, 'd> {
fn staged(
staged: &'input mut storage::Shared<owners::Edge<owners::Plain, pool::Borrowed<'w>>>,
left: &'input mut transfer::Len,
) -> Self {
Self {
backing: HeadBacking::Staged(bytes::Cursor::new(staged)),
left,
}
}
fn plain(head: Head<'input, 'd>, left: &'input mut transfer::Len) -> Self {
Self {
backing: HeadBacking::Plain(head),
left,
}
}
}
impl<'input, 'w: 'input, 'd: 'input> buffer::Read<'input> for Retention<'input, 'w, 'd> {
fn as_slice(&self) -> &'input [u8] {
match &self.backing {
Backing::Empty => &[],
Backing::Staged(plan) => buffer::Read::as_slice(plan),
Backing::Plain(plan) => buffer::Read::as_slice(plan),
}
}
fn try_split<const N: usize>(self) -> Result<(&'input [u8; N], Self), consume::Error> {
let (head, backing) = match self.backing {
Backing::Empty => {
let (head, _) = buffer::Read::try_split(&[][..])?;
(head, Backing::Empty)
}
Backing::Staged(plan) => {
let (head, tail) = buffer::Read::try_split(plan)?;
(head, Backing::Staged(tail))
}
Backing::Plain(plan) => {
let (head, tail) = buffer::Read::try_split(plan)?;
(head, Backing::Plain(tail))
}
};
Ok((head, Self { backing }))
}
}
impl<'w, 'd> buffer::Source for Current<'_, 'w, 'd> {
type View<'input>
= Retention<'input, 'w, 'd>
where
Self: 'input;
fn remaining(&self) -> usize {
self.as_ref().len()
}
}
impl<'w, 'd> buffer::Cursor for Current<'_, 'w, 'd> {
fn try_get<'input>(
&'input self,
range: ops::Range<usize>,
) -> Result<Self::View<'input>, range::Error> {
let backing = match &self.backing {
HeadBacking::Staged(head) => Backing::Staged(head.try_get(range)?),
HeadBacking::Plain(head) => Backing::Plain(head.try_get(range)?),
};
Ok(Retention { backing })
}
}
impl<'w, 'd> buffer::Complete for Current<'_, 'w, 'd> {
fn view(&self) -> Self::View<'_> {
let backing = match &self.backing {
HeadBacking::Staged(head) => Backing::Staged(head.view()),
HeadBacking::Plain(head) => Backing::Plain(head.view()),
};
Retention { backing }
}
}
impl AsRef<[u8]> for Current<'_, '_, '_> {
fn as_ref(&self) -> &[u8] {
buffer::Read::as_slice(&self.view())
}
}
impl consume::Consume for Current<'_, '_, '_> {
fn remaining(&self) -> usize {
self.as_ref().len()
}
fn try_consume(&mut self, amount: usize) -> Result<(), consume::Error> {
match &mut self.backing {
HeadBacking::Staged(head) => head.try_consume(amount),
HeadBacking::Plain(head) => head.try_consume(amount),
}
}
fn consume_max(&mut self, max: usize) -> usize {
match &mut self.backing {
HeadBacking::Staged(head) => head.consume_max(max),
HeadBacking::Plain(head) => head.consume_max(max),
}
}
}
impl<'w, 'd> access::Head<'d> for Current<'_, 'w, 'd> {
fn commit(self) -> usize {
let consumed = match self.backing {
HeadBacking::Staged(head) => head.commit(),
HeadBacking::Plain(head) => access::Head::commit(head),
};
*self.left = transfer::Len::prefix(self.left.into_raw() - consumed, *self.left);
consumed
}
}
const _: () = {
assert!(mem::size_of::<Current<'_, '_, '_>>() <= 5 * mem::size_of::<usize>());
assert!(mem::size_of::<Retention<'_, '_, '_>>() == 4 * mem::size_of::<usize>());
};