use crate::serialize::writer::*;
use crate::serialize::writer_unchecked::*;
use crate::{EncodesAs, WriteXDR};
use core::ops::Deref;
use xdr_common::{BytesM, StaticIfNoAlloc, VecM};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChunkedBytes<C, const MAX: u32 = { u32::MAX }>(pub C);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChunkedVec<C, const MAX: u32 = { u32::MAX }>(pub C);
impl<C, B, const MAX: u32> ChunkedBytes<C, MAX>
where
C: Deref<Target = [B]>,
B: AsRef<[u8]>,
{
pub fn len(&self) -> usize {
self.0.iter().map(|chunk| chunk.as_ref().len()).sum()
}
pub fn is_empty(&self) -> bool {
self.0.iter().all(|chunk| chunk.as_ref().is_empty())
}
}
impl<C, S, T, const MAX: u32> ChunkedVec<C, MAX>
where
C: Deref<Target = [S]>,
S: Deref<Target = [T]>,
{
pub fn len(&self) -> usize {
self.0.iter().map(|chunk| chunk.len()).sum()
}
pub fn is_empty(&self) -> bool {
self.0.iter().all(|chunk| chunk.is_empty())
}
}
impl<C, B, const MAX: u32> WriteXDR for ChunkedBytes<C, MAX>
where
C: Deref<Target = [B]>,
B: AsRef<[u8]>,
{
fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
let len = checked_len::<MAX>(self.0.iter().map(|chunk| chunk.as_ref().len()))?;
w.write_u32(len)?;
for chunk in self.0.iter() {
w.write_opaque_unpadded(chunk.as_ref())?;
}
w.write_opaque_padding(len as usize)
}
unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
let len = self.len();
debug_assert!(len <= MAX as usize, "ChunkedBytes exceeds declared bound");
unsafe {
w.write_u32(len as u32);
for chunk in self.0.iter() {
w.write_opaque_unpadded(chunk.as_ref());
}
w.write_opaque_padding(len);
}
}
}
impl<C, S, T, const MAX: u32> WriteXDR for ChunkedVec<C, MAX>
where
C: Deref<Target = [S]>,
S: Deref<Target = [T]>,
T: WriteXDR,
{
fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
let len = checked_len::<MAX>(self.0.iter().map(|chunk| chunk.len()))?;
w.write_u32(len)?;
for elt in self.0.iter().flat_map(|chunk| chunk.iter()) {
elt.write_xdr(w)?;
}
Ok(())
}
unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
let len = self.len();
debug_assert!(len <= MAX as usize, "ChunkedVec exceeds declared bound");
unsafe {
w.write_u32(len as u32);
for elt in self.0.iter().flat_map(|chunk| chunk.iter()) {
elt.write_xdr_unchecked(w);
}
}
}
}
fn checked_len<const MAX: u32>(mut lens: impl Iterator<Item = usize>) -> WriteResult<u32> {
lens.try_fold(0u32, |total, len| {
u32::try_from(len)
.ok()
.and_then(|len| total.checked_add(len))
})
.filter(|&len| len <= MAX)
.ok_or(WriteError::InvalidArrayLength)
}
impl<C, B, const MAX: u32> EncodesAs<Self> for ChunkedBytes<C, MAX>
where
C: Deref<Target = [B]>,
B: AsRef<[u8]>,
{
}
impl<C, B, const MAX: u32> EncodesAs<BytesM<MAX>> for ChunkedBytes<C, MAX>
where
C: Deref<Target = [B]>,
B: AsRef<[u8]>,
{
}
impl<C, S, T, const MAX: u32> EncodesAs<Self> for ChunkedVec<C, MAX>
where
C: Deref<Target = [S]>,
S: Deref<Target = [T]>,
T: WriteXDR,
{
}
impl<C, S, T, X, const MAX: u32> EncodesAs<VecM<X, MAX>> for ChunkedVec<C, MAX>
where
C: Deref<Target = [S]>,
S: Deref<Target = [T]>,
T: EncodesAs<X>,
X: StaticIfNoAlloc,
{
}
macro_rules! impl_const_max_size {
($($format:ident),*) => {$(
impl<C, const MAX: u32> crate::$format::ConstMaxSize for ChunkedBytes<C, MAX> {
const MAX_SIZE: Option<usize> =
crate::$format::const_writer::max_size_opaque_variable(MAX);
}
impl<C, const MAX: u32> crate::$format::ConstMaxSize for ChunkedVec<C, MAX> {
const MAX_SIZE: Option<usize> = None;
}
)*};
}
impl_const_max_size!(cxs, txs);