#[cfg(any(feature = "scale-codec", feature = "jam-codec"))]
pub struct PrependCompactInput<'a, I> {
pub encoded_len: &'a [u8],
pub read: usize,
pub inner: &'a mut I,
}
#[cfg(any(feature = "scale-codec", feature = "jam-codec"))]
macro_rules! impl_prepend_compact_input {
($codec:ident) => {
use $codec::{Error, Input};
impl<'a, I: Input> Input for PrependCompactInput<'a, I> {
fn remaining_len(&mut self) -> Result<Option<usize>, Error> {
let remaining_compact = self.encoded_len.len().saturating_sub(self.read);
Ok(self.inner.remaining_len()?.map(|len| len.saturating_add(remaining_compact)))
}
fn read(&mut self, into: &mut [u8]) -> Result<(), Error> {
if into.is_empty() {
return Ok(());
}
let remaining_compact = self.encoded_len.len().saturating_sub(self.read);
if remaining_compact > 0 {
let to_read = into.len().min(remaining_compact);
into[..to_read].copy_from_slice(&self.encoded_len[self.read..][..to_read]);
self.read += to_read;
if to_read < into.len() {
self.inner.read(&mut into[to_read..])
} else {
Ok(())
}
} else {
self.inner.read(into)
}
}
}
};
}
#[cfg(feature = "scale-codec")]
pub mod scale_codec_impl {
use super::PrependCompactInput;
impl_prepend_compact_input!(scale_codec);
}
#[cfg(feature = "jam-codec")]
pub mod jam_codec_impl {
use super::PrependCompactInput;
impl_prepend_compact_input!(jam_codec);
}
#[cfg(test)]
#[cfg(any(feature = "scale-codec", feature = "jam-codec"))]
mod tests {
use super::PrependCompactInput;
macro_rules! codec_tests {
($codec:ident, $mod_name:ident) => {
mod $mod_name {
use super::PrependCompactInput;
use $codec::{Compact, Encode, Input};
#[test]
fn prepend_compact_input_works() {
let encoded_len = Compact(3u32).encode();
let inner = [2, 3, 4];
let mut input =
PrependCompactInput { encoded_len: encoded_len.as_ref(), read: 0, inner: &mut &inner[..] };
assert_eq!(input.remaining_len(), Ok(Some(4)));
let mut empty_buf = [];
assert_eq!(input.read(&mut empty_buf), Ok(()));
assert_eq!(input.remaining_len(), Ok(Some(4)));
assert_eq!(input.read, 0);
let mut buf = [0; 4];
assert_eq!(input.read(&mut buf), Ok(()));
assert_eq!(buf[0], encoded_len[0]);
assert_eq!(buf[1..], inner[..]);
assert_eq!(input.remaining_len(), Ok(Some(0)));
assert_eq!(input.read, encoded_len.len());
assert!(input.read(&mut buf).is_err());
}
#[test]
fn prepend_compact_input_incremental_read_works() {
let encoded_len = Compact(3u32).encode();
let inner = [2, 3, 4];
let mut input =
PrependCompactInput { encoded_len: encoded_len.as_ref(), read: 0, inner: &mut &inner[..] };
assert_eq!(input.remaining_len(), Ok(Some(4)));
let mut buf = [0u8; 2];
assert_eq!(input.read(&mut buf), Ok(()));
assert_eq!(buf[0], encoded_len[0]);
assert_eq!(buf[1], inner[0]);
assert_eq!(input.remaining_len(), Ok(Some(2)));
assert_eq!(input.read, encoded_len.len());
assert_eq!(input.read(&mut buf), Ok(()));
assert_eq!(buf[..], inner[1..]);
assert_eq!(input.remaining_len(), Ok(Some(0)));
assert_eq!(input.read, encoded_len.len());
assert!(input.read(&mut buf).is_err());
}
}
};
}
#[cfg(feature = "scale-codec")]
codec_tests!(scale_codec, scale_codec_impl);
#[cfg(feature = "jam-codec")]
codec_tests!(jam_codec, jam_codec_impl);
}