use crate::error::Error;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use bytes::{Buf, BufMut, Bytes, BytesMut};
#[cfg(feature = "std")]
use std::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);
}
}
}
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 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,
};
use bytes::Bytes;
#[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]
#[should_panic(expected = "Can't encode 4 bytes into 5 bytes")]
fn test_encode_fixed_panic() {
let _: [u8; 5] = 42u32.encode_fixed();
}
}