Skip to main content

lumen/
io.rs

1//! Little-endian, bounds-checked byte reading and writing.
2//!
3//! Every read is checked against the buffer, and every failure carries the
4//! [`Check`] the caller attached to the reader, so a truncated payload in a
5//! `HEAD` chunk reports `head.*` rather than a generic parse error.
6
7use crate::check::Check;
8use crate::error::{Error, Result};
9
10/// A cursor over a byte slice with checked little-endian reads.
11#[derive(Debug, Clone)]
12pub struct Reader<'a> {
13    buf: &'a [u8],
14    pos: usize,
15    check: Check,
16}
17
18impl<'a> Reader<'a> {
19    /// A reader that reports truncation as [`Check::DirChunkExtent`].
20    ///
21    /// Callers parsing a specific structure should prefer [`Reader::checked`].
22    pub fn new(buf: &'a [u8]) -> Self {
23        Reader {
24            buf,
25            pos: 0,
26            check: Check::DirChunkExtent,
27        }
28    }
29
30    /// A reader whose truncation errors report `check`.
31    pub fn checked(buf: &'a [u8], check: Check) -> Self {
32        Reader { buf, pos: 0, check }
33    }
34
35    /// The same reader with a different truncation check.
36    pub fn with_check(mut self, check: Check) -> Self {
37        self.check = check;
38        self
39    }
40
41    /// The check reported when a read runs past the end.
42    pub fn check(&self) -> Check {
43        self.check
44    }
45
46    /// Bytes consumed so far.
47    pub fn pos(&self) -> usize {
48        self.pos
49    }
50
51    /// Bytes left.
52    pub fn remaining(&self) -> usize {
53        self.buf.len() - self.pos
54    }
55
56    /// Whether the reader is exhausted.
57    pub fn is_empty(&self) -> bool {
58        self.pos == self.buf.len()
59    }
60
61    /// The whole underlying buffer.
62    pub fn buffer(&self) -> &'a [u8] {
63        self.buf
64    }
65
66    /// The not-yet-consumed tail.
67    pub fn tail(&self) -> &'a [u8] {
68        &self.buf[self.pos..]
69    }
70
71    fn truncate(&self, want: usize) -> Result<()> {
72        if self.remaining() < want {
73            return Err(Error::new(
74                self.check,
75                format!(
76                    "truncated: need {want} byte(s) at offset {}, {} remaining",
77                    self.pos,
78                    self.remaining()
79                ),
80            ));
81        }
82        Ok(())
83    }
84
85    /// Read one byte.
86    pub fn u8(&mut self) -> Result<u8> {
87        self.truncate(1)?;
88        let v = self.buf[self.pos];
89        self.pos += 1;
90        Ok(v)
91    }
92
93    /// Read a `u16`.
94    pub fn u16(&mut self) -> Result<u16> {
95        Ok(u16::from_le_bytes(self.array::<2>()?))
96    }
97
98    /// Read a `u32`.
99    pub fn u32(&mut self) -> Result<u32> {
100        Ok(u32::from_le_bytes(self.array::<4>()?))
101    }
102
103    /// Read a `u64`.
104    pub fn u64(&mut self) -> Result<u64> {
105        Ok(u64::from_le_bytes(self.array::<8>()?))
106    }
107
108    /// Read a fixed-size array.
109    pub fn array<const N: usize>(&mut self) -> Result<[u8; N]> {
110        self.truncate(N)?;
111        let mut out = [0u8; N];
112        out.copy_from_slice(&self.buf[self.pos..self.pos + N]);
113        self.pos += N;
114        Ok(out)
115    }
116
117    /// Borrow the next `n` bytes.
118    pub fn bytes(&mut self, n: usize) -> Result<&'a [u8]> {
119        self.truncate(n)?;
120        let out = &self.buf[self.pos..self.pos + n];
121        self.pos += n;
122        Ok(out)
123    }
124
125    /// Borrow the whole tail.
126    pub fn rest(&mut self) -> &'a [u8] {
127        let out = &self.buf[self.pos..];
128        self.pos = self.buf.len();
129        out
130    }
131
132    /// Skip `n` bytes.
133    pub fn skip(&mut self, n: usize) -> Result<()> {
134        self.truncate(n)?;
135        self.pos += n;
136        Ok(())
137    }
138
139    /// Read a varint ([`section 5.2`](crate::varint)).
140    pub fn varint(&mut self) -> Result<u64> {
141        crate::varint::decode(self)
142    }
143
144    /// Read a varint bounded by `max`, rejecting a value above it before allocation.
145    pub fn varint_bounded(&mut self, max: u64) -> Result<u64> {
146        let v = self.varint()?;
147        if v > max {
148            return Err(Error::new(
149                self.check,
150                format!("varint value {v} exceeds the bound {max}"),
151            ));
152        }
153        Ok(v)
154    }
155
156    /// Read a UTF-8 string of `n` bytes.
157    pub fn utf8(&mut self, n: usize) -> Result<&'a str> {
158        let bytes = self.bytes(n)?;
159        core::str::from_utf8(bytes).map_err(|e| {
160            Error::new(
161                self.check,
162                format!("invalid UTF-8 at offset {}: {e}", self.pos - n),
163            )
164        })
165    }
166}
167
168/// A growable little-endian output buffer.
169#[derive(Debug, Default, Clone)]
170pub struct Writer {
171    buf: Vec<u8>,
172}
173
174impl Writer {
175    /// An empty writer.
176    pub fn new() -> Self {
177        Writer { buf: Vec::new() }
178    }
179
180    /// An empty writer with `capacity` bytes preallocated.
181    pub fn with_capacity(capacity: usize) -> Self {
182        Writer {
183            buf: Vec::with_capacity(capacity),
184        }
185    }
186
187    /// Bytes written so far.
188    pub fn len(&self) -> usize {
189        self.buf.len()
190    }
191
192    /// Whether nothing has been written.
193    pub fn is_empty(&self) -> bool {
194        self.buf.is_empty()
195    }
196
197    /// The bytes written so far.
198    pub fn as_slice(&self) -> &[u8] {
199        &self.buf
200    }
201
202    /// Consume the writer, yielding its bytes.
203    pub fn into_vec(self) -> Vec<u8> {
204        self.buf
205    }
206
207    /// Append one byte.
208    pub fn u8(&mut self, v: u8) {
209        self.buf.push(v);
210    }
211
212    /// Append a `u16`.
213    pub fn u16(&mut self, v: u16) {
214        self.buf.extend_from_slice(&v.to_le_bytes());
215    }
216
217    /// Append a `u32`.
218    pub fn u32(&mut self, v: u32) {
219        self.buf.extend_from_slice(&v.to_le_bytes());
220    }
221
222    /// Append a `u64`.
223    pub fn u64(&mut self, v: u64) {
224        self.buf.extend_from_slice(&v.to_le_bytes());
225    }
226
227    /// Append raw bytes.
228    pub fn bytes(&mut self, v: &[u8]) {
229        self.buf.extend_from_slice(v);
230    }
231
232    /// Append a varint.
233    pub fn varint(&mut self, v: u64) {
234        crate::varint::encode(self, v);
235    }
236
237    /// Pad with zero bytes until the length is a multiple of `align`.
238    pub fn pad_to(&mut self, align: usize) {
239        debug_assert!(align.is_power_of_two());
240        while self.buf.len() % align != 0 {
241            self.buf.push(0);
242        }
243    }
244
245    /// Overwrite the `u32` at `at`.
246    ///
247    /// Used for back-patching offsets once a layout is known.
248    pub fn patch_u32(&mut self, at: usize, v: u32) {
249        self.buf[at..at + 4].copy_from_slice(&v.to_le_bytes());
250    }
251
252    /// Overwrite the `u64` at `at`.
253    pub fn patch_u64(&mut self, at: usize, v: u64) {
254        self.buf[at..at + 8].copy_from_slice(&v.to_le_bytes());
255    }
256
257    /// Overwrite the bytes at `at` with `v`.
258    pub fn patch_bytes(&mut self, at: usize, v: &[u8]) {
259        self.buf[at..at + v.len()].copy_from_slice(v);
260    }
261}