use {
crate::io::{WriteResult, Writer, slice::SliceMutUnchecked, write_size_limit},
core::mem::MaybeUninit,
std::io::{BufWriter, Cursor, Write},
};
#[derive(Debug)]
pub struct WriteAdapter<W: ?Sized>(W);
impl<W: Write> WriteAdapter<W> {
pub fn new(writer: W) -> Self {
Self(writer)
}
}
impl<W: Write + ?Sized> Writer for WriteAdapter<W> {
fn write(&mut self, src: &[u8]) -> WriteResult<()> {
Ok(self.0.write_all(src)?)
}
fn finish(&mut self) -> WriteResult<()> {
Ok(self.0.flush()?)
}
}
impl<W: Write + ?Sized> Writer for BufWriter<W> {
fn write(&mut self, src: &[u8]) -> WriteResult<()> {
Ok(self.write_all(src)?)
}
fn finish(&mut self) -> WriteResult<()> {
Ok(self.flush()?)
}
}
#[inline]
fn cursor_slice_as_trusted_for(
cursor: &mut Cursor<impl AsMut<[u8]>>,
n_bytes: usize,
) -> WriteResult<impl Writer> {
let Ok(pos) = usize::try_from(cursor.position()) else {
return Err(write_size_limit(usize::MAX));
};
let inner = cursor.get_mut().as_mut();
let next_pos = pos.saturating_add(n_bytes);
if next_pos > inner.len() {
return Err(write_size_limit(n_bytes));
}
cursor.set_position(next_pos as u64);
let slice = &mut cursor.get_mut().as_mut()[pos..next_pos];
Ok(unsafe { SliceMutUnchecked::new(slice) })
}
impl Writer for Cursor<&mut [u8]> {
#[inline]
fn write(&mut self, src: &[u8]) -> WriteResult<()> {
Ok(self.write_all(src)?)
}
#[inline]
fn finish(&mut self) -> WriteResult<()> {
Ok(self.flush()?)
}
#[inline]
unsafe fn as_trusted_for(&mut self, n_bytes: usize) -> WriteResult<impl Writer> {
cursor_slice_as_trusted_for(self, n_bytes)
}
}
impl<const N: usize> Writer for Cursor<[u8; N]> {
#[inline]
fn write(&mut self, src: &[u8]) -> WriteResult<()> {
Ok(self.write_all(src)?)
}
#[inline]
fn finish(&mut self) -> WriteResult<()> {
Ok(self.flush()?)
}
#[inline]
unsafe fn as_trusted_for(&mut self, n_bytes: usize) -> WriteResult<impl Writer> {
cursor_slice_as_trusted_for(self, n_bytes)
}
}
impl Writer for Cursor<Box<[u8]>> {
#[inline]
fn write(&mut self, src: &[u8]) -> WriteResult<()> {
Ok(self.write_all(src)?)
}
#[inline]
fn finish(&mut self) -> WriteResult<()> {
Ok(self.flush()?)
}
#[inline]
unsafe fn as_trusted_for(&mut self, n_bytes: usize) -> WriteResult<impl Writer> {
cursor_slice_as_trusted_for(self, n_bytes)
}
}
#[inline]
fn cursor_vec_as_trusted_for(
cursor: &mut Cursor<impl AsMut<Vec<u8>>>,
n_bytes: usize,
) -> WriteResult<impl Writer> {
let Ok(pos) = usize::try_from(cursor.position()) else {
return Err(write_size_limit(usize::MAX));
};
let Some(next_pos) = pos.checked_add(n_bytes) else {
return Err(write_size_limit(n_bytes));
};
let vec = cursor.get_mut().as_mut();
let cur_len = vec.len();
if next_pos > vec.capacity() {
#[expect(clippy::arithmetic_side_effects)]
vec.reserve(next_pos - cur_len);
}
if let Some(init_gap) = pos.checked_sub(cur_len) {
let spare = vec.spare_capacity_mut();
debug_assert!(spare.len() >= init_gap);
unsafe {
spare
.get_unchecked_mut(..init_gap)
.fill(MaybeUninit::new(0))
}
}
if next_pos > cur_len {
unsafe { vec.set_len(next_pos) }
}
cursor.set_position(next_pos as u64);
let slice = unsafe {
core::slice::from_raw_parts_mut(
cursor
.get_mut()
.as_mut()
.as_mut_ptr()
.cast::<MaybeUninit<u8>>()
.add(pos),
n_bytes,
)
};
Ok(unsafe { SliceMutUnchecked::new(slice) })
}
impl Writer for Cursor<Vec<u8>> {
#[inline]
fn write(&mut self, src: &[u8]) -> WriteResult<()> {
Ok(self.write_all(src)?)
}
#[inline]
fn finish(&mut self) -> WriteResult<()> {
Ok(self.flush()?)
}
#[inline]
unsafe fn as_trusted_for(&mut self, n_bytes: usize) -> WriteResult<impl Writer> {
cursor_vec_as_trusted_for(self, n_bytes)
}
}
impl Writer for Cursor<&mut Vec<u8>> {
#[inline]
fn write(&mut self, src: &[u8]) -> WriteResult<()> {
Ok(self.write_all(src)?)
}
#[inline]
fn finish(&mut self) -> WriteResult<()> {
Ok(self.flush()?)
}
#[inline]
unsafe fn as_trusted_for(&mut self, n_bytes: usize) -> WriteResult<impl Writer> {
cursor_vec_as_trusted_for(self, n_bytes)
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{
io::WriteError,
serde::{Serialize, serialize, serialized_size},
},
};
const MAGIC: u64 = 0xdeadbeef_cafebabe;
const DATA: &[(u32, bool, &u64)] = &[
(1u32, false, &MAGIC),
(2u32, true, &MAGIC),
(3u32, false, &MAGIC),
];
fn assert_serializes_data(mut writer: impl Writer) {
<[(u32, bool, &u64)]>::serialize_into(writer.by_ref(), DATA).unwrap();
writer.finish().unwrap();
}
#[test]
fn write_adapter_serialize_tuples() {
let mut buf = Vec::new();
assert_serializes_data(WriteAdapter::new(&mut buf));
assert_eq!(buf, serialize(DATA).unwrap());
}
#[test]
fn buf_writer_serialize_tuples() {
let mut buf = Vec::new();
assert_serializes_data(BufWriter::new(&mut buf));
assert_eq!(buf, serialize(DATA).unwrap());
}
#[test]
fn cursor_vec_writer_serialize_tuples() {
let mut buf = Cursor::new(Vec::new());
assert_serializes_data(&mut buf);
assert_eq!(buf.into_inner(), serialize(DATA).unwrap());
}
#[test]
fn cursor_slice_writer_serialize_tuples() {
let size = serialized_size(DATA).unwrap() as usize;
let mut buf = Cursor::new(vec![0; size].into_boxed_slice());
assert_serializes_data(&mut buf);
assert_eq!(buf.into_inner().as_ref(), serialize(DATA).unwrap());
}
fn write_trusted(writer: &mut impl Writer, bytes: &[u8]) {
let mut trusted = unsafe { writer.as_trusted_for(bytes.len()) }.unwrap();
trusted.write(bytes).unwrap();
trusted.finish().unwrap();
}
macro_rules! with_vec_cursors {
($inner:expr, |$reader: ident| $body: block) => {{
{
let mut $reader = Cursor::new($inner.clone());
$body
}
{
let mut $reader = Cursor::new(&mut $inner);
$body
}
}};
}
#[test]
fn cursor_vec_trusted_append_with_spare_capacity() {
let mut inner = Vec::with_capacity(8);
with_vec_cursors!(inner, |cursor| {
cursor.write_all(&[1, 2, 3]).unwrap();
write_trusted(&mut cursor, &[4, 5]);
cursor.finish().unwrap();
assert_eq!(&*cursor.into_inner(), &vec![1, 2, 3, 4, 5]);
});
}
#[test]
fn cursor_vec_trusted_overwrite_then_extend() {
let mut inner = vec![1, 2, 3, 4];
with_vec_cursors!(inner, |cursor| {
cursor.set_position(2);
write_trusted(&mut cursor, &[9, 8, 7, 6]);
cursor.finish().unwrap();
assert_eq!(&cursor.into_inner()[..], &vec![1, 2, 9, 8, 7, 6]);
});
}
#[test]
fn cursor_vec_trusted_overwrite_preserves_tail() {
let mut inner = vec![1, 2, 3, 4, 5, 6];
with_vec_cursors!(inner, |cursor| {
cursor.set_position(2);
write_trusted(&mut cursor, &[9, 8]);
cursor.finish().unwrap();
assert_eq!(&cursor.into_inner()[..], &vec![1, 2, 9, 8, 5, 6]);
});
}
#[test]
fn cursor_vec_trusted_zero_fills_gap() {
let mut inner = Vec::with_capacity(16);
inner.extend_from_slice(&[1, 2, 3]);
with_vec_cursors!(inner, |cursor| {
cursor.set_position(6);
write_trusted(&mut cursor, &[9, 10]);
cursor.finish().unwrap();
assert_eq!(&cursor.into_inner()[..], &vec![1, 2, 3, 0, 0, 0, 9, 10]);
});
}
macro_rules! with_slice_cursors {
($inner:expr, |$reader: ident| $body: block) => {{
{
let mut $reader = Cursor::new($inner);
$body
}
{
let mut inner = $inner;
let mut $reader = Cursor::new(&mut inner[..]);
$body
}
{
let mut $reader = Cursor::new(Box::from($inner));
$body
}
}};
}
#[test]
fn cursor_mut_slice_trusted_writes_in_bounds() {
with_slice_cursors!([1, 2, 3, 4, 5], |cursor| {
let pos = {
cursor.set_position(1);
write_trusted(&mut cursor, &[9, 8, 7]);
cursor.finish().unwrap();
cursor.position()
};
assert_eq!(&cursor.get_ref()[..], &[1, 9, 8, 7, 5]);
assert_eq!(pos, 4);
});
}
#[test]
fn cursor_slice_trusted_out_of_bounds_errors() {
with_slice_cursors!([1, 2, 3], |cursor| {
cursor.set_position(2);
let result = unsafe { cursor.as_trusted_for(2) };
assert!(matches!(result, Err(WriteError::WriteSizeLimit(2))));
});
}
}