Skip to main content

iris_abi/
wire.rs

1//! Reading and writing the primitive values that ABI records are built out of.
2//!
3//! Everything on the wire is little-endian, because every machine anyone is going to run this on
4//! is little-endian and pretending otherwise would cost real instructions in the hot path for a
5//! portability nobody is asking for. If that ever stops being true it is a new ABI major version,
6//! not a runtime flag.
7//!
8//! Variable-length fields are padded so that the cursor lands back on a multiple of eight. That
9//! costs at most seven bytes per field and it means a later version of this crate can read a fixed
10//! width run of a record by pointing at it instead of copying it out field by field.
11
12use crate::error::{Error, Result};
13
14/// How wide the alignment of a record payload is, in bytes.
15///
16/// Record headers are exactly this wide too, so a record that starts aligned has a payload that
17/// starts aligned.
18pub const ALIGN: usize = 8;
19
20/// Rounds `n` up to the next multiple of [`ALIGN`].
21#[must_use]
22pub const fn align_up(n: usize) -> usize {
23    n.next_multiple_of(ALIGN)
24}
25
26/// A cursor that reads ABI values out of a byte slice.
27///
28/// The reader borrows its input and hands back borrowed slices, so decoding a record does not
29/// allocate and does not copy the payload.
30#[derive(Clone, Debug)]
31pub struct Reader<'a> {
32    buf: &'a [u8],
33    pos: usize,
34}
35
36impl<'a> Reader<'a> {
37    /// Starts reading at the beginning of `buf`.
38    #[must_use]
39    pub const fn new(buf: &'a [u8]) -> Self {
40        Self { buf, pos: 0 }
41    }
42
43    /// How many bytes are left.
44    #[must_use]
45    pub const fn remaining(&self) -> usize {
46        self.buf.len() - self.pos
47    }
48
49    /// Whether the reader has consumed everything.
50    #[must_use]
51    pub const fn is_empty(&self) -> bool {
52        self.remaining() == 0
53    }
54
55    /// How far into the buffer the cursor is.
56    #[must_use]
57    pub const fn position(&self) -> usize {
58        self.pos
59    }
60
61    fn take(&mut self, n: usize) -> Result<&'a [u8]> {
62        let available = self.remaining();
63        if n > available {
64            return Err(Error::Truncated {
65                needed: n,
66                available,
67            });
68        }
69        let out = &self.buf[self.pos..self.pos + n];
70        self.pos += n;
71        Ok(out)
72    }
73
74    /// Reads one byte.
75    ///
76    /// # Errors
77    ///
78    /// Returns [`Error::Truncated`] if the buffer is exhausted.
79    pub fn u8(&mut self) -> Result<u8> {
80        Ok(self.take(1)?[0])
81    }
82
83    /// Reads a little-endian `u16`.
84    ///
85    /// # Errors
86    ///
87    /// Returns [`Error::Truncated`] if fewer than two bytes are left.
88    pub fn u16(&mut self) -> Result<u16> {
89        let b = self.take(2)?;
90        Ok(u16::from_le_bytes([b[0], b[1]]))
91    }
92
93    /// Reads a little-endian `u32`.
94    ///
95    /// # Errors
96    ///
97    /// Returns [`Error::Truncated`] if fewer than four bytes are left.
98    pub fn u32(&mut self) -> Result<u32> {
99        let b = self.take(4)?;
100        Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
101    }
102
103    /// Reads a little-endian `u64`.
104    ///
105    /// # Errors
106    ///
107    /// Returns [`Error::Truncated`] if fewer than eight bytes are left.
108    pub fn u64(&mut self) -> Result<u64> {
109        let b = self.take(8)?;
110        Ok(u64::from_le_bytes([
111            b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
112        ]))
113    }
114
115    /// Reads a `u64` that a later version of a record appended, if the writer knew about it.
116    ///
117    /// This is the other half of the grow-at-the-end rule. A reader that does not care about a new
118    /// field just stops early and the framing puts it on the next record. A reader that does care
119    /// has to tell two situations apart: a writer that predates the field, which is fine and means
120    /// the field is absent, and a payload that was cut in half, which is not fine. Nothing left is
121    /// the first, something but not enough is the second.
122    ///
123    /// # Errors
124    ///
125    /// Returns [`Error::Truncated`] if there is at least one byte left but fewer than eight.
126    pub fn opt_u64(&mut self) -> Result<Option<u64>> {
127        if self.is_empty() {
128            return Ok(None);
129        }
130        self.u64().map(Some)
131    }
132
133    /// Reads exactly `n` bytes and borrows them from the input.
134    ///
135    /// # Errors
136    ///
137    /// Returns [`Error::Truncated`] if fewer than `n` bytes are left.
138    pub fn bytes(&mut self, n: usize) -> Result<&'a [u8]> {
139        self.take(n)
140    }
141
142    /// Skips `n` bytes.
143    ///
144    /// This is how a reader gets past a field it does not understand, which is the whole reason the
145    /// format carries lengths.
146    ///
147    /// # Errors
148    ///
149    /// Returns [`Error::Truncated`] if fewer than `n` bytes are left.
150    pub fn skip(&mut self, n: usize) -> Result<()> {
151        self.take(n).map(|_| ())
152    }
153
154    /// Skips forward to the next alignment boundary.
155    ///
156    /// # Errors
157    ///
158    /// Returns [`Error::Truncated`] if the padding runs off the end of the buffer.
159    pub fn align(&mut self) -> Result<()> {
160        self.skip(align_up(self.pos) - self.pos)
161    }
162
163    /// Reads a length-prefixed byte string, including its trailing padding.
164    ///
165    /// # Errors
166    ///
167    /// Returns [`Error::Truncated`] if the buffer ends inside the field, or
168    /// [`Error::LengthOverflow`] if the declared length does not fit in a `usize`.
169    pub fn var_bytes(&mut self) -> Result<&'a [u8]> {
170        let len = self.u32()?;
171        let len = usize::try_from(len).map_err(|_| Error::LengthOverflow)?;
172        let out = self.take(len)?;
173        self.align()?;
174        Ok(out)
175    }
176
177    /// Reads a length-prefixed UTF-8 string.
178    ///
179    /// # Errors
180    ///
181    /// Returns [`Error::NotUtf8`] if the bytes are not valid UTF-8, or the same errors as
182    /// [`Reader::var_bytes`].
183    pub fn var_str(&mut self) -> Result<&'a str> {
184        core::str::from_utf8(self.var_bytes()?).map_err(|_| Error::NotUtf8)
185    }
186
187    /// Splits off a reader over the next `n` bytes and steps this one past them.
188    ///
189    /// # Errors
190    ///
191    /// Returns [`Error::Truncated`] if fewer than `n` bytes are left.
192    pub fn sub(&mut self, n: usize) -> Result<Reader<'a>> {
193        Ok(Reader::new(self.take(n)?))
194    }
195}
196
197/// A cursor that writes ABI values into a byte slice.
198///
199/// The writer never grows its buffer. A caller that does not know how big a record will be should
200/// size the buffer with [`Writer::position`] on a dry run, or just use a buffer big enough for the
201/// largest record the ABI allows.
202#[derive(Debug)]
203pub struct Writer<'a> {
204    buf: &'a mut [u8],
205    pos: usize,
206}
207
208impl<'a> Writer<'a> {
209    /// Starts writing at the beginning of `buf`.
210    #[must_use]
211    pub fn new(buf: &'a mut [u8]) -> Self {
212        Self { buf, pos: 0 }
213    }
214
215    /// How many bytes have been written.
216    #[must_use]
217    pub const fn position(&self) -> usize {
218        self.pos
219    }
220
221    /// How much room is left.
222    #[must_use]
223    pub const fn remaining(&self) -> usize {
224        self.buf.len() - self.pos
225    }
226
227    /// Everything written so far.
228    #[must_use]
229    pub fn written(&self) -> &[u8] {
230        &self.buf[..self.pos]
231    }
232
233    fn room(&mut self, n: usize) -> Result<usize> {
234        let available = self.remaining();
235        if n > available {
236            return Err(Error::BufferFull {
237                needed: n,
238                available,
239            });
240        }
241        let at = self.pos;
242        self.pos += n;
243        Ok(at)
244    }
245
246    /// Writes one byte.
247    ///
248    /// # Errors
249    ///
250    /// Returns [`Error::BufferFull`] if there is no room.
251    pub fn u8(&mut self, v: u8) -> Result<()> {
252        let at = self.room(1)?;
253        self.buf[at] = v;
254        Ok(())
255    }
256
257    /// Writes a little-endian `u16`.
258    ///
259    /// # Errors
260    ///
261    /// Returns [`Error::BufferFull`] if there is no room.
262    pub fn u16(&mut self, v: u16) -> Result<()> {
263        self.raw(&v.to_le_bytes())
264    }
265
266    /// Writes a little-endian `u32`.
267    ///
268    /// # Errors
269    ///
270    /// Returns [`Error::BufferFull`] if there is no room.
271    pub fn u32(&mut self, v: u32) -> Result<()> {
272        self.raw(&v.to_le_bytes())
273    }
274
275    /// Writes a little-endian `u64`.
276    ///
277    /// # Errors
278    ///
279    /// Returns [`Error::BufferFull`] if there is no room.
280    pub fn u64(&mut self, v: u64) -> Result<()> {
281        self.raw(&v.to_le_bytes())
282    }
283
284    /// Writes bytes with no length prefix and no padding.
285    ///
286    /// # Errors
287    ///
288    /// Returns [`Error::BufferFull`] if there is no room.
289    pub fn raw(&mut self, v: &[u8]) -> Result<()> {
290        let at = self.room(v.len())?;
291        self.buf[at..at + v.len()].copy_from_slice(v);
292        Ok(())
293    }
294
295    /// Writes zeroes up to the next alignment boundary.
296    ///
297    /// # Errors
298    ///
299    /// Returns [`Error::BufferFull`] if there is no room.
300    pub fn align(&mut self) -> Result<()> {
301        let pad = align_up(self.pos) - self.pos;
302        let at = self.room(pad)?;
303        self.buf[at..at + pad].fill(0);
304        Ok(())
305    }
306
307    /// Writes a length-prefixed byte string and pads it out.
308    ///
309    /// # Errors
310    ///
311    /// Returns [`Error::BufferFull`] if there is no room, or [`Error::LengthOverflow`] if the slice
312    /// is longer than a `u32` can describe.
313    pub fn var_bytes(&mut self, v: &[u8]) -> Result<()> {
314        let len = u32::try_from(v.len()).map_err(|_| Error::LengthOverflow)?;
315        self.u32(len)?;
316        self.raw(v)?;
317        self.align()
318    }
319
320    /// Writes a length-prefixed string.
321    ///
322    /// # Errors
323    ///
324    /// Returns the same errors as [`Writer::var_bytes`].
325    pub fn var_str(&mut self, v: &str) -> Result<()> {
326        self.var_bytes(v.as_bytes())
327    }
328
329    /// Overwrites a `u32` that was already written, at absolute offset `at`.
330    ///
331    /// This exists so a record can reserve its length field, write its payload, and then fill the
332    /// length in once it is known.
333    ///
334    /// # Errors
335    ///
336    /// Returns [`Error::BufferFull`] if `at` is not inside what has been written.
337    pub fn patch_u32(&mut self, at: usize, v: u32) -> Result<()> {
338        if at + 4 > self.pos {
339            return Err(Error::BufferFull {
340                needed: at + 4,
341                available: self.pos,
342            });
343        }
344        self.buf[at..at + 4].copy_from_slice(&v.to_le_bytes());
345        Ok(())
346    }
347}