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::*};