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 #[must_use]
55 pub fn new(writer: W) -> Self {
56 Self { writer }
57 }
58
59 /// Borrow the underlying writer.
60 #[must_use]
61 pub fn writer(&self) -> &W {
62 &self.writer
63 }
64
65 /// Borrow the underlying writer mutably.
66 #[must_use]
67 pub fn writer_mut(&mut self) -> &mut W {
68 &mut self.writer
69 }
70
71 /// Consume the encoder and return the underlying writer.
72 #[must_use]
73 pub fn into_inner(self) -> W {
74 self.writer
75 }
76
77 /// Encode `value` straight into the underlying writer.
78 ///
79 /// # Errors
80 ///
81 /// - Propagates any [`crate::SerialError`] from the type's [`Serialize`].
82 /// - Maps any `std::io::Error` from the writer into [`SerialError::Io`].
83 #[inline]
84 pub fn write<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<()> {
85 value.serialize(self)
86 }
87}
88
89impl<W: Write> Encode for IoEncoder<W> {
90 #[inline]
91 fn write_byte(&mut self, byte: u8) -> Result<()> {
92 self.writer.write_all(&[byte]).map_err(map_io_error)
93 }
94
95 #[inline]
96 fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
97 self.writer.write_all(bytes).map_err(map_io_error)
98 }
99}
100
101/// Streaming decoder that reads directly from any [`Read`]-shaped source.
102///
103/// Each [`IoDecoder::read`] call may issue many small reads against the
104/// underlying source. Wrap raw sockets / files in [`std::io::BufReader`]
105/// first if read-syscall amplification is a concern.
106///
107/// # Examples
108///
109/// ```
110/// use pack_io::{IoEncoder, IoDecoder};
111/// use std::io::Cursor;
112///
113/// let mut buf: Vec<u8> = Vec::new();
114/// {
115/// let mut enc = IoEncoder::new(&mut buf);
116/// enc.write(&42_u64).unwrap();
117/// enc.write(&"hi").unwrap();
118/// }
119///
120/// let mut dec = IoDecoder::new(Cursor::new(buf));
121/// let n: u64 = dec.read().unwrap();
122/// let s: String = dec.read().unwrap();
123/// assert_eq!((n, s.as_str()), (42, "hi"));
124/// ```
125#[derive(Debug)]
126pub struct IoDecoder<R: Read> {
127 reader: R,
128 config: Config,
129}
130
131impl<R: Read> IoDecoder<R> {
132 /// Wrap `reader` with the default [`Config`].
133 #[must_use]
134 pub fn new(reader: R) -> Self {
135 Self {
136 reader,
137 config: Config::default(),
138 }
139 }
140
141 /// Wrap `reader` with the supplied configuration.
142 ///
143 /// # Errors
144 ///
145 /// Returns [`SerialError::InvalidLength`] if `config.max_alloc == 0`.
146 pub fn with_config(reader: R, config: Config) -> Result<Self> {
147 Ok(Self {
148 reader,
149 config: config.validate()?,
150 })
151 }
152
153 /// Borrow the underlying reader.
154 #[must_use]
155 pub fn reader(&self) -> &R {
156 &self.reader
157 }
158
159 /// Consume the decoder and return the underlying reader.
160 #[must_use]
161 pub fn into_inner(self) -> R {
162 self.reader
163 }
164
165 /// Decode the next value from the underlying reader.
166 ///
167 /// # Errors
168 ///
169 /// - Propagates any [`crate::SerialError`] from the type's
170 /// [`Deserialize`].
171 /// - Maps any `std::io::Error` from the reader into [`SerialError::Io`].
172 #[inline]
173 pub fn read<T: Deserialize>(&mut self) -> Result<T> {
174 T::deserialize(self)
175 }
176}
177
178impl<R: Read> Decode for IoDecoder<R> {
179 fn read_byte(&mut self) -> Result<u8> {
180 let mut buf = [0u8; 1];
181 self.read_into(&mut buf)?;
182 Ok(buf[0])
183 }
184
185 fn read_into(&mut self, out: &mut [u8]) -> Result<()> {
186 self.reader.read_exact(out).map_err(|e| {
187 if e.kind() == std::io::ErrorKind::UnexpectedEof {
188 SerialError::UnexpectedEof {
189 needed: out.len(),
190 remaining: 0,
191 }
192 } else {
193 map_io_error(e)
194 }
195 })
196 }
197
198 fn max_alloc(&self) -> usize {
199 self.config.max_alloc
200 }
201}
202
203/// Encode `value` and write the result into `writer` in a single call.
204///
205/// # Errors
206///
207/// - Propagates any [`crate::SerialError`] from the type's [`Serialize`].
208/// - Maps any `std::io::Error` from the writer into [`SerialError::Io`].
209///
210/// # Examples
211///
212/// ```
213/// use pack_io::encode_into;
214///
215/// let mut buf: Vec<u8> = Vec::new();
216/// encode_into(&(7_u64, "hello"), &mut buf).unwrap();
217/// assert!(!buf.is_empty());
218/// ```
219#[inline]
220pub fn encode_into<T, W>(value: &T, writer: &mut W) -> Result<()>
221where
222 T: Serialize + ?Sized,
223 W: Write,
224{
225 let mut enc = IoEncoder::new(writer);
226 enc.write(value)
227}
228
229/// Read all remaining bytes from `reader` and decode them as a single value
230/// of type `T`.
231///
232/// Use this for whole-buffer reads (a length-prefixed message you have
233/// already extracted from the transport, a small config file, …). For
234/// length-framed protocols where the producer wrote one value and then more
235/// bytes for something else, prefer [`IoDecoder`] directly.
236///
237/// # Errors
238///
239/// - Returns [`SerialError::TrailingBytes`] if the reader yielded extra
240/// bytes after the value was decoded.
241/// - Propagates any [`crate::SerialError`] from the type's [`Deserialize`].
242/// - Maps any `std::io::Error` from the reader into [`SerialError::Io`].
243///
244/// # Examples
245///
246/// ```
247/// use pack_io::{encode, decode_from};
248/// use std::io::Cursor;
249///
250/// let bytes = encode(&42_u64).unwrap();
251/// let n: u64 = decode_from(&mut Cursor::new(bytes)).unwrap();
252/// assert_eq!(n, 42);
253/// ```
254pub fn decode_from<T, R>(reader: &mut R) -> Result<T>
255where
256 T: Deserialize,
257 R: Read,
258{
259 let mut buf = alloc::vec::Vec::new();
260 let _ = reader.read_to_end(&mut buf).map_err(map_io_error)?;
261 crate::decode(&buf)
262}
263
264/// Map a `std::io::Error` into [`SerialError::Io`].
265#[inline]
266fn map_io_error(err: std::io::Error) -> SerialError {
267 use alloc::string::ToString;
268 SerialError::Io {
269 kind: err.kind(),
270 message: err.to_string(),
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use crate::encode;
278 use alloc::vec::Vec;
279 use std::io::Cursor;
280
281 #[test]
282 fn io_encoder_decoder_round_trip() {
283 let mut buf: Vec<u8> = Vec::new();
284 {
285 let mut enc = IoEncoder::new(&mut buf);
286 enc.write(&42_u64).unwrap();
287 enc.write(&"hello").unwrap();
288 enc.write(&true).unwrap();
289 }
290 let mut dec = IoDecoder::new(Cursor::new(buf));
291 let n: u64 = dec.read().unwrap();
292 let s: String = dec.read().unwrap();
293 let b: bool = dec.read().unwrap();
294 assert_eq!((n, s.as_str(), b), (42, "hello", true));
295 }
296
297 #[test]
298 fn encode_into_writes_same_bytes_as_encode() {
299 let value = (1u32, String::from("hi"), -2i32);
300 let from_fn = encode(&value).unwrap();
301 let mut from_io: Vec<u8> = Vec::new();
302 encode_into(&value, &mut from_io).unwrap();
303 assert_eq!(from_fn, from_io);
304 }
305
306 #[test]
307 fn decode_from_reads_same_value_as_decode() {
308 let bytes = encode(&(7u64, true)).unwrap();
309 let value: (u64, bool) = decode_from(&mut Cursor::new(bytes)).unwrap();
310 assert_eq!(value, (7, true));
311 }
312
313 #[test]
314 fn io_decoder_with_zero_cap_is_rejected() {
315 let cfg = Config::new().with_max_alloc(0);
316 let bytes: Vec<u8> = Vec::new();
317 let err = IoDecoder::with_config(Cursor::new(bytes), cfg).expect_err("zero cap");
318 assert!(matches!(err, SerialError::InvalidLength { .. }));
319 }
320
321 #[test]
322 fn io_decoder_short_read_surfaces_unexpected_eof() {
323 // Two-byte varint that says "more coming" but there's nothing after.
324 let bytes = alloc::vec![0x80];
325 let mut dec = IoDecoder::new(Cursor::new(bytes));
326 let err = dec.read::<u64>().expect_err("truncated");
327 assert!(matches!(err, SerialError::UnexpectedEof { .. }));
328 }
329}