macro_rules! impl_rw_primitive_exts {
(
endian: $($endian_ty:ident),+ $(,)?;
raw: $($raw_ty:ident),+ $(,)?;
) => {
pub trait ReadPrimitive{
$(
pastey::paste! {
#[doc = concat!("Reads a `", stringify!($endian_ty), "` using the reader's configured endianness.")]
fn [<read_ $endian_ty>](&mut self) -> Result<$endian_ty, crate::Error>;
}
)+
$(
pastey::paste! {
#[doc = concat!("Reads a raw `", stringify!($raw_ty), "` byte value (endianness is not applied).")]
fn [<read_ $raw_ty>](&mut self) -> Result<$raw_ty, crate::Error>;
}
)+
fn read_bool_byte(&mut self) -> Result<bool, crate::Error>;
}
pub trait WritePrimitive {
$(
pastey::paste! {
#[doc = concat!("Writes a `", stringify!($endian_ty), "` using the writer's configured endianness.")]
fn [<write_ $endian_ty>](&mut self, value: $endian_ty) -> Result<(), crate::Error>;
}
)+
$(
pastey::paste! {
#[doc = concat!("Writes a raw `", stringify!($raw_ty), "` byte value (endianness is not applied).")]
fn [<write_ $raw_ty>](&mut self, value: $raw_ty) -> Result<(), crate::Error>;
}
)+
fn write_bool_byte(&mut self, value: bool) -> Result<(), crate::Error>;
}
impl<'a, Ctx> crate::ReadPrimitive for crate::Reader<'a, Ctx> {
$(
pastey::paste! {
#[inline]
fn [<read_ $endian_ty>](&mut self) -> Result<$endian_ty, crate::Error> {
let raw = self.read_bytes::<{ core::mem::size_of::<$endian_ty>() }>()?;
let result = match self.endian() {
crate::Endian::Native => <$endian_ty>::from_ne_bytes(raw),
crate::Endian::Little => <$endian_ty>::from_le_bytes(raw),
crate::Endian::Big => <$endian_ty>::from_be_bytes(raw),
};
Ok(result)
}
}
)+
$(
pastey::paste! {
#[inline]
fn [<read_ $raw_ty>](&mut self) -> Result<$raw_ty, crate::Error> {
let raw = self.read_bytes::<1>()?;
Ok(raw[0] as $raw_ty)
}
}
)+
#[inline]
fn read_bool_byte(&mut self) -> Result<bool, crate::Error> {
let raw = self.read_bytes::<1>()?;
Ok(raw[0] != 0)
}
}
impl<Ctx> crate::WritePrimitive for crate::Writer<Ctx> {
$(
pastey::paste! {
#[inline]
fn [<write_ $endian_ty>](&mut self, value: $endian_ty) -> Result<(), crate::Error> {
let bytes = match self.endian() {
crate::Endian::Native => value.to_ne_bytes(),
crate::Endian::Little => value.to_le_bytes(),
crate::Endian::Big => value.to_be_bytes(),
};
self.write_bytes(&bytes)
}
}
)+
$(
pastey::paste! {
#[inline]
fn [<write_ $raw_ty>](&mut self, value: $raw_ty) -> Result<(), crate::Error> {
self.write_bytes(&[value as u8])
}
}
)+
#[inline]
fn write_bool_byte(&mut self, value: bool) -> Result<(), crate::Error> {
self.write_bytes(&[if value { 1 } else { 0 }])
}
}
};
}
impl_rw_primitive_exts!(
endian: u16, u32, u64, u128, usize, i16, i32, i64, i128, isize, f32, f64;
raw: u8, i8;
);
#[cfg(test)]
mod tests {
use alloc::vec;
use crate::{Endian, Error, ReadPrimitive, Reader, WritePrimitive, Writer};
#[test]
fn writes_endian_primitives_with_configured_byte_order() {
let mut le = Writer::new(Endian::Little, ());
le.write_u16(0x1234).unwrap();
assert_eq!(le.finish(), vec![0x34, 0x12]);
let mut be = Writer::new(Endian::Big, ());
be.write_u16(0x1234).unwrap();
assert_eq!(be.finish(), vec![0x12, 0x34]);
let mut native = Writer::new(Endian::Native, ());
native.write_u32(0x0102_0304).unwrap();
assert_eq!(native.finish(), 0x0102_0304u32.to_ne_bytes().to_vec());
}
#[test]
fn reads_endian_primitives_with_configured_byte_order() {
let mut le = Reader::new(&[0x34, 0x12], Endian::Little, ());
assert_eq!(le.read_u16().unwrap(), 0x1234);
let mut be = Reader::new(&[0x12, 0x34], Endian::Big, ());
assert_eq!(be.read_u16().unwrap(), 0x1234);
let native_bytes = 0x1122_3344u32.to_ne_bytes();
let mut native = Reader::new(&native_bytes, Endian::Native, ());
assert_eq!(native.read_u32().unwrap(), 0x1122_3344);
}
#[test]
fn raw_byte_and_bool_methods_work() {
let mut writer = Writer::new(Endian::Little, ());
writer.write_u8(255).unwrap();
writer.write_i8(-1).unwrap();
writer.write_bool_byte(false).unwrap();
writer.write_bool_byte(true).unwrap();
let bytes = writer.finish();
assert_eq!(bytes, vec![255, 255, 0, 1]);
let mut reader = Reader::new(&bytes, Endian::Little, ());
assert_eq!(reader.read_u8().unwrap(), 255);
assert_eq!(reader.read_i8().unwrap(), -1);
assert!(!reader.read_bool_byte().unwrap());
assert!(reader.read_bool_byte().unwrap());
}
#[test]
fn roundtrip_multiple_primitive_types() {
let mut writer = Writer::new(Endian::Big, ());
writer.write_u32(0xDEAD_BEEF).unwrap();
writer.write_i16(-1234).unwrap();
writer.write_f32(3.5).unwrap();
writer.write_f64(-1.25).unwrap();
let bytes = writer.finish();
let mut reader = Reader::new(&bytes, Endian::Big, ());
assert_eq!(reader.read_u32().unwrap(), 0xDEAD_BEEF);
assert_eq!(reader.read_i16().unwrap(), -1234);
assert_eq!(reader.read_f32().unwrap(), 3.5);
assert_eq!(reader.read_f64().unwrap(), -1.25);
}
#[test]
fn read_errors_when_not_enough_bytes() {
let mut reader = Reader::new(&[0xAA, 0xBB, 0xCC], Endian::Little, ());
assert!(matches!(reader.read_u32(), Err(Error::OutOfBounds)));
}
}