bnb/guide/io.rs
1//! The I/O ladder — where the codec reads from and writes to.
2//!
3//! The everyday entry points (`decode`/`peek`/`decode_exact`/`to_bytes`) work on byte
4//! slices and `Vec`s. When you need to read from a socket or a file, `decode_from`
5//! takes any [`Source`](crate::Source) and `encode_into` any [`Sink`](crate::Sink).
6//! Pick the source by what your input can do:
7//!
8//! | Source | Backing | Can seek? | Use for |
9//! |---|---|---|---|
10//! | [`BitReader`](crate::BitReader) | `&[u8]` slice | yes (free cursor math) | in-memory bytes |
11//! | [`StreamBitReader`](crate::StreamBitReader) | any `Read` | no (forward only) | a stream you read once |
12//! | [`BufSource`](crate::BufSource) | any `Read` | yes (within a bounded buffer) | a socket that also needs to seek |
13//! | [`SeekReader`](crate::SeekReader) | `Read + Seek` | yes (via `io::Seek`) | a large file / container |
14//! | `BytesReader` (`bytes` feature) | owned `Bytes` | yes | zero-copy async framing |
15//!
16//! Seeking is only needed by messages that use `#[br(restore_position)]`; everything
17//! else runs over the forward-only [`StreamBitReader`](crate::StreamBitReader) too.
18//!
19//! # The low-level cursor
20//!
21//! [`BitReader`](crate::BitReader)/[`BitWriter`](crate::BitWriter) are the bit cursors
22//! under everything — read or write any [`Bits`](crate::Bits) value at the current bit
23//! offset:
24//!
25//! ```
26//! use bnb::{BitReader, BitWriter, u4, u12};
27//!
28//! let mut w = BitWriter::new();
29//! w.write(u4::new(0xA)).unwrap();
30//! w.write(u12::new(0xBCD)).unwrap();
31//! assert_eq!(w.into_bytes(), [0xAB, 0xCD]);
32//!
33//! let mut r = BitReader::new(&[0xAB, 0xCD]);
34//! assert_eq!(r.read::<u4>().unwrap(), u4::new(0xA));
35//! assert_eq!(r.read::<u12>().unwrap(), u12::new(0xBCD));
36//! ```
37//!
38//! # `decode_from` over each source
39//!
40//! The same message decodes from a slice cursor, a forward stream, a buffered socket,
41//! or a seekable file — only the source type changes:
42//!
43//! ```
44//! use bnb::{bin, BitReader, StreamBitReader, BufSource, SeekReader};
45//! use std::io::Cursor;
46//!
47//! #[bin(big)]
48//! #[derive(Debug, PartialEq)]
49//! struct Word { value: u32 }
50//!
51//! let bytes = [0x12, 0x34, 0x56, 0x78];
52//!
53//! // in-memory slice cursor
54//! let mut r = BitReader::new(&bytes);
55//! assert_eq!(Word::decode_from(&mut r).unwrap(), Word { value: 0x1234_5678 });
56//!
57//! // a forward-only `Read` (a `&[u8]` is `Read` but not `Seek`)
58//! let mut s = StreamBitReader::new(&bytes[..]);
59//! assert_eq!(Word::decode_from(&mut s).unwrap(), Word { value: 0x1234_5678 });
60//!
61//! // a `Read` with a bounded retain-and-seek buffer (the socket case)
62//! let mut b = BufSource::new(&bytes[..]);
63//! assert_eq!(Word::decode_from(&mut b).unwrap(), Word { value: 0x1234_5678 });
64//!
65//! // a `Read + Seek` (a file; here a Cursor over a Vec)
66//! let mut f = SeekReader::new(Cursor::new(bytes.to_vec()));
67//! assert_eq!(Word::decode_from(&mut f).unwrap(), Word { value: 0x1234_5678 });
68//! ```
69//!
70//! # Encoding
71//!
72//! `to_bytes()` is the common case; `encode(&mut impl Write)` goes straight to a
73//! socket or file, and `encode_into(&mut impl Sink)` targets an explicit bit sink.
74//!
75//! ```
76//! use bnb::bin;
77//! use bnb::EncodeExt; // brings `.encode(&mut impl Write)` into scope (the `std` feature)
78//! # #[bin(big)] #[derive(Debug, PartialEq)] struct Word { value: u32 }
79//! let w = Word { value: 0x1234_5678 };
80//! assert_eq!(w.to_bytes().unwrap(), [0x12, 0x34, 0x56, 0x78]);
81//!
82//! let mut out: Vec<u8> = Vec::new(); // any std::io::Write
83//! w.encode(&mut out).unwrap();
84//! assert_eq!(out, [0x12, 0x34, 0x56, 0x78]);
85//! ```
86//!
87//! # The `bytes` feature
88//!
89//! With `--features bytes`, `BytesReader`/`BytesWriter` decode from / encode to the
90//! `bytes` crate's `Bytes`/`BytesMut` for zero-copy async framing:
91//!
92//! ```ignore
93//! // requires `bnb = { features = ["bytes"] }`
94//! use bnb::{BytesReader, BytesWriter, Sink};
95//! let mut w = BytesWriter::new();
96//! w.write(0x1234u16).unwrap();
97//! let frame = w.freeze(); // a zero-copy `bytes::Bytes`
98//! let mut r = BytesReader::new(frame); // owns the frame, no copy
99//! ```
100//!
101//! # Bridging to `std::io`
102//!
103//! The ladder above adapts a `std::io::Read` *into* a [`Source`](crate::Source)
104//! ([`BufSource`](crate::BufSource)/[`SeekReader`](crate::SeekReader)). The reverse —
105//! handing a bnb cursor to `std::io`-based code from a `parse_with`/`write_with` — is
106//! [`Source::as_read`](crate::Source::as_read) and [`Sink::as_write`](crate::Sink::as_write),
107//! byte views over the cursor. With `From<io::Error>`, `std::io` results `?` straight
108//! into a [`BitError`](crate::BitError):
109//!
110//! ```
111//! use bnb::{BitError, BitReader, Source};
112//! use std::io::Read;
113//!
114//! fn read_three<S: Source>(r: &mut S) -> Result<[u8; 3], BitError> {
115//! let mut buf = [0u8; 3];
116//! r.as_read().read_exact(&mut buf)?; // a `std::io::Read` view over the cursor
117//! Ok(buf)
118//! }
119//!
120//! let mut r = BitReader::new(&[0xAA, 0xBB, 0xCC]);
121//! assert_eq!(read_three(&mut r).unwrap(), [0xAA, 0xBB, 0xCC]);
122//! ```
123//!
124//! # Streaming and partial input
125//!
126//! A [`StreamBitReader`](crate::StreamBitReader) or [`BufSource`](crate::BufSource)
127//! that runs out mid-message reports [`ErrorKind::Incomplete`](crate::ErrorKind), the
128//! "read more bytes and retry" signal — distinct from a definitive parse failure. See
129//! [`errors`](super::errors).