mod private {
pub trait Sealed {}
}
macro_rules! write {
($($(#[$m: meta])* $vis: vis fn $ident: ident => $ty: ty ;)*) => {
$(
$(#[$m])*
#[doc = concat!("Writes an [`", stringify!($ty), "`] to the underlying buffer.")]
$vis fn $ident(&mut self, val: $ty) {
self.buf.extend(&val.to_le_bytes());
}
)*
};
}
macro_rules! read {
($($(#[$m: meta])* $vis: vis fn $ident: ident => $ty: ty ;)*) => {
$(
$(#[$m])*
#[doc = concat!("Reads an [`", stringify!($ty), "`] at the current cursor position from the underlying view.")]
#[doc = concat!("After execution, the cursor will be incremented by `mem::size_of::<", stringify!($ty), ">()` bytes.")]
$vis fn $ident(&mut self) -> Option<$ty> {
let size = std::mem::size_of::<$ty>();
let at = self.cursor.checked_add(size)?;
if at > self.view.len() {
self.cursor = self.view.len();
return None;
}
let value = <$ty>::from_le_bytes(self.view[self.cursor..at].try_into().expect("byte size correct"));
self.cursor = at;
Some(value)
}
)*
};
}
macro_rules! primitives {
($($ty: ty as $w: ident, $r: ident;)*) => {
$(
impl private::Sealed for $ty {}
impl BytePrimitive for $ty {
fn write(self, buf: &mut ByteBuffer) {
buf.$w(self);
}
fn read(read: &mut ByteReader) -> Option<Self> {
read.$r()
}
}
impl<const N: usize> private::Sealed for [$ty; N] {}
impl<const N: usize> BytePrimitive for [$ty; N] {
fn write(self, buf: &mut ByteBuffer) {
for item in self {
buf.$w(item)
}
}
fn read(read: &mut ByteReader) -> Option<Self> {
let mut arr = [Default::default(); N];
for elem in &mut arr {
*elem = read.$r()?;
}
Some(arr)
}
}
)*
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ByteBuffer {
buf: Vec<u8>,
}
impl Default for ByteBuffer {
fn default() -> Self {
Self::new()
}
}
impl From<Vec<u8>> for ByteBuffer {
fn from(value: Vec<u8>) -> Self {
Self { buf: value }
}
}
impl From<ByteBuffer> for Vec<u8> {
fn from(value: ByteBuffer) -> Self {
value.buf
}
}
impl<const S: usize> From<[u8; S]> for ByteBuffer {
fn from(value: [u8; S]) -> Self {
Self { buf: value.into() }
}
}
impl ByteBuffer {
pub fn new() -> Self {
Self {
buf: Vec::new(),
}
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn read<'a>(&'a self) -> ByteReader<'a> {
ByteReader::new(&self.buf)
}
write! {
pub fn write_i8 => i8;
pub fn write_i16 => i16;
pub fn write_i32 => i32;
pub fn write_i64 => i64;
pub fn write_isize => isize;
pub fn write_f32 => f32;
pub fn write_f64 => f64;
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ByteReader<'a> {
view: &'a [u8],
cursor: usize,
}
impl<'a> ByteReader<'a> {
pub fn new(view: &'a [u8]) -> Self {
Self {
view,
cursor: 0,
}
}
pub fn cursor(&self) -> usize {
self.cursor
}
pub fn cursor_mut(&mut self) -> &mut usize {
&mut self.cursor
}
read! {
pub fn read_i8 => i8;
pub fn read_i16 => i16;
pub fn read_i32 => i32;
pub fn read_i64 => i64;
pub fn read_isize => isize;
pub fn read_f32 => f32;
pub fn read_f64 => f64;
}
}
pub trait BytePrimitive: private::Sealed {
fn write(self, buf: &mut ByteBuffer);
fn read(read: &mut ByteReader) -> Option<Self> where Self: Sized;
}
primitives! {
i8 as write_i8, read_i8;
i16 as write_i16, read_i16;
i32 as write_i32, read_i32;
i64 as write_i64, read_i64;
isize as write_isize, read_isize;
f32 as write_f32, read_f32;
f64 as write_f64, read_f64;
}
#[cfg(test)]
mod tests {
use super::ByteBuffer;
#[test]
fn test_read_write() {
let mut buf = ByteBuffer::new();
buf.write_i8(28);
buf.write_f32(-13.72);
buf.write_i64(173_283_012_736);
assert_eq!(buf.len(), 13);
let mut read = buf.read();
assert_eq!(read.read_i8(), Some(28));
assert_eq!(read.read_f32(), Some(-13.72));
assert_eq!(read.read_i64(), Some(173_283_012_736));
assert_eq!(read.cursor(), buf.len());
assert_eq!(read.read_i8(), None);
}
}