Skip to main content

wincode/io/
mod.rs

1//! [`Reader`] and [`Writer`] implementations.
2use {
3    core::{
4        mem::{self, transmute, MaybeUninit},
5        ptr,
6        slice::from_raw_parts,
7    },
8    thiserror::Error,
9};
10
11#[derive(Error, Debug)]
12pub enum ReadError {
13    #[error("Attempting to read {0} bytes")]
14    ReadSizeLimit(usize),
15    #[error(
16        "Unsupported zero-copy operation: reader does not support deserializing zero-copy types"
17    )]
18    UnsupportedZeroCopy,
19    #[cfg(feature = "std")]
20    #[error(transparent)]
21    Io(#[from] std::io::Error),
22}
23
24pub type ReadResult<T> = core::result::Result<T, ReadError>;
25
26#[cold]
27const fn read_size_limit(len: usize) -> ReadError {
28    ReadError::ReadSizeLimit(len)
29}
30
31/// Trait for structured reading of bytes from a source into potentially uninitialized memory.
32///
33/// # Advancement semantics
34/// - `fill_*` methods never advance.
35/// - `copy_into_*` and `borrow_*` methods advance by the number of bytes read.
36/// - [`Reader::as_trusted_for`] advances the parent by the number of bytes requested.
37///
38/// # Zero-copy semantics
39/// Only implement [`Reader::borrow_exact`] for sources where stable borrows into the backing storage are possible.
40/// Callers should prefer [`Reader::fill_exact`] to remain compatible with readers that don’t support zero-copy.
41/// Returns [`ReadError::UnsupportedZeroCopy`] for readers that do not support zero-copy.
42pub trait Reader<'a> {
43    /// A variant of the [`Reader`] that can elide bounds checking within a given window.
44    ///
45    /// Trusted variants of the [`Reader`] should generally not be constructed directly,
46    /// but rather by calling [`Reader::as_trusted_for`] on a trusted [`Reader`].
47    /// This will ensure that the safety invariants are upheld.
48    type Trusted<'b>: Reader<'a>
49    where
50        Self: 'b;
51
52    /// Return up to `n_bytes` from the internal buffer without advancing. Implementations may
53    /// read more data internally to satisfy future requests. Returns fewer than `n_bytes` at EOF.
54    ///
55    /// This is _not_ required to return exactly `n_bytes`, it is required to return _up to_ `n_bytes`.
56    /// Use [`Reader::fill_exact`] if you need exactly `n_bytes`.
57    fn fill_buf(&mut self, n_bytes: usize) -> ReadResult<&[u8]>;
58
59    /// Return exactly `n_bytes` without advancing.
60    ///
61    /// Errors if the source cannot provide enough bytes.
62    fn fill_exact(&mut self, n_bytes: usize) -> ReadResult<&[u8]> {
63        let src = self.fill_buf(n_bytes)?;
64        if src.len() != n_bytes {
65            return Err(read_size_limit(n_bytes));
66        }
67        Ok(src)
68    }
69
70    /// Return exactly `N` bytes as `&[u8; N]` without advancing.
71    ///
72    /// Errors if fewer than `N` bytes are available.
73    fn fill_array<const N: usize>(&mut self) -> ReadResult<&[u8; N]> {
74        let src = self.fill_exact(N)?;
75        // SAFETY:
76        // - `fill_exact` ensures we read N bytes.
77        Ok(unsafe { &*src.as_ptr().cast::<[u8; N]>() })
78    }
79
80    /// Zero-copy: return a borrowed slice of exactly `len` bytes and advance by `len`.
81    ///
82    /// The returned slice is tied to `'a`. Prefer [`Reader::fill_exact`] unless you truly need zero-copy.
83    /// Errors for readers that don't support zero-copy.
84    #[inline]
85    fn borrow_exact(&mut self, len: usize) -> ReadResult<&'a [u8]> {
86        Self::borrow_exact_mut(self, len).map(|s| &*s)
87    }
88
89    /// Zero-copy: return a borrowed mutable slice of exactly `len` bytes and advance by `len`.
90    ///
91    /// Errors for readers that don't support zero-copy.
92    #[expect(unused_variables)]
93    fn borrow_exact_mut(&mut self, len: usize) -> ReadResult<&'a mut [u8]> {
94        Err(ReadError::UnsupportedZeroCopy)
95    }
96
97    /// Advance by exactly `amt` bytes without bounds checks.
98    ///
99    /// May panic if fewer than `amt` bytes remain.
100    ///
101    /// # Safety
102    ///
103    /// - `amt` must be less than or equal to the number of bytes remaining in the reader.
104    unsafe fn consume_unchecked(&mut self, amt: usize);
105
106    /// Advance the reader exactly `amt` bytes, returning an error if the source does not have enough bytes.
107    fn consume(&mut self, amt: usize) -> ReadResult<()>;
108
109    /// Advance the parent by `n_bytes` and return a [`Reader`] that can elide bounds checks within
110    /// that `n_bytes` window.
111    ///
112    /// Implementors must:
113    /// - Ensure that either at least `n_bytes` bytes are available backing the
114    ///   returned reader, or return an error.
115    /// - Arrange that the returned `Trusted` reader's methods operate within
116    ///   that `n_bytes` window (it may buffer or prefetch arbitrarily).
117    ///
118    /// Note:
119    /// - `as_trusted_for` is intended for callers that know they will operate
120    ///   within a fixed-size window and want to avoid intermediate bounds checks.
121    /// - If you simply want to advance the parent by `n_bytes` without using
122    ///   a trusted window, prefer `consume(n_bytes)` instead.
123    ///
124    /// # Safety
125    ///
126    /// The caller must ensure that, through the returned reader, they do not
127    /// cause more than `n_bytes` bytes to be logically read or consumed
128    /// without performing additional bounds checks.
129    ///
130    /// Concretely:
131    /// - The total number of bytes accessed/consumed via the `Trusted` reader
132    ///   (`fill_*`, `copy_into_*`, `consume`, etc.) must be **<= `n_bytes`**.
133    ///
134    /// Violating this is undefined behavior, because `Trusted` readers are
135    /// permitted to elide bounds checks within the `n_bytes` window; reading past the
136    /// `n_bytes` window may read past the end of the underlying buffer.
137    unsafe fn as_trusted_for(&mut self, n_bytes: usize) -> ReadResult<Self::Trusted<'_>>;
138
139    /// Return a reference to the next byte without advancing.
140    ///
141    /// May buffer more bytes if necessary. Errors if no bytes remain.
142    #[inline]
143    fn peek(&mut self) -> ReadResult<&u8> {
144        self.fill_buf(1)?.first().ok_or_else(|| read_size_limit(1))
145    }
146
147    /// Copy and consume exactly `dst.len()` bytes from the [`Reader`] into `dst`.
148    ///
149    /// # Safety
150    ///
151    /// - `dst` must not overlap with the internal buffer.
152    #[inline]
153    fn copy_into_slice(&mut self, dst: &mut [MaybeUninit<u8>]) -> ReadResult<()> {
154        let src = self.fill_exact(dst.len())?;
155        // SAFETY:
156        // - `fill_exact` must do the appropriate bounds checking.
157        unsafe {
158            ptr::copy_nonoverlapping(src.as_ptr().cast(), dst.as_mut_ptr(), dst.len());
159            self.consume_unchecked(dst.len());
160        }
161        Ok(())
162    }
163
164    /// Copy and consume exactly `N` bytes from the [`Reader`] into `dst`.
165    ///
166    /// # Safety
167    ///
168    /// - `dst` must not overlap with the internal buffer.
169    #[inline]
170    fn copy_into_array<const N: usize>(
171        &mut self,
172        dst: &mut MaybeUninit<[u8; N]>,
173    ) -> ReadResult<()> {
174        let src = self.fill_array::<N>()?;
175        // SAFETY:
176        // - `fill_array` must do the appropriate bounds checking.
177        unsafe {
178            ptr::copy_nonoverlapping(src, dst.as_mut_ptr(), 1);
179            self.consume_unchecked(N);
180        }
181        Ok(())
182    }
183
184    /// Copy and consume exactly `size_of::<T>()` bytes from the [`Reader`] into `dst`.
185    ///
186    /// # Safety
187    ///
188    /// - `T` must be initialized by reads of `size_of::<T>()` bytes.
189    /// - `dst` must not overlap with the internal buffer.
190    #[inline]
191    unsafe fn copy_into_t<T>(&mut self, dst: &mut MaybeUninit<T>) -> ReadResult<()> {
192        let src = self.fill_exact(size_of::<T>())?;
193        // SAFETY:
194        // - `fill_exact` must do the appropriate bounds checking.
195        unsafe {
196            ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().cast(), size_of::<T>());
197            self.consume_unchecked(size_of::<T>());
198        }
199        Ok(())
200    }
201
202    /// Copy and consume exactly `dst.len() * size_of::<T>()` bytes from the [`Reader`] into `dst`.
203    ///
204    /// # Safety
205    ///
206    /// - `T` must be initialized by reads of `size_of::<T>()` bytes.
207    /// - `dst` must not overlap with the internal buffer.
208    #[inline]
209    unsafe fn copy_into_slice_t<T>(&mut self, dst: &mut [MaybeUninit<T>]) -> ReadResult<()> {
210        let len = size_of_val(dst);
211        let bytes = self.fill_exact(len)?;
212        // SAFETY:
213        // - `fill_exact` must do the appropriate bounds checking.
214        unsafe {
215            ptr::copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr().cast(), len);
216            self.consume_unchecked(len);
217        }
218        Ok(())
219    }
220}
221
222#[derive(Error, Debug)]
223pub enum WriteError {
224    #[error("Attempting to write {0} bytes")]
225    WriteSizeLimit(usize),
226    #[cfg(feature = "std")]
227    #[error(transparent)]
228    Io(#[from] std::io::Error),
229}
230
231#[cold]
232const fn write_size_limit(len: usize) -> WriteError {
233    WriteError::WriteSizeLimit(len)
234}
235
236pub type WriteResult<T> = core::result::Result<T, WriteError>;
237
238/// Trait for structured writing of bytes into a source of potentially uninitialized memory.
239pub trait Writer {
240    /// A variant of the [`Writer`] that can elide bounds checking within a given window.
241    ///
242    /// Trusted variants of the [`Writer`] should generally not be constructed directly,
243    /// but rather by calling [`Writer::as_trusted_for`] on a trusted [`Writer`].
244    /// This will ensure that the safety invariants are upheld.
245    type Trusted<'a>: Writer
246    where
247        Self: 'a;
248
249    /// Finalize the writer by performing any required cleanup or flushing.
250    ///
251    /// # Regarding trusted writers
252    ///
253    /// Trusted writers are not guaranteed to live as long as the parent [`Writer`] that
254    /// created them, and are typically short-lived. wincode will call `finish` after
255    /// trusted writers have completed their work, so they may rely on `finish` perform
256    /// local cleanup when needed. Importantly, trusted writers must not perform actions
257    /// that would invalidate the parent [`Writer`].
258    ///
259    /// For example, a file writer may buffer internally and delegate to trusted
260    /// sub-writers with their own buffers. These trusted writers should not close
261    /// the underlying file descriptor or other parent-owned resources, as that would
262    /// invalidate the parent writer.
263    fn finish(&mut self) -> WriteResult<()> {
264        Ok(())
265    }
266
267    /// Write exactly `src.len()` bytes from the given `src` into the writer.
268    fn write(&mut self, src: &[u8]) -> WriteResult<()>;
269
270    /// Advance the parent by `n_bytes` and return a [`Writer`] that can elide bounds checks within
271    /// that `n_bytes` window.
272    ///
273    /// Implementors must:
274    /// - Ensure that either at least `n_bytes` bytes are available backing the
275    ///   returned writer, or return an error.
276    /// - Arrange that the returned `Trusted` writer's methods operate within
277    ///   that `n_bytes` window (it may buffer or prefetch arbitrarily).
278    ///
279    /// Note:
280    /// - `as_trusted_for` is intended for callers that know they will operate
281    ///   within an exact-size window and want to avoid intermediate bounds checks.
282    ///
283    /// # Safety
284    ///
285    /// The caller must treat the returned writer as having exclusive access to
286    /// exactly `n_bytes` bytes of **uninitialized** output space in the parent,
287    /// and must:
288    ///
289    /// - Ensure that no write performed through the `Trusted` writer can
290    ///   address memory outside of that `n_bytes` window.
291    /// - Ensure that, before the `Trusted` writer is finished or the parent
292    ///   writer is used again, **every byte** in that `n_bytes` window has
293    ///   been initialized at least once via the `Trusted` writer.
294    /// - Call [`Writer::finish`] on the `Trusted` writer when writing is complete and
295    ///   before the parent writer is used again.
296    ///
297    /// Concretely:
298    /// - All writes performed via the `Trusted` writer (`write`, `write_t`,
299    ///   `write_slice_t`, etc.) must stay within the `[0, n_bytes)` region of
300    ///   the reserved space.
301    /// - It is permitted to overwrite the same bytes multiple times, but the
302    ///   union of all bytes written must cover the entire `[0, n_bytes)` window.
303    ///
304    /// Violating this is undefined behavior, because:
305    /// - `Trusted` writers are permitted to elide bounds checks within the
306    ///   `n_bytes` window; writing past the window may write past the end of
307    ///   the underlying destination.
308    /// - Failing to initialize all `n_bytes` may leave uninitialized memory in
309    ///   the destination that later safe code assumes to be fully initialized.
310    unsafe fn as_trusted_for(&mut self, n_bytes: usize) -> WriteResult<Self::Trusted<'_>>;
311
312    /// Write `T` as bytes into the source.
313    ///
314    /// # Safety
315    ///
316    /// - `T` must be plain ol' data.
317    #[inline]
318    unsafe fn write_t<T: ?Sized>(&mut self, src: &T) -> WriteResult<()> {
319        let src = from_raw_parts((src as *const T).cast::<u8>(), size_of_val(src));
320        self.write(src)?;
321        Ok(())
322    }
323
324    /// Write `[T]` as bytes into the source.
325    ///
326    /// # Safety
327    ///
328    /// - `T` must be plain ol' data.
329    #[inline]
330    unsafe fn write_slice_t<T>(&mut self, src: &[T]) -> WriteResult<()> {
331        let len = size_of_val(src);
332        let src = from_raw_parts(src.as_ptr().cast::<u8>(), len);
333        self.write(src)?;
334        Ok(())
335    }
336}
337
338mod cursor;
339mod slice;
340#[cfg(feature = "alloc")]
341mod vec;
342pub use {cursor::Cursor, slice::*};