use crate::buf::{ReadBuf, WriteBuf};
use crate::error::{Error, Result};
use crate::varint;
pub trait Encode {
#[must_use]
fn encoded_size(&self) -> usize;
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()>;
}
pub trait Decode<'de>: Sized {
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self>;
}
impl Encode for u8 {
#[inline]
fn encoded_size(&self) -> usize {
1
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
buf.write_u8(*self)
}
}
impl<'de> Decode<'de> for u8 {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
buf.read_u8()
}
}
impl Encode for i8 {
#[inline]
fn encoded_size(&self) -> usize {
1
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
buf.write_i8(*self)
}
}
impl<'de> Decode<'de> for i8 {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
buf.read_i8()
}
}
impl Encode for bool {
#[inline]
fn encoded_size(&self) -> usize {
1
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
buf.write_u8(u8::from(*self))
}
}
impl<'de> Decode<'de> for bool {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
let value = match buf.peek_u8()? {
0 => false,
1 => true,
_ => return Err(Error::InvalidEncoding),
};
buf.consume(1);
Ok(value)
}
}
impl Encode for [u8] {
#[inline]
fn encoded_size(&self) -> usize {
self.len()
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
buf.write_bytes(self)
}
}
impl<const N: usize> Encode for [u8; N] {
#[inline]
fn encoded_size(&self) -> usize {
N
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
buf.write_bytes(self)
}
}
impl<'de, const N: usize> Decode<'de> for [u8; N] {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
buf.read_array()
}
}
impl<'de, const N: usize> Decode<'de> for &'de [u8; N] {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
buf.read_array_ref()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Be<T>(pub T);
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Le<T>(pub T);
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Varint<T>(pub T);
macro_rules! fixed_width_impls {
($($ty:ty: $read_be:ident, $write_be:ident, $read_le:ident, $write_le:ident;)*) => {$(
impl Encode for Be<$ty> {
#[inline]
fn encoded_size(&self) -> usize {
core::mem::size_of::<$ty>()
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
buf.$write_be(self.0)
}
}
impl<'de> Decode<'de> for Be<$ty> {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
buf.$read_be().map(Be)
}
}
impl Encode for Le<$ty> {
#[inline]
fn encoded_size(&self) -> usize {
core::mem::size_of::<$ty>()
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
buf.$write_le(self.0)
}
}
impl<'de> Decode<'de> for Le<$ty> {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
buf.$read_le().map(Le)
}
}
)*};
}
fixed_width_impls! {
u16: read_u16_be, write_u16_be, read_u16_le, write_u16_le;
u32: read_u32_be, write_u32_be, read_u32_le, write_u32_le;
u64: read_u64_be, write_u64_be, read_u64_le, write_u64_le;
u128: read_u128_be, write_u128_be, read_u128_le, write_u128_le;
i16: read_i16_be, write_i16_be, read_i16_le, write_i16_le;
i32: read_i32_be, write_i32_be, read_i32_le, write_i32_le;
i64: read_i64_be, write_i64_be, read_i64_le, write_i64_le;
i128: read_i128_be, write_i128_be, read_i128_le, write_i128_le;
f32: read_f32_be, write_f32_be, read_f32_le, write_f32_le;
f64: read_f64_be, write_f64_be, read_f64_le, write_f64_le;
}
macro_rules! varint_impls {
($($ty:ty: $len:path, $encode:path, $decode:path;)*) => {$(
impl Encode for Varint<$ty> {
#[inline]
fn encoded_size(&self) -> usize {
$len(self.0)
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
$encode(self.0, buf)
}
}
impl<'de> Decode<'de> for Varint<$ty> {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
$decode(buf).map(Varint)
}
}
)*};
}
varint_impls! {
u16: varint::encoded_len_u16, varint::encode_u16, varint::decode_u16;
u32: varint::encoded_len_u32, varint::encode_u32, varint::decode_u32;
u64: varint::encoded_len_u64, varint::encode_u64, varint::decode_u64;
usize: varint::encoded_len_usize, varint::encode_usize, varint::decode_usize;
i32: varint::encoded_len_i32, varint::encode_i32, varint::decode_i32;
i64: varint::encoded_len_i64, varint::encode_i64, varint::decode_i64;
}
macro_rules! tuple_impls {
($(($($name:ident $idx:tt),+);)*) => {$(
impl<$($name: Encode),+> Encode for ($($name,)+) {
#[inline]
fn encoded_size(&self) -> usize {
0 $(+ self.$idx.encoded_size())+
}
#[inline]
fn encode(&self, buf: &mut WriteBuf<'_>) -> Result<()> {
if self.encoded_size() > buf.remaining() {
return Err(Error::BufferFull);
}
let start = buf.position();
let result = (|| {
$(self.$idx.encode(buf)?;)+
Ok(())
})();
if result.is_err() {
buf.rewind(start);
}
result
}
}
impl<'de, $($name: Decode<'de>),+> Decode<'de> for ($($name,)+) {
#[inline]
fn decode(buf: &mut ReadBuf<'de>) -> Result<Self> {
let mut scratch = buf.clone();
let value = ($($name::decode(&mut scratch)?,)+);
*buf = scratch;
Ok(value)
}
}
)*};
}
tuple_impls! {
(A 0, B 1);
(A 0, B 1, C 2);
(A 0, B 1, C 2, D 3);
(A 0, B 1, C 2, D 3, E 4);
(A 0, B 1, C 2, D 3, E 4, F 5);
}
#[cfg(test)]
mod tests {
use super::*;
struct Rejects;
impl Encode for Rejects {
fn encoded_size(&self) -> usize {
1
}
fn encode(&self, _buf: &mut WriteBuf<'_>) -> Result<()> {
Err(Error::InvalidEncoding)
}
}
fn round_trip<T>(value: T) -> T
where
T: Encode + for<'de> Decode<'de> + PartialEq + core::fmt::Debug,
{
let mut out = [0u8; 64];
let mut buf = WriteBuf::new(&mut out);
value.encode(&mut buf).unwrap();
let n = buf.position();
assert_eq!(n, value.encoded_size(), "encoded_size must be exact");
let mut r = ReadBuf::new(&out[..n]);
let decoded = T::decode(&mut r).unwrap();
assert!(r.is_empty());
decoded
}
#[test]
fn primitive_round_trips() {
assert_eq!(round_trip(0xABu8), 0xAB);
assert_eq!(round_trip(-5i8), -5);
assert!(round_trip(true));
assert!(!round_trip(false));
assert_eq!(round_trip([1u8, 2, 3]), [1, 2, 3]);
}
#[test]
fn wrapper_round_trips() {
assert_eq!(round_trip(Be(u16::MAX)), Be(u16::MAX));
assert_eq!(round_trip(Le(0x0102_0304_u32)), Le(0x0102_0304));
assert_eq!(round_trip(Be(u64::MAX - 1)), Be(u64::MAX - 1));
assert_eq!(round_trip(Le(u128::MAX / 3)), Le(u128::MAX / 3));
assert_eq!(round_trip(Be(i32::MIN)), Be(i32::MIN));
assert_eq!(round_trip(Le(i64::MIN)), Le(i64::MIN));
assert_eq!(round_trip(Be(i128::MIN)), Be(i128::MIN));
assert_eq!(round_trip(Le(-3i16)), Le(-3));
assert_eq!(round_trip(Be(2.5f64)), Be(2.5));
assert_eq!(round_trip(Le(-0.5f64)), Le(-0.5));
assert_eq!(round_trip(Be(9.0f32)), Be(9.0));
for v in [0u16, 300, u16::MAX] {
assert_eq!(round_trip(Varint(v)), Varint(v));
}
for v in [0usize, 1 << 20, usize::MAX] {
assert_eq!(round_trip(Varint(v)), Varint(v));
}
assert_eq!(round_trip(Varint(u32::MAX)), Varint(u32::MAX));
assert_eq!(round_trip(Varint(u64::MAX)), Varint(u64::MAX));
assert_eq!(round_trip(Varint(i32::MIN)), Varint(i32::MIN));
assert_eq!(round_trip(Varint(i64::MAX)), Varint(i64::MAX));
}
#[test]
fn byte_order_is_explicit() {
let mut out = [0u8; 4];
let mut buf = WriteBuf::new(&mut out);
Be(0x0102u16).encode(&mut buf).unwrap();
Le(0x0102u16).encode(&mut buf).unwrap();
assert_eq!(out, [0x01, 0x02, 0x02, 0x01]);
}
#[test]
fn tuples_encode_in_order() {
let value = (1u8, Be(0x0203u16), Varint(300u32), true);
let mut out = [0u8; 16];
let mut buf = WriteBuf::new(&mut out);
value.encode(&mut buf).unwrap();
assert_eq!(buf.written(), &[1, 2, 3, 0xAC, 0x02, 1]);
assert_eq!(round_trip(value), value);
assert_eq!(round_trip((1u8, 2u8)), (1, 2));
assert_eq!(round_trip((1u8, 2u8, 3u8)), (1, 2, 3));
assert_eq!(round_trip((1u8, 2u8, 3u8, 4u8, 5u8)), (1, 2, 3, 4, 5));
assert_eq!(
round_trip((1u8, 2u8, 3u8, 4u8, 5u8, 6u8)),
(1, 2, 3, 4, 5, 6)
);
}
#[test]
fn tuple_decode_and_encode_are_atomic() {
let mut r = ReadBuf::new(&[7, 0x01]);
assert_eq!(<(u8, Be<u16>)>::decode(&mut r), Err(Error::UnexpectedEof));
assert_eq!(r.position(), 0);
let mut out = [0xEEu8; 2];
let mut buf = WriteBuf::new(&mut out);
assert_eq!((1u8, Be(2u16)).encode(&mut buf), Err(Error::BufferFull));
assert_eq!(buf.position(), 0);
assert_eq!(out, [0xEE, 0xEE], "a short buffer writes nothing");
let mut out = [0u8; 4];
let mut buf = WriteBuf::new(&mut out);
assert_eq!((1u8, Rejects).encode(&mut buf), Err(Error::InvalidEncoding));
assert_eq!(buf.position(), 0);
}
#[test]
fn bool_is_strict() {
let mut r = ReadBuf::new(&[2]);
assert_eq!(bool::decode(&mut r), Err(Error::InvalidEncoding));
assert_eq!(r.position(), 0);
assert_eq!(
bool::decode(&mut ReadBuf::new(&[])),
Err(Error::UnexpectedEof)
);
}
#[test]
fn byte_slices_and_array_refs() {
let mut out = [0u8; 8];
let mut buf = WriteBuf::new(&mut out);
let raw: &[u8] = b"wire";
assert_eq!(raw.encoded_size(), 4);
raw.encode(&mut buf).unwrap();
assert_eq!(buf.written(), b"wire");
let data = *b"abcdef";
let mut r = ReadBuf::new(&data);
let head = <&[u8; 4]>::decode(&mut r).unwrap();
assert_eq!(head, b"abcd");
assert_eq!(<[u8; 4]>::decode(&mut r), Err(Error::UnexpectedEof));
}
#[test]
fn encode_errors_propagate() {
let mut out = [0u8; 1];
let mut buf = WriteBuf::new(&mut out);
assert_eq!(Be(1u32).encode(&mut buf), Err(Error::BufferFull));
assert_eq!(Le(1u32).encode(&mut buf), Err(Error::BufferFull));
assert_eq!(Varint(300u32).encode(&mut buf), Err(Error::BufferFull));
assert_eq!(buf.position(), 0);
let mut r = ReadBuf::new(&[0x80]);
assert_eq!(Varint::<u64>::decode(&mut r), Err(Error::UnexpectedEof));
assert_eq!(Le::<u16>::decode(&mut r), Err(Error::UnexpectedEof));
assert_eq!(Be::<u16>::decode(&mut r), Err(Error::UnexpectedEof));
}
}