use thiserror::Error;
use tor_bytes::Writer;
#[non_exhaustive]
#[derive(Clone, Debug, Error)]
pub(crate) enum SliceWriterError {
#[error("Tried to write more than would fit into a fixed-size slice.")]
Truncated,
}
pub(crate) struct SliceWriter<T> {
data: T,
offset: usize,
}
impl<T> Writer for SliceWriter<T>
where
T: AsMut<[u8]>,
{
fn write_all(&mut self, b: &[u8]) {
let new_offset = self.offset.saturating_add(b.len());
if new_offset <= self.data.as_mut().len() {
self.data.as_mut()[self.offset..new_offset].copy_from_slice(b);
self.offset = new_offset;
} else {
self.offset = usize::MAX;
}
}
}
impl<T> SliceWriter<T>
where
T: AsMut<[u8]>,
{
pub(crate) fn advance(&mut self, n: usize) {
let new_offset = self.offset.saturating_add(n);
if new_offset <= self.data.as_mut().len() {
self.offset = new_offset;
} else {
self.offset = usize::MAX;
}
}
}
impl<T> SliceWriter<T> {
pub(crate) fn new(data: T) -> Self {
Self { data, offset: 0 }
}
pub(crate) fn try_unwrap(self) -> Result<(T, usize), SliceWriterError> {
let offset = self.offset()?;
Ok((self.data, offset))
}
pub(crate) fn offset(&self) -> Result<usize, SliceWriterError> {
if self.offset != usize::MAX {
Ok(self.offset)
} else {
Err(SliceWriterError::Truncated)
}
}
#[inline]
pub(crate) fn assert_offset_is(&self, n: usize) {
debug_assert_eq!(self.offset, n);
}
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
#[test]
fn basics() {
let mut w = SliceWriter::new([0_u8; 16]);
w.write_u8(b'h');
w.write_u16(0x656c);
w.write_u32(0x6c6f2077);
w.write_all(b"orld!");
let (a, len) = w.try_unwrap().unwrap();
assert_eq!(a.as_ref(), b"hello world!\0\0\0\0");
assert_eq!(len, 12);
}
#[test]
fn full_is_ok() {
let mut w = SliceWriter::new([0_u8; 4]);
w.write_u8(1);
w.write_u16(0x0203);
w.write_u8(4);
let (a, len) = w.try_unwrap().unwrap();
assert_eq!(a.as_ref(), [1, 2, 3, 4]);
assert_eq!(len, 4);
}
#[test]
fn too_full_is_not_ok() {
let mut w = SliceWriter::new([0_u8; 5]);
w.write_u32(12);
w.write_u32(12);
assert!(matches!(w.try_unwrap(), Err(SliceWriterError::Truncated)));
}
}