pack_io/io.rs
1//! `std::io::Read` / `std::io::Write` integration: the streaming Tier-2
2//! encoder and decoder pair, plus convenience free functions.
3//!
4//! This module is gated on the `std` feature (on by default). With `std` off,
5//! the crate compiles for `no_std` targets using `core` + `alloc` only and
6//! none of this module is reachable.
7//!
8//! ## When to use which entry point
9//!
10//! - For one-shot send / receive of a single value, prefer [`encode_into`] /
11//! [`decode_from`]: they take any `Write` / `Read` and handle the
12//! buffering.
13//! - For interleaved writes / reads across many values without per-value
14//! allocation, instantiate an [`IoEncoder`] / [`IoDecoder`] and call
15//! `write` / `read` repeatedly.
16//!
17//! ## Errors
18//!
19//! Both directions surface I/O failure through the codec's
20//! [`crate::SerialError`] type via [`SerialError::Io`]. The `Io` variant
21//! captures `std::io::ErrorKind` and a message string — enough to surface
22//! the original cause without taking on a non-`Clone` payload.
23
24use std::io::{Read, Write};
25
26use crate::codec::{Config, Decode, Encode};
27use crate::error::{Result, SerialError};
28use crate::traits::{Deserialize, Serialize};
29
30/// Streaming encoder that writes directly into any [`Write`]-shaped sink.
31///
32/// Each [`IoEncoder::write`] call calls into the underlying writer for the
33/// bytes the type produces. The encoder does **not** buffer; if you wrap a
34/// raw socket / file, wrap it in a [`std::io::BufWriter`] first.
35///
36/// # Examples
37///
38/// ```
39/// use pack_io::IoEncoder;
40///
41/// let mut sink: Vec<u8> = Vec::new();
42/// let mut enc = IoEncoder::new(&mut sink);
43/// enc.write(&42_u64).unwrap();
44/// enc.write(&"hello").unwrap();
45/// assert!(!sink.is_empty());
46/// ```
47#[derive(Debug)]
48pub struct IoEncoder<W: Write> {
49 writer: W,
50}
51
52impl<W: Write> IoEncoder<W> {
53 /// Wrap `writer` in an encoder.
54 ///
55 /// The encoder does not buffer; wrap raw sockets / files in a
56 /// [`std::io::BufWriter`] first if syscall amplification is a concern.
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// use pack_io::IoEncoder;
62 ///
63 /// let mut sink: Vec<u8> = Vec::new();
64 /// let _enc = IoEncoder::new(&mut sink);
65 /// ```
66 #[must_use]
67 pub fn new(writer: W) -> Self {
68 Self { writer }
69 }
70
71 /// Borrow the underlying writer.
72 ///
73 /// # Examples
74 ///
75 /// ```
76 /// use pack_io::IoEncoder;
77 ///
78 /// let mut sink: Vec<u8> = Vec::new();
79 /// let enc = IoEncoder::new(&mut sink);
80 /// let _: &&mut Vec<u8> = &enc.writer();
81 /// ```
82 #[must_use]
83 pub fn writer(&self) -> &W {
84 &self.writer
85 }
86
87 /// Borrow the underlying writer mutably.
88 ///
89 /// Useful when downstream code needs `&mut W` to call writer-specific
90 /// methods (e.g. `flush`) without consuming the encoder.
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// use std::io::{BufWriter, Write};
96 /// use pack_io::IoEncoder;
97 ///
98 /// let mut sink: Vec<u8> = Vec::new();
99 /// let buffered = BufWriter::new(&mut sink);
100 /// let mut enc = IoEncoder::new(buffered);
101 /// enc.write(&7_u64).unwrap();
102 /// enc.writer_mut().flush().unwrap();
103 /// ```
104 #[must_use]
105 pub fn writer_mut(&mut self) -> &mut W {
106 &mut self.writer
107 }
108
109 /// Consume the encoder and return the underlying writer.
110 ///
111 /// # Examples
112 ///
113 /// ```
114 /// use pack_io::IoEncoder;
115 ///
116 /// let sink: Vec<u8> = Vec::new();
117 /// let mut enc = IoEncoder::new(sink);
118 /// enc.write(&42_u64).unwrap();
119 /// let written: Vec<u8> = enc.into_inner();
120 /// assert_eq!(written, &[0x2a]);
121 /// ```
122 #[must_use]
123 pub fn into_inner(self) -> W {
124 self.writer
125 }
126
127 /// Encode `value` straight into the underlying writer.
128 ///
129 /// # Errors
130 ///
131 /// - Propagates any [`crate::SerialError`] from the type's [`Serialize`].
132 /// - Maps any `std::io::Error` from the writer into [`SerialError::Io`].
133 #[inline]
134 pub fn write<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<()> {
135 value.serialize(self)
136 }
137}
138
139impl<W: Write> Encode for IoEncoder<W> {
140 #[inline]
141 fn write_byte(&mut self, byte: u8) -> Result<()> {
142 self.writer.write_all(&[byte]).map_err(map_io_error)
143 }
144
145 #[inline]
146 fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
147 self.writer.write_all(bytes).map_err(map_io_error)
148 }
149}
150
151/// Streaming decoder that reads directly from any [`Read`]-shaped source.
152///
153/// Each [`IoDecoder::read`] call may issue many small reads against the
154/// underlying source. Wrap raw sockets / files in [`std::io::BufReader`]
155/// first if read-syscall amplification is a concern.
156///
157/// # Examples
158///
159/// ```
160/// use pack_io::{IoEncoder, IoDecoder};
161/// use std::io::Cursor;
162///
163/// let mut buf: Vec<u8> = Vec::new();
164/// {
165/// let mut enc = IoEncoder::new(&mut buf);
166/// enc.write(&42_u64).unwrap();
167/// enc.write(&"hi").unwrap();
168/// }
169///
170/// let mut dec = IoDecoder::new(Cursor::new(buf));
171/// let n: u64 = dec.read().unwrap();
172/// let s: String = dec.read().unwrap();
173/// assert_eq!((n, s.as_str()), (42, "hi"));
174/// ```
175#[derive(Debug)]
176pub struct IoDecoder<R: Read> {
177 reader: R,
178 config: Config,
179}
180
181impl<R: Read> IoDecoder<R> {
182 /// Wrap `reader` with the default [`Config`] (1 GiB `max_alloc`).
183 ///
184 /// For tighter allocation caps on untrusted input, use
185 /// [`IoDecoder::with_config`] instead.
186 ///
187 /// # Examples
188 ///
189 /// ```
190 /// use std::io::Cursor;
191 /// use pack_io::IoDecoder;
192 ///
193 /// let bytes = pack_io::encode(&42_u64).unwrap();
194 /// let mut dec = IoDecoder::new(Cursor::new(bytes));
195 /// let n: u64 = dec.read().unwrap();
196 /// assert_eq!(n, 42);
197 /// ```
198 #[must_use]
199 pub fn new(reader: R) -> Self {
200 Self {
201 reader,
202 config: Config::default(),
203 }
204 }
205
206 /// Wrap `reader` with the supplied configuration.
207 ///
208 /// # Errors
209 ///
210 /// Returns [`SerialError::InvalidLength`] if `config.max_alloc == 0`.
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// use std::io::Cursor;
216 /// use pack_io::{Config, IoDecoder};
217 ///
218 /// let cfg = Config::new().with_max_alloc(16 * 1024);
219 /// let bytes = pack_io::encode(&"hello").unwrap();
220 /// let mut dec = IoDecoder::with_config(Cursor::new(bytes), cfg).unwrap();
221 /// let s: String = dec.read().unwrap();
222 /// assert_eq!(s, "hello");
223 /// ```
224 pub fn with_config(reader: R, config: Config) -> Result<Self> {
225 Ok(Self {
226 reader,
227 config: config.validate()?,
228 })
229 }
230
231 /// Borrow the underlying reader.
232 ///
233 /// # Examples
234 ///
235 /// ```
236 /// use std::io::Cursor;
237 /// use pack_io::IoDecoder;
238 ///
239 /// let dec = IoDecoder::new(Cursor::new(vec![0u8; 4]));
240 /// assert_eq!(dec.reader().get_ref().len(), 4);
241 /// ```
242 #[must_use]
243 pub fn reader(&self) -> &R {
244 &self.reader
245 }
246
247 /// Consume the decoder and return the underlying reader.
248 ///
249 /// Useful when the caller wants to take back ownership of the source
250 /// (e.g. to drop the reader, return it to a pool, or feed it to a
251 /// different consumer) after the decoded prefix has been processed.
252 ///
253 /// # Examples
254 ///
255 /// ```
256 /// use std::io::Cursor;
257 /// use pack_io::IoDecoder;
258 ///
259 /// let bytes = pack_io::encode(&42_u64).unwrap();
260 /// let mut dec = IoDecoder::new(Cursor::new(bytes));
261 /// let _n: u64 = dec.read().unwrap();
262 /// let reader: Cursor<Vec<u8>> = dec.into_inner();
263 /// assert_eq!(reader.position(), 1); // one byte consumed for u64=42
264 /// ```
265 #[must_use]
266 pub fn into_inner(self) -> R {
267 self.reader
268 }
269
270 /// Decode the next value from the underlying reader.
271 ///
272 /// # Errors
273 ///
274 /// - Propagates any [`crate::SerialError`] from the type's
275 /// [`Deserialize`].
276 /// - Maps any `std::io::Error` from the reader into [`SerialError::Io`].
277 #[inline]
278 pub fn read<T: Deserialize>(&mut self) -> Result<T> {
279 T::deserialize(self)
280 }
281}
282
283impl<R: Read> Decode for IoDecoder<R> {
284 fn read_byte(&mut self) -> Result<u8> {
285 let mut buf = [0u8; 1];
286 self.read_into(&mut buf)?;
287 Ok(buf[0])
288 }
289
290 fn read_into(&mut self, out: &mut [u8]) -> Result<()> {
291 self.reader.read_exact(out).map_err(|e| {
292 if e.kind() == std::io::ErrorKind::UnexpectedEof {
293 SerialError::UnexpectedEof {
294 needed: out.len(),
295 remaining: 0,
296 }
297 } else {
298 map_io_error(e)
299 }
300 })
301 }
302
303 fn max_alloc(&self) -> usize {
304 self.config.max_alloc
305 }
306}
307
308/// Encode `value` and write the result into `writer` in a single call.
309///
310/// # Errors
311///
312/// - Propagates any [`crate::SerialError`] from the type's [`Serialize`].
313/// - Maps any `std::io::Error` from the writer into [`SerialError::Io`].
314///
315/// # Examples
316///
317/// ```
318/// use pack_io::encode_into;
319///
320/// let mut buf: Vec<u8> = Vec::new();
321/// encode_into(&(7_u64, "hello"), &mut buf).unwrap();
322/// assert!(!buf.is_empty());
323/// ```
324#[inline]
325pub fn encode_into<T, W>(value: &T, writer: &mut W) -> Result<()>
326where
327 T: Serialize + ?Sized,
328 W: Write,
329{
330 let mut enc = IoEncoder::new(writer);
331 enc.write(value)
332}
333
334/// Read all remaining bytes from `reader` and decode them as a single value
335/// of type `T`.
336///
337/// Use this for whole-buffer reads (a length-prefixed message you have
338/// already extracted from the transport, a small config file, …). For
339/// length-framed protocols where the producer wrote one value and then more
340/// bytes for something else, prefer [`IoDecoder`] directly.
341///
342/// # Errors
343///
344/// - Returns [`SerialError::TrailingBytes`] if the reader yielded extra
345/// bytes after the value was decoded.
346/// - Propagates any [`crate::SerialError`] from the type's [`Deserialize`].
347/// - Maps any `std::io::Error` from the reader into [`SerialError::Io`].
348///
349/// # Examples
350///
351/// ```
352/// use pack_io::{encode, decode_from};
353/// use std::io::Cursor;
354///
355/// let bytes = encode(&42_u64).unwrap();
356/// let n: u64 = decode_from(&mut Cursor::new(bytes)).unwrap();
357/// assert_eq!(n, 42);
358/// ```
359pub fn decode_from<T, R>(reader: &mut R) -> Result<T>
360where
361 T: Deserialize,
362 R: Read,
363{
364 let mut buf = alloc::vec::Vec::new();
365 let _ = reader.read_to_end(&mut buf).map_err(map_io_error)?;
366 crate::decode(&buf)
367}
368
369/// Map a `std::io::Error` into [`SerialError::Io`].
370#[inline]
371fn map_io_error(err: std::io::Error) -> SerialError {
372 use alloc::string::ToString;
373 SerialError::Io {
374 kind: err.kind(),
375 message: err.to_string(),
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use crate::encode;
383 use alloc::vec::Vec;
384 use std::io::Cursor;
385
386 #[test]
387 fn io_encoder_decoder_round_trip() {
388 let mut buf: Vec<u8> = Vec::new();
389 {
390 let mut enc = IoEncoder::new(&mut buf);
391 enc.write(&42_u64).unwrap();
392 enc.write(&"hello").unwrap();
393 enc.write(&true).unwrap();
394 }
395 let mut dec = IoDecoder::new(Cursor::new(buf));
396 let n: u64 = dec.read().unwrap();
397 let s: String = dec.read().unwrap();
398 let b: bool = dec.read().unwrap();
399 assert_eq!((n, s.as_str(), b), (42, "hello", true));
400 }
401
402 #[test]
403 fn encode_into_writes_same_bytes_as_encode() {
404 let value = (1u32, String::from("hi"), -2i32);
405 let from_fn = encode(&value).unwrap();
406 let mut from_io: Vec<u8> = Vec::new();
407 encode_into(&value, &mut from_io).unwrap();
408 assert_eq!(from_fn, from_io);
409 }
410
411 #[test]
412 fn decode_from_reads_same_value_as_decode() {
413 let bytes = encode(&(7u64, true)).unwrap();
414 let value: (u64, bool) = decode_from(&mut Cursor::new(bytes)).unwrap();
415 assert_eq!(value, (7, true));
416 }
417
418 #[test]
419 fn io_decoder_with_zero_cap_is_rejected() {
420 let cfg = Config::new().with_max_alloc(0);
421 let bytes: Vec<u8> = Vec::new();
422 let err = IoDecoder::with_config(Cursor::new(bytes), cfg).expect_err("zero cap");
423 assert!(matches!(err, SerialError::InvalidLength { .. }));
424 }
425
426 #[test]
427 fn io_decoder_short_read_surfaces_unexpected_eof() {
428 // Two-byte varint that says "more coming" but there's nothing after.
429 let bytes = alloc::vec![0x80];
430 let mut dec = IoDecoder::new(Cursor::new(bytes));
431 let err = dec.read::<u64>().expect_err("truncated");
432 assert!(matches!(err, SerialError::UnexpectedEof { .. }));
433 }
434}