use crate::error::Error;
#[cfg(not(feature = "std"))]
use alloc::{sync::Arc, vec::Vec};
use bytes::{Buf, BufMut, Bytes, BytesMut};
#[cfg(feature = "std")]
use std::{sync::Arc, vec::Vec};
pub trait FixedSize {
const SIZE: usize;
}
pub trait EncodeSize {
fn encode_size(&self) -> usize;
#[doc(hidden)]
#[inline]
fn encode_size_slice(values: &[Self]) -> usize
where
Self: Sized,
{
values.iter().map(EncodeSize::encode_size).sum()
}
#[inline]
fn encode_inline_size(&self) -> usize {
self.encode_size()
}
#[doc(hidden)]
#[inline]
fn encode_inline_size_slice(values: &[Self]) -> usize
where
Self: Sized,
{
values
.iter()
.map(EncodeSize::encode_inline_size)
.sum::<usize>()
}
}
impl<T: FixedSize> EncodeSize for T {
#[inline]
fn encode_size(&self) -> usize {
Self::SIZE
}
#[inline]
fn encode_size_slice(values: &[Self]) -> usize
where
Self: Sized,
{
Self::SIZE * values.len()
}
#[inline]
fn encode_inline_size_slice(values: &[Self]) -> usize
where
Self: Sized,
{
Self::encode_size_slice(values)
}
}
pub trait Write {
fn write(&self, buf: &mut impl BufMut);
#[doc(hidden)]
#[inline]
fn write_slice(values: &[Self], buf: &mut impl BufMut)
where
Self: Sized,
{
for item in values {
item.write(buf);
}
}
#[inline]
fn write_bufs(&self, buf: &mut impl BufsMut) {
self.write(buf);
}
#[doc(hidden)]
#[inline]
fn write_slice_bufs(values: &[Self], buf: &mut impl BufsMut)
where
Self: Sized,
{
for item in values {
item.write_bufs(buf);
}
}
}
impl<T: EncodeSize + ?Sized> EncodeSize for Arc<T> {
#[inline]
fn encode_size(&self) -> usize {
self.as_ref().encode_size()
}
#[inline]
fn encode_inline_size(&self) -> usize {
self.as_ref().encode_inline_size()
}
}
impl<T: Write + ?Sized> Write for Arc<T> {
#[inline]
fn write(&self, buf: &mut impl BufMut) {
self.as_ref().write(buf);
}
#[inline]
fn write_bufs(&self, buf: &mut impl BufsMut) {
self.as_ref().write_bufs(buf);
}
}
pub trait Read: Sized {
type Cfg: Clone + Send + Sync + 'static;
fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error>;
#[doc(hidden)]
#[inline]
fn read_vec(buf: &mut impl Buf, len: usize, cfg: &Self::Cfg) -> Result<Vec<Self>, Error> {
let mut values = Vec::with_capacity(len);
for _ in 0..len {
values.push(Self::read_cfg(buf, cfg)?);
}
Ok(values)
}
#[doc(hidden)]
#[inline]
fn read_array<const N: usize>(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<[Self; N], Error> {
Ok(Self::read_vec(buf, N, cfg)?
.try_into()
.unwrap_or_else(|_| unreachable!("array length should match capacity")))
}
}
pub trait Encode: Write + EncodeSize {
fn encode(&self) -> Bytes {
self.encode_mut().freeze()
}
fn encode_mut(&self) -> BytesMut {
let len = self.encode_size();
let mut buffer = BytesMut::with_capacity(len);
self.write(&mut buffer);
assert_eq!(buffer.len(), len, "write() did not write expected bytes");
buffer
}
}
impl<T: Write + EncodeSize> Encode for T {}
pub trait EncodeShared: Encode + Send + Sync {}
impl<T: Encode + Send + Sync> EncodeShared for T {}
pub trait Decode: Read {
fn decode_cfg(mut buf: impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
let result = Self::read_cfg(&mut buf, cfg)?;
let remaining = buf.remaining();
if remaining > 0 {
return Err(Error::ExtraData(remaining));
}
Ok(result)
}
}
impl<T: Read> Decode for T {}
pub trait Codec: Encode + Decode {}
impl<T: Encode + Decode> Codec for T {}
pub trait EncodeFixed: Write + FixedSize {
fn encode_fixed<const N: usize>(&self) -> [u8; N] {
assert_eq!(
N,
Self::SIZE,
"Can't encode {} bytes into {} bytes",
Self::SIZE,
N
);
let mut array = [0u8; N];
let mut buf = &mut array[..];
self.write(&mut buf);
assert_eq!(buf.len(), 0);
array
}
}
impl<T: Write + FixedSize> EncodeFixed for T {}
pub trait DecodeFixed: Read<Cfg = ()> + FixedSize {
fn decode_fixed<const N: usize>(bytes: [u8; N]) -> Result<Self, Error> {
assert_eq!(
N,
Self::SIZE,
"Can't decode {} bytes into {} bytes",
N,
Self::SIZE
);
Self::decode_cfg(bytes.as_ref(), &())
}
}
impl<T: Read<Cfg = ()> + FixedSize> DecodeFixed for T {}
pub trait CodecFixed: Codec + FixedSize {}
impl<T: Codec + FixedSize> CodecFixed for T {}
pub trait CodecShared: Codec + Send + Sync {}
impl<T: Codec + Send + Sync> CodecShared for T {}
pub trait CodecFixedShared: CodecFixed<Cfg = ()> + Send + Sync {}
impl<T: CodecFixed<Cfg = ()> + Send + Sync> CodecFixedShared for T {}
pub trait BufsMut: BufMut {
fn push(&mut self, bytes: impl Into<Bytes>);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
extensions::{DecodeExt, ReadExt},
Error, FixedArray,
};
use bytes::Bytes;
use core::marker::PhantomData;
#[test]
fn test_insufficient_buffer() {
let mut reader = Bytes::from_static(&[0x01, 0x02]);
assert!(matches!(u32::read(&mut reader), Err(Error::EndOfBuffer)));
}
#[test]
fn test_extra_data() {
let encoded = Bytes::from_static(&[0x01, 0x02]);
assert!(matches!(u8::decode(encoded), Err(Error::ExtraData(1))));
}
#[test]
fn test_encode_fixed() {
let value = 42u32;
let encoded: [u8; 4] = value.encode_fixed();
let decoded = <u32>::decode(&encoded[..]).unwrap();
assert_eq!(value, decoded);
}
#[test]
fn test_arc_encode() {
let value = Arc::new(vec![1u8, 2, 3]);
assert_eq!(value.encode(), value.as_ref().encode());
assert_eq!(value.encode_size(), value.as_ref().encode_size());
}
#[test]
#[should_panic(expected = "Can't encode 4 bytes into 5 bytes")]
fn test_encode_fixed_panic() {
let _: [u8; 5] = 42u32.encode_fixed();
}
#[derive(Debug, Eq, PartialEq, FixedArray)]
struct FixedBytes([u8; 2]);
impl Write for FixedBytes {
fn write(&self, buf: &mut impl BufMut) {
self.0.write(buf);
}
}
impl Read for FixedBytes {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
Ok(Self(<[u8; Self::SIZE]>::read(buf)?))
}
}
impl FixedSize for FixedBytes {
const SIZE: usize = 2;
}
#[test]
fn test_fixed_array() {
let value = FixedBytes([1, 2]);
let encoded: [u8; FixedBytes::SIZE] = (&value).into();
assert_eq!(encoded, [1, 2]);
assert_eq!(<[u8; FixedBytes::SIZE]>::from(value), encoded);
assert_eq!(FixedBytes::try_from(encoded).unwrap(), FixedBytes([1, 2]));
assert_eq!(FixedBytes::try_from(&encoded).unwrap(), FixedBytes([1, 2]));
assert_eq!(
FixedBytes::try_from([1u8, 2].as_slice()).unwrap(),
FixedBytes([1, 2])
);
assert!(matches!(
FixedBytes::try_from([1u8].as_slice()),
Err(Error::EndOfBuffer)
));
assert!(matches!(
FixedBytes::try_from([1u8, 2, 3].as_slice()),
Err(Error::ExtraData(1))
));
}
#[test]
fn test_decode_fixed() {
assert_eq!(
FixedBytes::decode_fixed([1, 2]).unwrap(),
FixedBytes([1, 2])
);
}
#[test]
#[should_panic(expected = "Can't decode 3 bytes into 2 bytes")]
fn test_decode_fixed_panic() {
let _ = FixedBytes::decode_fixed([1, 2, 3]);
}
#[derive(Debug, Eq, PartialEq, FixedArray)]
#[fixed_array(infallible)]
struct InfallibleFixedBytes([u8; 2]);
impl Write for InfallibleFixedBytes {
fn write(&self, buf: &mut impl BufMut) {
self.0.write(buf);
}
}
impl Read for InfallibleFixedBytes {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
Ok(Self(<[u8; Self::SIZE]>::read(buf)?))
}
}
impl FixedSize for InfallibleFixedBytes {
const SIZE: usize = 2;
}
#[test]
fn test_fixed_array_infallible() {
let value = InfallibleFixedBytes([1, 2]);
let encoded: [u8; InfallibleFixedBytes::SIZE] = (&value).into();
assert_eq!(encoded, [1, 2]);
assert_eq!(<[u8; InfallibleFixedBytes::SIZE]>::from(value), encoded);
assert_eq!(
InfallibleFixedBytes::from(encoded),
InfallibleFixedBytes([1, 2])
);
assert_eq!(
InfallibleFixedBytes::from(&encoded),
InfallibleFixedBytes([1, 2])
);
assert_eq!(
InfallibleFixedBytes::try_from([1u8, 2].as_slice()).unwrap(),
InfallibleFixedBytes([1, 2])
);
assert!(matches!(
InfallibleFixedBytes::try_from([1u8, 2, 3].as_slice()),
Err(Error::ExtraData(1))
));
}
#[derive(Debug, Eq, PartialEq, FixedArray)]
#[fixed_array(bytes([u8; N]))]
struct GenericFixed<const N: usize>([u8; N]);
impl<const N: usize> Write for GenericFixed<N> {
fn write(&self, buf: &mut impl BufMut) {
self.0.write(buf);
}
}
impl<const N: usize> Read for GenericFixed<N> {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
Ok(Self(<[u8; N]>::read(buf)?))
}
}
impl<const N: usize> FixedSize for GenericFixed<N> {
const SIZE: usize = N;
}
#[test]
fn test_fixed_array_generic() {
let value = GenericFixed::<3>([1, 2, 3]);
let encoded: [u8; 3] = (&value).into();
assert_eq!(encoded, [1, 2, 3]);
assert_eq!(<[u8; 3]>::from(value), encoded);
assert_eq!(
GenericFixed::<3>::try_from(encoded).unwrap(),
GenericFixed([1, 2, 3])
);
assert_eq!(
GenericFixed::<3>::try_from(&encoded).unwrap(),
GenericFixed([1, 2, 3])
);
assert_eq!(
GenericFixed::<3>::try_from([1u8, 2, 3].as_slice()).unwrap(),
GenericFixed([1, 2, 3])
);
}
#[derive(Debug, Eq, PartialEq, FixedArray)]
#[fixed_array(infallible, bytes([u8; N]))]
struct GenericInfallible<const N: usize>([u8; N]);
impl<const N: usize> Write for GenericInfallible<N> {
fn write(&self, buf: &mut impl BufMut) {
self.0.write(buf);
}
}
impl<const N: usize> Read for GenericInfallible<N> {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
Ok(Self(<[u8; N]>::read(buf)?))
}
}
impl<const N: usize> FixedSize for GenericInfallible<N> {
const SIZE: usize = N;
}
#[test]
fn test_fixed_array_generic_infallible() {
let value = GenericInfallible::<3>([1, 2, 3]);
let encoded: [u8; 3] = (&value).into();
assert_eq!(encoded, [1, 2, 3]);
assert_eq!(<[u8; 3]>::from(value), encoded);
assert_eq!(
GenericInfallible::<3>::from(encoded),
GenericInfallible([1, 2, 3])
);
assert_eq!(
GenericInfallible::<3>::from(&encoded),
GenericInfallible([1, 2, 3])
);
assert_eq!(
GenericInfallible::<3>::try_from([1u8, 2, 3].as_slice()).unwrap(),
GenericInfallible([1, 2, 3])
);
}
trait FixedArrayBound {}
#[derive(Debug, Eq, PartialEq)]
struct Bounded;
impl FixedArrayBound for Bounded {}
#[derive(Debug, Eq, PartialEq, FixedArray)]
#[fixed_array(bytes([u8; 2]))]
struct BoundedGeneric<T> {
marker: PhantomData<T>,
raw: [u8; 2],
}
impl<T: FixedArrayBound> Write for BoundedGeneric<T> {
fn write(&self, buf: &mut impl BufMut) {
self.raw.write(buf);
}
}
impl<T: FixedArrayBound> Read for BoundedGeneric<T> {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
Ok(Self {
marker: PhantomData,
raw: <[u8; 2]>::read(buf)?,
})
}
}
impl<T: FixedArrayBound> FixedSize for BoundedGeneric<T> {
const SIZE: usize = 2;
}
#[test]
fn test_fixed_array_bounded_generic() {
let value = BoundedGeneric::<Bounded> {
marker: PhantomData,
raw: [1, 2],
};
let encoded: [u8; 2] = (&value).into();
assert_eq!(encoded, [1, 2]);
assert_eq!(<[u8; 2]>::from(value).as_ref(), &[1, 2]);
assert_eq!(
BoundedGeneric::<Bounded>::try_from(encoded).unwrap().raw,
[1, 2]
);
assert_eq!(
BoundedGeneric::<Bounded>::try_from(&encoded).unwrap().raw,
[1, 2]
);
assert_eq!(
BoundedGeneric::<Bounded>::try_from([1u8, 2].as_slice())
.unwrap()
.raw,
[1, 2]
);
}
#[derive(Debug, Eq, PartialEq, FixedArray)]
#[fixed_array(bytes([u8; 2]))]
struct LifetimeFixed<'a> {
marker: PhantomData<&'a ()>,
raw: [u8; 2],
}
impl Write for LifetimeFixed<'_> {
fn write(&self, buf: &mut impl BufMut) {
self.raw.write(buf);
}
}
impl Read for LifetimeFixed<'_> {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
Ok(Self {
marker: PhantomData,
raw: <[u8; 2]>::read(buf)?,
})
}
}
impl FixedSize for LifetimeFixed<'_> {
const SIZE: usize = 2;
}
#[test]
fn test_fixed_array_lifetime() {
let value = LifetimeFixed {
marker: PhantomData,
raw: [1, 2],
};
let encoded: [u8; LifetimeFixed::SIZE] = (&value).into();
assert_eq!(encoded, [1, 2]);
assert_eq!(<[u8; LifetimeFixed::SIZE]>::from(value).as_ref(), &[1, 2]);
assert_eq!(LifetimeFixed::try_from(encoded).unwrap().raw, [1, 2]);
assert_eq!(LifetimeFixed::try_from(&encoded).unwrap().raw, [1, 2]);
assert_eq!(
LifetimeFixed::try_from([1u8, 2].as_slice()).unwrap().raw,
[1, 2]
);
}
}