use core::fmt;
pub trait EncodeSink {
fn remaining_capacity(&self) -> Option<usize>;
fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull>;
fn write_parts(&mut self, parts: &[&[u8]]) -> Result<(), SinkFull> {
let available = self.remaining_capacity();
let mut needed = 0usize;
for part in parts {
needed = needed.checked_add(part.len()).ok_or(SinkFull {
needed: usize::MAX,
available: available.unwrap_or(usize::MAX),
})?;
}
if let Some(available) = available
&& needed > available
{
return Err(SinkFull { needed, available });
}
for part in parts {
self.write(part)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SinkFull {
pub needed: usize,
pub available: usize,
}
impl fmt::Display for SinkFull {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"sink full: need {}, have {}",
self.needed, self.available
)
}
}
impl core::error::Error for SinkFull {}
impl EncodeSink for &mut [u8] {
fn remaining_capacity(&self) -> Option<usize> {
Some(self.len())
}
fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
if bytes.len() > self.len() {
return Err(SinkFull {
needed: bytes.len(),
available: self.len(),
});
}
let (head, tail) = core::mem::take(self).split_at_mut(bytes.len());
head.copy_from_slice(bytes);
*self = tail;
Ok(())
}
}
#[cfg(feature = "heapless")]
impl<const N: usize> EncodeSink for heapless::Vec<u8, N> {
fn remaining_capacity(&self) -> Option<usize> {
Some(N - self.len())
}
fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
let available = N - self.len();
self.extend_from_slice(bytes).map_err(|_| SinkFull {
needed: bytes.len(),
available,
})
}
}
#[cfg(feature = "alloc")]
impl EncodeSink for alloc::vec::Vec<u8> {
fn remaining_capacity(&self) -> Option<usize> {
None
}
fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
self.extend_from_slice(bytes);
Ok(())
}
}
#[cfg(feature = "alloc")]
impl EncodeSink for bytes::BytesMut {
fn remaining_capacity(&self) -> Option<usize> {
None
}
fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
self.extend_from_slice(bytes);
Ok(())
}
}
#[cfg(test)]
mod tests;