#![warn(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
use core::cell::Cell;
use super::errors::TBytesError;
#[derive(Clone, Debug)]
pub struct TBytesWriter<B: TBytesWriterBackend> {
backend: B,
}
impl<B: TBytesWriterBackend> TBytesWriter<B> {
pub fn new(backend: B) -> Self {
Self { backend }
}
}
pub trait TBytesWriterFor<T> {
fn write(&mut self, value: T) -> Result<usize, TBytesError>;
fn write_slice(&mut self, values: &[T]) -> Result<usize, TBytesError>;
fn write_array<const N: usize>(&mut self, values: [T; N]) -> Result<usize, TBytesError> {
self.write_slice(values.as_slice())
}
}
macro_rules! t_bytes_writer_for {
($type_: ident) => {
impl<B: TBytesWriterBackend> TBytesWriterFor<$type_> for TBytesWriter<B> {
fn write(&mut self, value: $type_) -> Result<usize, TBytesError> {
const SIZE: usize = core::mem::size_of::<$type_>();
let bytes = value.to_ne_bytes();
self.backend.write(&bytes)?;
Ok(SIZE)
}
fn write_slice(&mut self, values: &[$type_]) -> Result<usize, TBytesError> {
let mut num_bytes: usize = 0;
for val in values {
let bytes = val.to_ne_bytes();
num_bytes += self.backend.write(&bytes)?;
}
Ok(num_bytes)
}
}
};
}
t_bytes_writer_for!(u8);
t_bytes_writer_for!(u16);
t_bytes_writer_for!(u32);
t_bytes_writer_for!(u64);
t_bytes_writer_for!(u128);
t_bytes_writer_for!(i8);
t_bytes_writer_for!(i16);
t_bytes_writer_for!(i32);
t_bytes_writer_for!(i64);
t_bytes_writer_for!(i128);
t_bytes_writer_for!(f32);
t_bytes_writer_for!(f64);
pub trait TBytesWriterBackend {
fn write(&mut self, bytes: &[u8]) -> Result<usize, TBytesError>;
}
#[derive(Debug)]
pub struct TBytesWriterSliceBackend<'a> {
buffer: &'a mut [u8],
pos: Cell<usize>,
}
impl<'a> TBytesWriterSliceBackend<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
Self {
buffer,
pos: Default::default(),
}
}
pub fn pos(&self) -> usize {
self.pos.get()
}
}
impl<'a> TBytesWriterBackend for TBytesWriterSliceBackend<'a> {
fn write(&mut self, bytes: &[u8]) -> Result<usize, TBytesError> {
let num_bytes = bytes.len();
let pos = self.pos.get();
if self.buffer.len() < pos + num_bytes {
return Err(TBytesError::OutOfBounds);
}
let frame = &mut self.buffer[pos..pos + num_bytes];
frame.copy_from_slice(bytes);
self.pos.replace(pos + num_bytes);
Ok(num_bytes)
}
}
impl<'a> From<&'a mut [u8]> for TBytesWriter<TBytesWriterSliceBackend<'a>> {
fn from(value: &'a mut [u8]) -> Self {
TBytesWriter::new(TBytesWriterSliceBackend::new(value))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slice_backend() {
let mut buffer = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9u8];
let mut backend = TBytesWriterSliceBackend::new(&mut buffer);
let num_bytes = backend.write(&[9, 8, 7, 6, 5, 4, 3, 2, 1, 0u8]).unwrap();
assert_eq!(num_bytes, 10);
let err = backend.write(&[0u8]);
assert!(err.is_err());
assert!(matches!(err, Err(TBytesError::OutOfBounds)));
assert_eq!(&buffer, &[9, 8, 7, 6, 5, 4, 3, 2, 1, 0u8]);
}
#[test]
fn slice_backend_out_of_bouts_is_idempotent() {
let mut buffer = [0; 10];
let mut backend = TBytesWriterSliceBackend::new(&mut buffer);
let num_bytes = backend.write(&[1; 10]).unwrap();
assert_eq!(num_bytes, 10);
let err = backend.write(&[0]);
assert!(matches!(err, Err(TBytesError::OutOfBounds)));
let err = backend.write(&[0]);
assert!(matches!(err, Err(TBytesError::OutOfBounds)));
assert_eq!(backend.pos(), buffer.len());
assert_eq!(&buffer, &[1; 10]);
}
#[test]
fn from_mut_slice() {
let mut buffer = [0; 2];
let mut writer = TBytesWriter::from(buffer.as_mut_slice());
writer.write(511u16).unwrap();
assert_eq!(&buffer, &[255, 1]);
}
#[test]
fn bytes_writer_for() {
let mut buffer = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9u8];
let backend = TBytesWriterSliceBackend::new(&mut buffer);
let mut writer = TBytesWriter::new(backend);
let num_bytes = writer.write(0u64).unwrap();
assert_eq!(num_bytes, 8);
let num_bytes = writer.write_slice(&[1, 2u8]).unwrap();
assert_eq!(num_bytes, 2);
let err = writer.write(0u8);
assert!(err.is_err());
assert!(matches!(err, Err(TBytesError::OutOfBounds)));
assert_eq!(&buffer, &[0, 0, 0, 0, 0, 0, 0, 0, 1, 2u8]);
}
#[test]
fn write_array() {
let mut buffer = [0; 4];
let mut writer = TBytesWriter::from(buffer.as_mut_slice());
writer.write_array([1u8; 4]).unwrap();
assert_eq!(&buffer, &[1; 4]);
}
}