Skip to main content

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}