use super::put::*;
use core::mem::MaybeUninit;
type Mu8 = MaybeUninit<u8>;
pub struct UncheckedBufWriter<'a> {
buf: &'a mut [Mu8],
written: usize,
}
impl<'a> UncheckedBufWriter<'a> {
pub fn new(buf: &'a mut [Mu8]) -> Self {
Self { buf, written: 0 }
}
pub fn written(&self) -> usize {
self.written
}
#[inline]
unsafe fn reserve<'s>(&'s mut self, len: usize) -> &'s mut [Mu8]
where
'a: 's,
{
let extent = self.written + len;
debug_assert!(
extent <= self.buf.len(),
"UncheckedBufWriter: buffer overflow"
);
let start = self.written;
self.written = extent;
unsafe { self.buf.get_unchecked_mut(start..extent) }
}
}
impl PutUnchecked for UncheckedBufWriter<'_> {
#[inline]
unsafe fn put_bytes(&mut self, bytes: &[u8]) {
let buf = unsafe { self.reserve(bytes.len()) };
buf.write_copy_of_slice(bytes);
}
#[inline]
unsafe fn put_opaque_variable(&mut self, bytes: &[u8]) {
debug_assert!(bytes.len() <= u32::MAX as usize);
static PAD: [u8; 3] = [0; 3];
let len = bytes.len();
let rem = if len.is_multiple_of(4) {
0
} else {
4 - len % 4
};
let size = 4 + len + rem;
unsafe {
let buf = self.reserve(size);
buf.get_unchecked_mut(0..4)
.write_copy_of_slice(&(len as u32).to_be_bytes());
buf.get_unchecked_mut(4..4 + len).write_copy_of_slice(bytes);
buf.get_unchecked_mut(4 + len..size)
.write_copy_of_slice(PAD.get_unchecked(0..rem));
}
}
#[inline]
unsafe fn put_opaque_fixed(&mut self, bytes: &[u8]) {
debug_assert!(bytes.len() <= u32::MAX as usize);
static PAD: [u8; 3] = [0; 3];
let len = bytes.len();
let rem = if len.is_multiple_of(4) {
0
} else {
4 - len % 4
};
let size = len + rem;
unsafe {
let buf = self.reserve(size);
buf.get_unchecked_mut(0..len).write_copy_of_slice(bytes);
buf.get_unchecked_mut(len..size)
.write_copy_of_slice(PAD.get_unchecked(0..rem));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serialize::writer_unchecked::WriterUnchecked;
#[test]
fn ints_and_bool() {
let mut buf = [MaybeUninit::uninit(); 8];
let mut w = UncheckedBufWriter::new(&mut buf);
unsafe {
WriterUnchecked::<TXS>::write_u32(&mut w, 42);
WriterUnchecked::<TXS>::write_bool(&mut w, true);
}
let written = w.written();
assert_eq!(written, 8);
let bytes = unsafe { buf[..written].assume_init_ref() };
assert_eq!(bytes, [0, 0, 0, 42, 0, 0, 0, 1]);
}
#[test]
fn opaque_variable_pads() {
let mut buf = [MaybeUninit::uninit(); 8];
let mut w = UncheckedBufWriter::new(&mut buf);
unsafe { WriterUnchecked::<TXS>::write_opaque_variable(&mut w, b"hi") };
let written = w.written();
assert_eq!(written, 8);
let bytes = unsafe { buf[..written].assume_init_ref() };
assert_eq!(bytes, [0, 0, 0, 2, 0x68, 0x69, 0, 0]);
}
#[test]
fn opaque_fixed_pads() {
let mut buf = [MaybeUninit::uninit(); 4];
let mut w = UncheckedBufWriter::new(&mut buf);
unsafe { WriterUnchecked::<TXS>::write_opaque_fixed(&mut w, &[0xAA, 0xBB, 0xCC]) };
let written = w.written();
assert_eq!(written, 4);
let bytes = unsafe { buf[..written].assume_init_ref() };
assert_eq!(bytes, [0xAA, 0xBB, 0xCC, 0x00]);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "UncheckedBufWriter: buffer overflow")]
fn overflow_debug_asserts() {
let mut buf = [MaybeUninit::uninit(); 3];
let mut w = UncheckedBufWriter::new(&mut buf);
unsafe { WriterUnchecked::<TXS>::write_u32(&mut w, 1) };
}
}