src/slice.rs
use crate::scalar::{ReadScalar, ScalarError, WriteScalar};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SliceError {
Size(ScalarError),
BufferTooSmall,
Utf8,
}
pub trait WriteSlice: Sized {
fn write_slice(&self, buf: &mut &mut [u8]) -> Result<(), SliceError> {
self.write_slice_len(buf)?;
self.write_slice_value(buf)
}
fn write_slice_len(&self, buf: &mut &mut [u8]) -> Result<(), SliceError>;
fn write_slice_value(&self, buf: &mut &mut [u8]) -> Result<(), SliceError>;
}
pub trait ReadSlice<'a>: Sized {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, SliceError> {
let len = Self::read_slice_len(buf)?;
Self::read_slice_value(buf, len)
}
fn read_slice_len(buf: &mut &'a [u8]) -> Result<usize, SliceError>;
fn read_slice_value(buf: &mut &'a [u8], len: usize) -> Result<Self, SliceError>;
}
fn write_and_advance(buf: &mut &mut [u8], payload: &[u8]) -> Result<(), SliceError> {
if payload.len() > buf.len() {
return Err(SliceError::BufferTooSmall);
}
let (to_edit, remain) = core::mem::take(buf).split_at_mut(payload.len());
to_edit.copy_from_slice(payload);
*buf = remain;
Ok(())
}
fn read_and_advance<'a>(buf: &mut &'a [u8], len: usize) -> Result<&'a [u8], SliceError> {
if len > buf.len() {
return Err(SliceError::BufferTooSmall);
}
let (ret, remain) = buf.split_at(len);
*buf = remain;
Ok(ret)
}
impl WriteSlice for &[u8] {
fn write_slice_len(&self, buf: &mut &mut [u8]) -> Result<(), SliceError> {
self.len().write_scalar(buf).map_err(SliceError::Size)
}
fn write_slice_value(&self, buf: &mut &mut [u8]) -> Result<(), SliceError> {
write_and_advance(buf, self)
}
}
impl<'a> ReadSlice<'a> for &'a [u8] {
fn read_slice_len(buf: &mut &'a [u8]) -> Result<usize, SliceError> {
usize::read_scalar(buf).map_err(SliceError::Size)
}
fn read_slice_value(buf: &mut &'a [u8], len: usize) -> Result<Self, SliceError> {
read_and_advance(buf, len)
}
}
impl WriteSlice for &str {
fn write_slice_len(&self, buf: &mut &mut [u8]) -> Result<(), SliceError> {
self.as_bytes().write_slice_len(buf)
}
fn write_slice_value(&self, buf: &mut &mut [u8]) -> Result<(), SliceError> {
self.as_bytes().write_slice_value(buf)
}
}
impl<'a> ReadSlice<'a> for &'a str {
fn read_slice_len(buf: &mut &'a [u8]) -> Result<usize, SliceError> {
<&[u8]>::read_slice_len(buf)
}
fn read_slice_value(buf: &mut &'a [u8], len: usize) -> Result<Self, SliceError> {
core::str::from_utf8(<&[u8]>::read_slice_value(buf, len)?).map_err(|_| SliceError::Utf8)
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_slice_roundtrip {
($($name:ident: $ty:ty = $value:expr),+ $(,)?) => {
$(
#[test]
fn $name() {
let mut buffer = [0u8; 32];
let mut buf = &mut buffer[..];
let value: $ty = $value;
value.write_slice(&mut buf).unwrap();
let mut read_buf = &buffer[..];
let result = <$ty>::read_slice(&mut read_buf).unwrap();
assert_eq!(result, value);
}
)+
};
}
test_slice_roundtrip! {
test_slice: &[u8] = &[10, 20, 30, 40, 50],
test_slice_empty: &[u8] = &[],
test_str: &str = "Hello, World!",
test_str_empty: &str = "",
test_str_unicode: &str = "Hëllö 世界 🦀",
}
#[test]
fn test_str_invalid_utf8() {
let mut buffer = [0u8; 20];
buffer[0..8].copy_from_slice(&3usize.to_be_bytes());
buffer[9] = 0xFF;
buffer[10] = 0xFF;
buffer[11] = 0xFF;
let mut read_buf = &buffer[..];
let result = <&str>::read_slice(&mut read_buf);
assert!(matches!(result, Err(SliceError::Utf8)));
}
}