#[cfg(feature = "alloc")]
use alloc::vec::Vec;
pub mod encoders;
pub trait Encode {
type Encoder<'e>: Encoder
where
Self: 'e;
fn encoder(&self) -> Self::Encoder<'_>;
}
pub trait Encoder {
fn current_chunk(&self) -> &[u8];
fn advance(&mut self) -> EncoderStatus;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[must_use = "encoding has to stop when Finished is returned"]
pub enum EncoderStatus {
HasMore,
Finished,
}
impl EncoderStatus {
pub fn has_more(&self) -> bool { matches!(self, Self::HasMore) }
pub fn has_finished(&self) -> bool { matches!(self, Self::Finished) }
}
#[macro_export]
macro_rules! encoder_newtype {
(
$(#[$($struct_attr:tt)*])*
$vis:vis struct $name:ident<$lt:lifetime>($encoder:ty);
) => {
$(#[$($struct_attr)*])*
$vis struct $name<$lt>($encoder, core::marker::PhantomData<&$lt $encoder>);
#[allow(clippy::type_complexity)]
impl<$lt> $name<$lt> {
pub(crate) const fn new(encoder: $encoder) -> $name<$lt> {
$name(encoder, core::marker::PhantomData)
}
}
impl<$lt> $crate::Encoder for $name<$lt> {
#[inline]
fn current_chunk(&self) -> &[u8] { self.0.current_chunk() }
#[inline]
fn advance(&mut self) -> $crate::EncoderStatus { self.0.advance() }
}
}
}
#[macro_export]
macro_rules! encoder_newtype_exact {
(
$(#[$($struct_attr:tt)*])*
$vis:vis struct $name:ident<$lt:lifetime>($encoder:ty);
) => {
$crate::encoder_newtype! {
$(#[$($struct_attr)*])*
$vis struct $name<$lt>($encoder);
}
impl<$lt> $crate::ExactSizeEncoder for $name<$lt> {
#[inline]
fn len(&self) -> usize { self.0.len() }
}
}
}
#[derive(Debug, Clone)]
pub struct EncoderByteIter<T: Encoder> {
enc: T,
position: usize,
}
impl<T: Encoder> EncoderByteIter<T> {
pub fn new(encoder: T) -> Self { Self { enc: encoder, position: 0 } }
pub fn peek_chunk(&mut self) -> &[u8] {
if self.position < self.enc.current_chunk().len() {
&self.enc.current_chunk()[self.position..]
} else {
loop {
if self.enc.advance().has_finished() {
return &[];
}
if !self.enc.current_chunk().is_empty() {
self.position = 0;
return self.enc.current_chunk();
}
}
}
}
}
impl<T: Encoder> Iterator for EncoderByteIter<T> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(b) = self.enc.current_chunk().get(self.position) {
self.position += 1;
return Some(*b);
} else if self.enc.advance().has_finished() {
return None;
}
self.position = 0;
}
}
fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
if let Some(b) =
self.position.checked_add(n).and_then(|pos| self.enc.current_chunk().get(pos))
{
self.position += n + 1;
return Some(*b);
}
n -= self.enc.current_chunk().len() - self.position;
if self.enc.advance().has_finished() {
return None;
}
loop {
if let Some(b) = self.enc.current_chunk().get(n) {
self.position = n + 1;
return Some(*b);
}
n -= self.enc.current_chunk().len();
if self.enc.advance().has_finished() {
return None;
}
}
}
}
impl<T> ExactSizeIterator for EncoderByteIter<T>
where
T: Encoder + ExactSizeEncoder,
{
fn len(&self) -> usize { self.enc.len() - self.position }
}
pub trait ExactSizeEncoder: Encoder {
fn len(&self) -> usize;
fn is_empty(&self) -> bool { self.len() == 0 }
}
#[cfg(feature = "alloc")]
pub fn encode_to_vec<T>(object: &T) -> Vec<u8>
where
T: Encode + ?Sized,
{
let mut encoder = object.encoder();
drain_to_vec(&mut encoder)
}
#[cfg(feature = "alloc")]
pub fn drain_to_vec<T>(encoder: &mut T) -> Vec<u8>
where
T: Encoder + ?Sized,
{
let mut vec = Vec::new();
loop {
vec.extend_from_slice(encoder.current_chunk());
if encoder.advance().has_finished() {
break;
}
}
vec
}
#[cfg(feature = "std")]
pub fn encode_to_writer<T, W>(object: &T, writer: W) -> Result<(), std::io::Error>
where
T: Encode + ?Sized,
W: std::io::Write,
{
let mut encoder = object.encoder();
drain_to_writer(&mut encoder, writer)
}
#[cfg(feature = "std")]
pub fn drain_to_writer<T, W>(encoder: &mut T, mut writer: W) -> Result<(), std::io::Error>
where
T: Encoder + ?Sized,
W: std::io::Write,
{
loop {
writer.write_all(encoder.current_chunk())?;
if encoder.advance().has_finished() {
break;
}
}
Ok(())
}
#[track_caller]
pub fn check_encode<T: Encode + ?Sized>(value: &T, expected: &[u8]) {
check_encoder(&mut value.encoder(), expected);
}
#[track_caller]
pub fn check_encoder<T: Encoder + ?Sized>(encoder: &mut T, mut expected: &[u8]) {
let orig_expected_len = expected.len();
let mut chunk_number = 0usize;
let mut bytes_processed = 0usize;
loop {
let chunk = encoder.current_chunk();
assert!(
chunk.len() <= expected.len(),
"encoder yielded more bytes ({}) than expected ({})",
bytes_processed + chunk.len(),
orig_expected_len
);
if let Some((i, _)) =
chunk.iter().zip(&expected[..chunk.len()]).enumerate().find(|&(_, (a, b))| a != b)
{
panic!(
"encoder did not yield expected bytes - difference in chunk #{}, after {} bytes",
chunk_number,
bytes_processed + i
);
}
bytes_processed += chunk.len();
expected = &expected[chunk.len()..];
chunk_number += 1;
if encoder.advance().has_finished() {
break;
}
}
assert!(
expected.is_empty(),
"encoder did not yield enough bytes - {} more expected",
expected.len()
);
}
impl<T: Encoder> Encoder for Option<T> {
fn current_chunk(&self) -> &[u8] {
match self {
Some(encoder) => encoder.current_chunk(),
None => &[],
}
}
fn advance(&mut self) -> EncoderStatus {
match self {
Some(encoder) => encoder.advance(),
None => EncoderStatus::Finished,
}
}
}