use core::str;
pub const MAX_FIELD: usize = 16 * 1024 * 1024;
fn check_field_len(len: usize) -> Result<(), CodecError> {
if len > MAX_FIELD {
Err(CodecError::TooLong(len))
} else {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CodecError {
OutOfSpace,
Truncated,
BadUtf8,
TooLong(usize),
}
pub struct Writer<'a> {
buf: &'a mut [u8],
pos: usize,
}
impl<'a> Writer<'a> {
pub fn new(buf: &'a mut [u8]) -> Writer<'a> {
Writer { buf, pos: 0 }
}
pub fn position(&self) -> usize {
self.pos
}
fn raw(&mut self, bytes: &[u8]) -> Result<(), CodecError> {
let end = self.pos + bytes.len();
let slot = self
.buf
.get_mut(self.pos..end)
.ok_or(CodecError::OutOfSpace)?;
slot.copy_from_slice(bytes);
self.pos = end;
Ok(())
}
pub fn u8(&mut self, v: u8) -> Result<(), CodecError> {
self.raw(&[v])
}
pub fn u16(&mut self, v: u16) -> Result<(), CodecError> {
self.raw(&v.to_le_bytes())
}
pub fn u32(&mut self, v: u32) -> Result<(), CodecError> {
self.raw(&v.to_le_bytes())
}
pub fn u64(&mut self, v: u64) -> Result<(), CodecError> {
self.raw(&v.to_le_bytes())
}
pub fn bytes(&mut self, b: &[u8]) -> Result<(), CodecError> {
check_field_len(b.len())?;
self.u32(b.len() as u32)?;
self.raw(b)
}
pub fn str(&mut self, s: &str) -> Result<(), CodecError> {
self.bytes(s.as_bytes())
}
}
pub struct Reader<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> Reader<'a> {
pub fn new(buf: &'a [u8]) -> Reader<'a> {
Reader { buf, pos: 0 }
}
pub fn position(&self) -> usize {
self.pos
}
fn take(&mut self, n: usize) -> Result<&'a [u8], CodecError> {
let end = self.pos + n;
let slice = self.buf.get(self.pos..end).ok_or(CodecError::Truncated)?;
self.pos = end;
Ok(slice)
}
pub fn u8(&mut self) -> Result<u8, CodecError> {
Ok(self.take(1)?[0])
}
pub fn u16(&mut self) -> Result<u16, CodecError> {
let b = self.take(2)?;
Ok(u16::from_le_bytes([b[0], b[1]]))
}
pub fn u32(&mut self) -> Result<u32, CodecError> {
let b = self.take(4)?;
Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
}
pub fn u64(&mut self) -> Result<u64, CodecError> {
let b = self.take(8)?;
Ok(u64::from_le_bytes([
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
]))
}
pub fn bytes(&mut self) -> Result<&'a [u8], CodecError> {
let len = self.u32()? as usize;
check_field_len(len)?;
self.take(len)
}
pub fn str(&mut self) -> Result<&'a str, CodecError> {
str::from_utf8(self.bytes()?).map_err(|_| CodecError::BadUtf8)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn max_field_cap_and_its_guard_are_pinned() {
assert_eq!(MAX_FIELD, 16_777_216); assert_eq!(check_field_len(MAX_FIELD), Ok(())); assert_eq!(
check_field_len(MAX_FIELD + 1),
Err(CodecError::TooLong(MAX_FIELD + 1))
);
}
#[test]
fn writer_then_reader_round_trips_every_scalar_and_field_type() {
let mut buf = [0u8; 128];
let mut w = Writer::new(&mut buf);
w.u8(0xAB).unwrap();
w.u16(0x1234).unwrap();
w.u32(0xDEAD_BEEF).unwrap();
w.u64(0x0102_0304_0506_0708).unwrap();
w.bytes(&[1, 2, 3]).unwrap();
w.str("héllo").unwrap();
let n = w.position();
let mut r = Reader::new(&buf[..n]);
assert_eq!(r.u8().unwrap(), 0xAB);
assert_eq!(r.u16().unwrap(), 0x1234);
assert_eq!(r.u32().unwrap(), 0xDEAD_BEEF);
assert_eq!(r.u64().unwrap(), 0x0102_0304_0506_0708);
assert_eq!(r.bytes().unwrap(), &[1, 2, 3]);
assert_eq!(r.str().unwrap(), "héllo");
assert_eq!(r.position(), n); }
#[test]
fn writer_lays_down_the_exact_little_endian_bytes() {
let mut buf = [0u8; 16];
let mut w = Writer::new(&mut buf);
w.u16(0x0102).unwrap();
w.u32(0x0304_0506).unwrap();
assert_eq!(w.position(), 6);
assert_eq!(&buf[..2], &[0x02, 0x01]); assert_eq!(&buf[2..6], &[0x06, 0x05, 0x04, 0x03]); }
#[test]
fn bytes_field_is_length_prefixed() {
let mut buf = [0u8; 16];
let mut w = Writer::new(&mut buf);
w.bytes(&[0xAA, 0xBB]).unwrap();
let n = w.position(); assert_eq!(n, 6);
assert_eq!(&buf[..4], &2u32.to_le_bytes()); assert_eq!(&buf[4..6], &[0xAA, 0xBB]); }
#[test]
fn writer_rejects_a_field_that_does_not_fit_at_the_boundary() {
let mut four = [0u8; 4];
let mut w = Writer::new(&mut four);
w.u32(1).unwrap(); assert_eq!(w.u8(0), Err(CodecError::OutOfSpace)); let mut seven = [0u8; 7];
assert_eq!(Writer::new(&mut seven).u64(0), Err(CodecError::OutOfSpace));
let mut eight = [0u8; 8];
assert_eq!(Writer::new(&mut eight).u64(0), Ok(()));
}
#[test]
fn reader_rejects_a_truncated_field_at_the_boundary() {
assert_eq!(Reader::new(&[]).u8(), Err(CodecError::Truncated));
assert_eq!(Reader::new(&[0]).u16(), Err(CodecError::Truncated)); assert_eq!(Reader::new(&[0, 0, 0]).u32(), Err(CodecError::Truncated)); assert_eq!(Reader::new(&[0; 7]).u64(), Err(CodecError::Truncated)); let mut framed = [0u8; 6];
Writer::new(&mut framed).bytes(&[1, 2]).unwrap();
assert_eq!(
Reader::new(&framed[..5]).bytes(),
Err(CodecError::Truncated)
);
}
#[test]
fn reader_rejects_a_str_field_that_is_not_utf8() {
let mut buf = [0u8; 8];
let mut w = Writer::new(&mut buf);
w.bytes(&[0xFF]).unwrap(); let n = w.position();
assert_eq!(Reader::new(&buf[..n]).str(), Err(CodecError::BadUtf8));
}
#[test]
fn over_long_length_prefix_is_refused_before_addressing() {
let mut buf = [0u8; 8];
buf[..4].copy_from_slice(&((MAX_FIELD as u32) + 1).to_le_bytes());
assert_eq!(
Reader::new(&buf).bytes(),
Err(CodecError::TooLong(MAX_FIELD + 1))
);
}
#[test]
fn empty_bytes_and_str_round_trip() {
let mut buf = [0u8; 16];
let mut w = Writer::new(&mut buf);
w.bytes(&[]).unwrap();
w.str("").unwrap();
let n = w.position();
let mut r = Reader::new(&buf[..n]);
assert_eq!(r.bytes().unwrap(), &[] as &[u8]);
assert_eq!(r.str().unwrap(), "");
}
}