#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("buffer full")]
BufferFull,
#[error("not enough data")]
NotEnoughData,
}
#[derive(Debug)]
pub struct Writer<'a> {
buf: &'a mut [u8],
len: usize,
}
pub struct Reserved<const N: usize> {
offset: usize,
}
impl<'a> Writer<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
Self { buf, len: 0 }
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn remaining(&mut self) -> &mut [u8] {
&mut self.buf[self.len..]
}
pub fn commit(&mut self, n: usize) {
debug_assert!(n <= self.remaining().len());
self.len += n;
}
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> {
let dest = self
.buf
.get_mut(self.len..self.len + bytes.len())
.ok_or(Error::BufferFull)?;
dest.copy_from_slice(bytes);
self.commit(bytes.len());
Ok(())
}
pub fn write_u8(&mut self, value: u8) -> Result<(), Error> {
self.write_bytes(&[value])
}
pub fn write_u16(&mut self, value: u16) -> Result<(), Error> {
self.write_bytes(&value.to_be_bytes())
}
pub fn write_u32(&mut self, value: u32) -> Result<(), Error> {
self.write_bytes(&value.to_be_bytes())
}
pub fn reserve<const N: usize>(&mut self) -> Result<Reserved<N>, Error> {
let end = self.len.checked_add(N).ok_or(Error::BufferFull)?;
if end > self.buf.len() {
return Err(Error::BufferFull);
}
let at = self.len;
self.len = end;
Ok(Reserved { offset: at })
}
pub fn apply<const N: usize>(
&mut self,
reserved: Reserved<N>,
value: [u8; N],
) -> Result<(), Error> {
let data = self.claim(reserved)?;
data.copy_from_slice(&value);
Ok(())
}
pub fn claim<const N: usize>(&mut self, reserved: Reserved<N>) -> Result<&mut [u8], Error> {
let at = reserved.offset;
Ok(&mut self.buf[at..at + N])
}
pub fn finish(self) -> (&'a mut [u8], &'a mut [u8]) {
self.buf.split_at_mut(self.len)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn writing_to_a_buffer() {
let mut buffer = [0; 8];
let mut writer = Writer::new(&mut buffer);
assert_eq!(writer.len(), 0);
assert!(writer.is_empty());
writer.write_u32(0xDEADBEEF).unwrap();
assert_eq!(writer.len(), 4);
assert_eq!(writer.remaining(), &[0; 4]);
let (written, remaining) = writer.finish();
assert_eq!(written, &[0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(remaining, &[0; 4]);
}
}