//! Minimal little-endian binary encoding for commands, snapshots and //! recordings. use std::fmt; /// Error returned when bytes cannot be decoded. #[derive(Clone, Debug, PartialEq, Eq)] pub enum DecodeError { /// The input ended before the value was complete. UnexpectedEnd, /// The input contained a value that is not allowed at this position. InvalidValue(&'static str), /// The input contained bytes after the last expected value. TrailingBytes, } impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DecodeError::UnexpectedEnd => write!(f, "unexpected end of input"), DecodeError::InvalidValue(what) => write!(f, "invalid value: {what}"), DecodeError::TrailingBytes => write!(f, "trailing bytes after the last value"), } } } impl std::error::Error for DecodeError {} /// Appends values to a growing byte buffer. #[derive(Debug, Default)] pub struct Writer { buf: Vec, } impl Writer { pub fn new() -> Self { Self::default() } pub fn u8(&mut self, value: u8) { self.buf.push(value); } pub fn u16(&mut self, value: u16) { self.buf.extend_from_slice(&value.to_le_bytes()); } pub fn u32(&mut self, value: u32) { self.buf.extend_from_slice(&value.to_le_bytes()); } pub fn u64(&mut self, value: u64) { self.buf.extend_from_slice(&value.to_le_bytes()); } pub fn i32(&mut self, value: i32) { self.buf.extend_from_slice(&value.to_le_bytes()); } pub fn bool(&mut self, value: bool) { self.buf.push(u8::from(value)); } /// Writes bytes as they are, without a length. pub fn raw(&mut self, value: &[u8]) { self.buf.extend_from_slice(value); } /// Writes a `u32` length followed by the bytes. pub fn bytes(&mut self, value: &[u8]) { let len = u32::try_from(value.len()).expect("length fits in u32"); self.u32(len); self.buf.extend_from_slice(value); } /// Writes a string as UTF-8 with a `u32` length in front. pub fn str(&mut self, value: &str) { self.bytes(value.as_bytes()); } pub fn into_bytes(self) -> Vec { self.buf } } /// Reads values from a byte slice in the order they were written. #[derive(Debug)] pub struct Reader<'a> { buf: &'a [u8], pos: usize, } impl<'a> Reader<'a> { pub fn new(buf: &'a [u8]) -> Self { Self { buf, pos: 0 } } /// Number of bytes that have not been read yet. pub fn remaining(&self) -> usize { self.buf.len() - self.pos } /// Reads exactly `len` bytes. pub fn raw(&mut self, len: usize) -> Result<&'a [u8], DecodeError> { if len > self.remaining() { return Err(DecodeError::UnexpectedEnd); } let slice = &self.buf[self.pos..self.pos + len]; self.pos += len; Ok(slice) } fn array(&mut self) -> Result<[u8; N], DecodeError> { let mut out = [0u8; N]; out.copy_from_slice(self.raw(N)?); Ok(out) } pub fn u8(&mut self) -> Result { Ok(self.array::<1>()?[0]) } pub fn u16(&mut self) -> Result { Ok(u16::from_le_bytes(self.array()?)) } pub fn u32(&mut self) -> Result { Ok(u32::from_le_bytes(self.array()?)) } pub fn u64(&mut self) -> Result { Ok(u64::from_le_bytes(self.array()?)) } pub fn i32(&mut self) -> Result { Ok(i32::from_le_bytes(self.array()?)) } pub fn bool(&mut self) -> Result { match self.u8()? { 0 => Ok(false), 1 => Ok(true), _ => Err(DecodeError::InvalidValue("bool")), } } /// Reads a `u32` length followed by that many bytes. pub fn bytes(&mut self) -> Result<&'a [u8], DecodeError> { let len = self.u32()? as usize; self.raw(len) } /// Reads a UTF-8 string with a `u32` length in front. pub fn string(&mut self) -> Result { let bytes = self.bytes()?; std::str::from_utf8(bytes) .map(str::to_owned) .map_err(|_| DecodeError::InvalidValue("utf-8 string")) } /// Succeeds only if every byte has been read. pub fn finish(self) -> Result<(), DecodeError> { if self.remaining() == 0 { Ok(()) } else { Err(DecodeError::TrailingBytes) } } }