use crate::{BitDecode, BitEncode, BitReader, BitWriter, ErrorKind};
use bytes::{Buf, BytesMut};
use core::marker::PhantomData;
use std::io;
use tokio_util::codec::{Decoder, Encoder};
pub struct BinCodec<T>(PhantomData<T>);
impl<T> BinCodec<T> {
#[must_use]
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<T> Default for BinCodec<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: BitDecode + BitEncode> Decoder for BinCodec<T> {
type Item = T;
type Error = io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T>, io::Error> {
if src.is_empty() {
return Ok(None);
}
let mut reader = BitReader::with_layout(&src[..], <T as BitEncode>::LAYOUT);
match <T as BitDecode>::bit_decode(&mut reader) {
Ok(item) => {
let consumed = reader.bit_pos() / 8;
src.advance(consumed);
Ok(Some(item))
}
Err(e)
if matches!(
e.kind,
ErrorKind::UnexpectedEof { .. } | ErrorKind::Incomplete { .. }
) =>
{
Ok(None)
}
Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string())),
}
}
}
impl<T: BitEncode> Encoder<T> for BinCodec<T> {
type Error = io::Error;
fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), io::Error> {
let mut w = BitWriter::with_layout(<T as BitEncode>::LAYOUT);
item.bit_encode(&mut w)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
dst.extend_from_slice(&w.into_bytes());
Ok(())
}
}
#[cfg(test)]
mod component {
use super::BinCodec;
use bnb::bin;
use bytes::BytesMut;
use tokio_util::codec::{Decoder, Encoder};
#[bin(big)]
#[derive(Debug, Clone, PartialEq, Eq)]
struct Msg {
a: u16,
b: u16,
}
#[bin(big, magic = 0xCAFEu16)]
#[derive(Debug, Clone, PartialEq, Eq)]
struct Magic {
v: u8,
}
#[bin(little)]
#[derive(Debug, Clone, PartialEq, Eq)]
struct LeMsg {
a: u16,
b: u32,
}
#[test]
fn little_endian_message_round_trips() {
let mut codec = BinCodec::<LeMsg>::new();
let mut buf = BytesMut::new();
let m = LeMsg {
a: 0x1234,
b: 0xAABB_CCDD,
};
codec.encode(m.clone(), &mut buf).unwrap();
assert_eq!(&buf[..], &[0x34, 0x12, 0xDD, 0xCC, 0xBB, 0xAA]);
assert_eq!(codec.decode(&mut buf).unwrap(), Some(m));
assert!(buf.is_empty());
}
#[test]
fn encoder_writes_exact_message_bytes() {
let mut codec = BinCodec::<Msg>::new();
let mut buf = BytesMut::new();
codec
.encode(
Msg {
a: 0x0102,
b: 0x0304,
},
&mut buf,
)
.unwrap();
assert_eq!(&buf[..], &[0x01, 0x02, 0x03, 0x04]);
}
#[test]
fn encode_then_decode_round_trips_and_drains() {
let mut codec = BinCodec::<Msg>::new();
let mut buf = BytesMut::new();
let m = Msg {
a: 0xAABB,
b: 0xCCDD,
};
codec.encode(m.clone(), &mut buf).unwrap();
assert_eq!(codec.decode(&mut buf).unwrap(), Some(m));
assert!(buf.is_empty(), "decode consumed exactly the one frame");
}
#[test]
fn decode_empty_buffer_is_none() {
let mut codec = BinCodec::<Msg>::new();
let mut buf = BytesMut::new();
assert_eq!(codec.decode(&mut buf).unwrap(), None);
}
#[test]
fn decode_partial_frame_is_none_and_keeps_bytes() {
let mut codec = BinCodec::<Msg>::new();
let mut buf = BytesMut::from(&[0x01, 0x02][..]); assert_eq!(codec.decode(&mut buf).unwrap(), None);
assert_eq!(
&buf[..],
&[0x01, 0x02],
"a partial frame is left for the next read"
);
}
#[test]
fn decode_consumes_one_message_and_leaves_the_tail() {
let mut codec = BinCodec::<Msg>::new();
let mut buf = BytesMut::from(&[0x01, 0x02, 0x03, 0x04, 0xEE, 0xFF][..]);
assert_eq!(
codec.decode(&mut buf).unwrap(),
Some(Msg {
a: 0x0102,
b: 0x0304
})
);
assert_eq!(
&buf[..],
&[0xEE, 0xFF],
"trailing bytes remain for the next frame"
);
}
#[test]
fn decode_walks_back_to_back_messages() {
let mut codec = BinCodec::<Msg>::new();
let mut buf = BytesMut::from(&[0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04][..]);
assert_eq!(codec.decode(&mut buf).unwrap(), Some(Msg { a: 1, b: 2 }));
assert_eq!(codec.decode(&mut buf).unwrap(), Some(Msg { a: 3, b: 4 }));
assert_eq!(codec.decode(&mut buf).unwrap(), None);
}
#[test]
fn decode_bad_magic_is_an_invalid_data_error() {
let mut codec = BinCodec::<Magic>::new();
let mut buf = BytesMut::from(&[0x00, 0x00, 0x07][..]); let err = codec.decode(&mut buf).unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
}
#[test]
fn default_constructs_a_codec() {
let _c: BinCodec<Msg> = BinCodec::default();
}
}