Skip to main content

ridl_rt/
flatbuffers.rs

1//! Shared reading and writing for the FlatBuffers payload encoding.
2//!
3//! This module is gated by the `flatbuffers` cargo feature. It holds the parts
4//! of a FlatBuffers codec that are the same for every payload type, so that a
5//! generated `Payload<FlatBuffers>` implementation carries only what its own
6//! shape decides: which fields it has, where the projection put them, and what
7//! its typl constraints are.
8//!
9//! It takes no dependency. The FlatBuffers runtime crate that ADR-0020
10//! decision 5 permits under this feature is not used here, and nothing in this
11//! module allocates. That decision's 2026-10-03 amendment excludes planus from
12//! the permission: no planus crate may be a dependency of this crate.
13//!
14//! # What is here, and what is not
15//!
16//! Reading: the byte-order reads, the vtable walk, and the string and vector
17//! headers — everything a generated `verify` needs to walk a buffer it does
18//! not trust. Writing: [`Builder`], which builds a buffer from the end of a
19//! caller's slice downwards.
20//!
21//! **This module decides no layout.** [`Builder::push_table`] is handed a
22//! slot, an offset and a table size and writes the bytes they describe; it
23//! does not choose them. Which slot a field takes is a fact of the
24//! projection, which the size bound and the two emitters all read. A field's
25//! offset inside its table and the table's size are the codec emitter's,
26//! because no other emitter can observe them — a `.fbs` schema states no
27//! offsets — and the bound charges enough alignment slack per slot to hold
28//! whatever order that emitter writes a table's fields in.
29//!
30//! [`Builder::push_offset_vector`] arrived with that emitter, for a vector of
31//! strings and of tables. A helper for a union has not been needed: a union's
32//! wrapper table and a non-table arm's box are both ordinary tables
33//! (ADR-0019 decisions 1 and 2), and [`Builder::push_table`] writes them.
34//!
35//! # Alignment, and why reads are safe at any alignment
36//!
37//! Every scalar is read by copying its bytes into an array and calling
38//! `from_le_bytes`, so no read dereferences an unaligned pointer and a buffer
39//! may arrive at any alignment, which is what a transport hands over. A
40//! generated `verify` therefore never produces `Malformed::Unaligned`.
41//!
42//! The writer still aligns what it writes, because a reader that is not this
43//! one — a `flatc`-generated accessor, or another language's runtime — does
44//! read in place.
45
46use core::mem::size_of;
47use core::str;
48
49use crate::payload::{EncodeError, Malformed};
50
51/// The size of a `uoffset_t`, of an `soffset_t`, and of a vector's or a
52/// string's length prefix.
53pub const OFFSET_SIZE: usize = 4;
54
55/// The size of a `voffset_t`, one vtable entry.
56pub const VOFFSET_SIZE: usize = 2;
57
58/// The bytes a vtable carries before its first entry: its own size, then the
59/// size of the table it describes.
60pub const VTABLE_HEADER: usize = 4;
61
62fn at(buf: &[u8], from: usize, len: usize) -> Result<&[u8], Malformed> {
63    buf.get(from..)
64        .and_then(|rest| rest.get(..len))
65        .ok_or(Malformed::OutOfBounds)
66}
67
68macro_rules! read_scalar {
69    ($name:ident, $ty:ty, $what:literal) => {
70        #[doc = concat!("Reads the little-endian `", $what, "` at `from`.")]
71        ///
72        /// The bytes are copied before they are read, so `from` needs no
73        /// alignment.
74        pub fn $name(buf: &[u8], from: usize) -> Result<$ty, Malformed> {
75            let mut bytes = [0u8; size_of::<$ty>()];
76            bytes.copy_from_slice(at(buf, from, size_of::<$ty>())?);
77            Ok(<$ty>::from_le_bytes(bytes))
78        }
79    };
80}
81
82read_scalar!(read_u8, u8, "u8");
83read_scalar!(read_i8, i8, "i8");
84read_scalar!(read_u16, u16, "u16");
85read_scalar!(read_i16, i16, "i16");
86read_scalar!(read_u32, u32, "u32");
87read_scalar!(read_i32, i32, "i32");
88read_scalar!(read_u64, u64, "u64");
89read_scalar!(read_i64, i64, "i64");
90read_scalar!(read_f32, f32, "f32");
91read_scalar!(read_f64, f64, "f64");
92
93/// Reads the FlatBuffers boolean at `from`: zero is false, anything else is
94/// true.
95pub fn read_bool(buf: &[u8], from: usize) -> Result<bool, Malformed> {
96    Ok(read_u8(buf, from)? != 0)
97}
98
99/// Follows the `uoffset_t` stored at `from` and returns the position it names.
100///
101/// A `uoffset_t` is unsigned and relative to its own position, so it always
102/// points forward.
103pub fn follow(buf: &[u8], from: usize) -> Result<usize, Malformed> {
104    let relative = u64::from(read_u32(buf, from)?);
105    let target = from as u64 + relative;
106    if target >= buf.len() as u64 {
107        return Err(Malformed::OutOfBounds);
108    }
109    Ok(target as usize)
110}
111
112/// The position of the root table: the `uoffset_t` at the start of the buffer.
113pub fn root(buf: &[u8]) -> Result<usize, Malformed> {
114    follow(buf, 0)
115}
116
117/// The position of field `slot` of the table at `table`, or `None` when the
118/// table does not carry it.
119///
120/// An absent field is not an error: a FlatBuffers table omits a field whose
121/// value equals its declared default, and a vtable shorter than `slot` omits
122/// every field from there on. Whether an absent field is legal for the type is
123/// the generated `verify`'s question, not this one's.
124///
125/// `width` is the field's size in bytes — four for an offset. The field must
126/// lie wholly inside the table the vtable describes, so a vtable that points a
127/// field across the table's end is rejected here rather than read from bytes
128/// belonging to another object.
129pub fn field(
130    buf: &[u8],
131    table: usize,
132    slot: u16,
133    width: usize,
134) -> Result<Option<usize>, Malformed> {
135    // The table's first field is a signed offset backwards to its vtable.
136    // The arithmetic is done in i64 so that it cannot wrap on any target.
137    let soffset = read_i32(buf, table)?;
138    let vtable = table as i64 - i64::from(soffset);
139    if vtable < 0 || vtable as u64 >= buf.len() as u64 {
140        return Err(Malformed::OutOfBounds);
141    }
142    let vtable = vtable as usize;
143
144    let vtable_bytes = usize::from(read_u16(buf, vtable)?);
145    if vtable_bytes < VTABLE_HEADER {
146        return Err(Malformed::OutOfBounds);
147    }
148    // The whole vtable must lie inside the buffer, so that a walk of it is
149    // bounded by the buffer rather than by its own declared size.
150    at(buf, vtable, vtable_bytes)?;
151    let table_bytes = usize::from(read_u16(buf, vtable + VOFFSET_SIZE)?);
152
153    let entry = vtable + VTABLE_HEADER + usize::from(slot) * VOFFSET_SIZE;
154    if entry + VOFFSET_SIZE > vtable + vtable_bytes {
155        return Ok(None);
156    }
157    let offset = usize::from(read_u16(buf, entry)?);
158    if offset == 0 {
159        return Ok(None);
160    }
161    // The field must lie wholly inside the table the vtable describes: one
162    // that starts inside it and ends past it would otherwise be read from
163    // whatever follows the table in the buffer.
164    if offset < OFFSET_SIZE || offset + width > table_bytes {
165        return Err(Malformed::OutOfBounds);
166    }
167    let position = table as u64 + offset as u64;
168    if position >= buf.len() as u64 {
169        return Err(Malformed::OutOfBounds);
170    }
171    Ok(Some(position as usize))
172}
173
174/// The string the `uoffset_t` at `from` names, checked for UTF-8.
175pub fn string(buf: &[u8], from: usize) -> Result<&str, Malformed> {
176    let start = follow(buf, from)?;
177    let len = read_u32(buf, start)? as usize;
178    let bytes = at(buf, start + OFFSET_SIZE, len)?;
179    // A FlatBuffers string carries a terminating zero after its bytes, so a
180    // reader can hand it to C. Accepting a buffer without one would let
181    // `verify` pass bytes a C consumer runs off the end of.
182    if at(buf, start + OFFSET_SIZE + len, 1)?[0] != 0 {
183        return Err(Malformed::OutOfBounds);
184    }
185    str::from_utf8(bytes).map_err(|_| Malformed::Utf8)
186}
187
188/// A vector's length and the position of its first element.
189#[derive(Clone, Copy, Debug, PartialEq, Eq)]
190pub struct Vector {
191    /// The number of elements.
192    pub len: usize,
193    /// The position of element zero.
194    pub first: usize,
195}
196
197impl Vector {
198    /// The position of element `index`, which the caller has checked against
199    /// [`Vector::len`].
200    pub fn element(&self, index: usize, stride: usize) -> usize {
201        self.first + index * stride
202    }
203}
204
205/// The vector the `uoffset_t` at `from` names, whose elements are `stride`
206/// bytes each.
207///
208/// The whole element span is checked against the buffer here, so a walk over
209/// the elements is bounded by one check rather than by one per element.
210pub fn vector(buf: &[u8], from: usize, stride: usize) -> Result<Vector, Malformed> {
211    let start = follow(buf, from)?;
212    let len = read_u32(buf, start)? as usize;
213    let first = start + OFFSET_SIZE;
214    let span = len.checked_mul(stride).ok_or(Malformed::OutOfBounds)?;
215    at(buf, first, span)?;
216    Ok(Vector { len, first })
217}
218
219/// The position of an object in a buffer under construction, measured
220/// backwards from the end of the builder's slice.
221///
222/// It is not an offset into the finished buffer. A `Pos` is only meaningful to
223/// the [`Builder`] that produced it, and passing one to a different builder is
224/// a programming error that `debug_assert` catches.
225#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
226pub struct Pos(usize);
227
228/// A value written into a table field.
229#[derive(Clone, Copy, Debug, PartialEq)]
230pub enum Field {
231    /// A boolean, one byte.
232    Bool(bool),
233    /// An unsigned byte.
234    U8(u8),
235    /// A signed byte.
236    I8(i8),
237    /// An unsigned 16-bit integer.
238    U16(u16),
239    /// A signed 16-bit integer.
240    I16(i16),
241    /// An unsigned 32-bit integer.
242    U32(u32),
243    /// A signed 32-bit integer.
244    I32(i32),
245    /// An unsigned 64-bit integer.
246    U64(u64),
247    /// A signed 64-bit integer.
248    I64(i64),
249    /// A 32-bit float.
250    F32(f32),
251    /// A 64-bit float.
252    F64(f64),
253    /// A `uoffset_t` to an object already written: a string, a vector or a
254    /// table.
255    Offset(Pos),
256}
257
258impl Field {
259    /// The bytes this value occupies in a table.
260    pub fn size(&self) -> usize {
261        match self {
262            Field::Bool(_) | Field::U8(_) | Field::I8(_) => 1,
263            Field::U16(_) | Field::I16(_) => 2,
264            Field::U32(_) | Field::I32(_) | Field::F32(_) | Field::Offset(_) => 4,
265            Field::U64(_) | Field::I64(_) | Field::F64(_) => 8,
266        }
267    }
268}
269
270/// One field of a table, at the slot and offset the projection assigned it.
271#[derive(Clone, Copy, Debug, PartialEq)]
272pub struct TableField {
273    /// The vtable slot, which is the field's position in declaration order.
274    pub slot: u16,
275    /// The field's offset from the start of the table.
276    pub offset: u16,
277    /// The value.
278    pub value: Field,
279}
280
281/// Builds a FlatBuffers buffer from the end of a caller's slice downwards.
282///
283/// A FlatBuffers buffer is built back to front: a child is written before the
284/// parent that names it, and the root offset is written last and sits at the
285/// start of the finished buffer. The builder therefore fills the slice from
286/// its end, and [`Builder::finish`] returns the finished buffer as a subslice
287/// of the slice ending at its end — which is why `Encoded.bytes` is a subslice
288/// rather than a prefix.
289///
290/// Nothing here allocates. The caller's slice is the only storage, and a write
291/// that does not fit returns [`EncodeError::Capacity`] with the bytes the
292/// encoding needed so far.
293pub struct Builder<'a> {
294    out: &'a mut [u8],
295    used: usize,
296}
297
298impl<'a> Builder<'a> {
299    /// A builder over `out`.
300    pub fn new(out: &'a mut [u8]) -> Self {
301        Builder { out, used: 0 }
302    }
303
304    /// The bytes written so far, including alignment padding.
305    pub fn used(&self) -> usize {
306        self.used
307    }
308
309    /// Reserves `size` bytes for an object aligned to `align`, and returns its
310    /// position and the bytes to fill, in address order.
311    ///
312    /// The reserved bytes and any padding are zeroed, so a buffer the caller
313    /// handed over dirty does not leak its old contents into the encoding and
314    /// two encodes of one value produce the same bytes.
315    pub fn reserve(&mut self, size: usize, align: usize) -> Result<(Pos, &mut [u8]), EncodeError> {
316        self.reserve_skewed(size, align, 0)
317    }
318
319    /// As [`Builder::reserve`], but puts the byte `skew` bytes into the object
320    /// on the `align` boundary, rather than its first byte.
321    ///
322    /// A vector needs this: its elements carry the alignment and they sit
323    /// behind a four-byte length prefix. Aligning the prefix instead would
324    /// leave a vector of eight-byte elements on a four-byte boundary, which
325    /// this builder's own reads survive — they copy before they read — and a
326    /// reader that loads in place does not.
327    fn reserve_skewed(
328        &mut self,
329        size: usize,
330        align: usize,
331        skew: usize,
332    ) -> Result<(Pos, &mut [u8]), EncodeError> {
333        debug_assert!(align.is_power_of_two(), "an alignment is a power of two");
334        let capacity = self.out.len();
335        let unpadded = match self.used.checked_add(size) {
336            Some(n) => n,
337            None => {
338                return Err(EncodeError::Capacity {
339                    needed: usize::MAX,
340                    available: capacity,
341                })
342            }
343        };
344        // An object at position `p` starts at offset `total - p` of the
345        // finished buffer, and `finish` makes `total` a multiple of the
346        // buffer's alignment. Padding so that `p` is congruent to `skew`
347        // modulo `align` therefore puts the object's byte `skew` on an
348        // `align` boundary in the finished buffer.
349        let padding = (align + (skew % align) - (unpadded % align)) % align;
350        let needed = match unpadded.checked_add(padding) {
351            Some(n) => n,
352            None => {
353                return Err(EncodeError::Capacity {
354                    needed: usize::MAX,
355                    available: capacity,
356                })
357            }
358        };
359        if needed > capacity {
360            return Err(EncodeError::Capacity {
361                needed,
362                available: capacity,
363            });
364        }
365        self.used = needed;
366        let start = capacity - needed;
367        self.out[start..start + size + padding].fill(0);
368        Ok((Pos(needed), &mut self.out[start..start + size]))
369    }
370
371    fn delta(from: Pos, to: Pos) -> usize {
372        debug_assert!(
373            to < from,
374            "a uoffset points forward, so its target is written before it and by this builder"
375        );
376        from.0.saturating_sub(to.0)
377    }
378}
379
380macro_rules! push_scalar {
381    ($name:ident, $ty:ty, $what:literal) => {
382        #[doc = concat!("Writes a little-endian `", $what, "`, aligned to its own size.")]
383        pub fn $name(&mut self, value: $ty) -> Result<Pos, EncodeError> {
384            let (position, bytes) = self.reserve(size_of::<$ty>(), size_of::<$ty>())?;
385            bytes.copy_from_slice(&value.to_le_bytes());
386            Ok(position)
387        }
388    };
389}
390
391impl<'a> Builder<'a> {
392    push_scalar!(push_u8, u8, "u8");
393    push_scalar!(push_i8, i8, "i8");
394    push_scalar!(push_u16, u16, "u16");
395    push_scalar!(push_i16, i16, "i16");
396    push_scalar!(push_u32, u32, "u32");
397    push_scalar!(push_i32, i32, "i32");
398    push_scalar!(push_u64, u64, "u64");
399    push_scalar!(push_i64, i64, "i64");
400    push_scalar!(push_f32, f32, "f32");
401    push_scalar!(push_f64, f64, "f64");
402
403    /// Writes a `uoffset_t` naming `target`.
404    pub fn push_offset(&mut self, target: Pos) -> Result<Pos, EncodeError> {
405        let (position, bytes) = self.reserve(OFFSET_SIZE, OFFSET_SIZE)?;
406        let delta = Builder::delta(position, target) as u32;
407        bytes.copy_from_slice(&delta.to_le_bytes());
408        Ok(position)
409    }
410
411    /// Writes a string: its length, its bytes, and the terminating zero a
412    /// FlatBuffers string carries so that a reader can hand it to C.
413    pub fn push_string(&mut self, value: &str) -> Result<Pos, EncodeError> {
414        let len = value.len();
415        let size = OFFSET_SIZE + len + 1;
416        let (position, bytes) = self.reserve(size, OFFSET_SIZE)?;
417        bytes[..OFFSET_SIZE].copy_from_slice(&(len as u32).to_le_bytes());
418        bytes[OFFSET_SIZE..OFFSET_SIZE + len].copy_from_slice(value.as_bytes());
419        // The terminator is already zero: `reserve` zeroes what it hands back.
420        Ok(position)
421    }
422
423    /// Writes a vector of scalars that the caller has already encoded, each
424    /// `stride` bytes, in element order.
425    ///
426    /// When `stride` is eight the buffer's own alignment must be at least
427    /// eight, so [`Builder::finish`] is passed eight or more; otherwise the
428    /// elements are aligned within the buffer and the buffer is not.
429    pub fn push_vector(&mut self, elements: &[u8], stride: usize) -> Result<Pos, EncodeError> {
430        debug_assert!(stride > 0, "an element occupies at least one byte");
431        debug_assert!(
432            elements.len() % stride == 0,
433            "the element bytes are a whole number of elements"
434        );
435        let count = elements.len() / stride;
436        let size = OFFSET_SIZE + elements.len();
437        // The elements carry the alignment, and they start `OFFSET_SIZE`
438        // bytes into the object, behind the length prefix. For a stride of
439        // four or less the prefix's own alignment already covers them.
440        let (align, skew) = if stride > OFFSET_SIZE {
441            (stride, OFFSET_SIZE)
442        } else {
443            (OFFSET_SIZE, 0)
444        };
445        let (position, bytes) = self.reserve_skewed(size, align, skew)?;
446        bytes[..OFFSET_SIZE].copy_from_slice(&(count as u32).to_le_bytes());
447        bytes[OFFSET_SIZE..].copy_from_slice(elements);
448        Ok(position)
449    }
450
451    /// Writes a vector of `uoffset_t`s naming objects already written, in
452    /// element order.
453    ///
454    /// A vector of strings, of tables, or of anything else the projection
455    /// places out of line is written this way: the elements are pushed first,
456    /// and their positions are handed over here. Each offset is relative to
457    /// its own position in the vector, which is why the caller cannot compute
458    /// them itself — a [`Pos`] carries no arithmetic outside this module.
459    pub fn push_offset_vector(&mut self, targets: &[Pos]) -> Result<Pos, EncodeError> {
460        let capacity = self.out.len();
461        let count = targets.len();
462        let size = match count
463            .checked_add(1)
464            .and_then(|slots| slots.checked_mul(OFFSET_SIZE))
465        {
466            Some(size) => size,
467            None => {
468                return Err(EncodeError::Capacity {
469                    needed: usize::MAX,
470                    available: capacity,
471                })
472            }
473        };
474        let (position, bytes) = self.reserve(size, OFFSET_SIZE)?;
475        bytes[..OFFSET_SIZE].copy_from_slice(&(count as u32).to_le_bytes());
476        for (index, target) in targets.iter().enumerate() {
477            let at = OFFSET_SIZE * (index + 1);
478            // Element `index` sits `at` bytes into the object, and so that
479            // many bytes closer to the end of the buffer than the object's
480            // own position.
481            let here = Pos(position.0 - at);
482            let delta = Builder::delta(here, *target) as u32;
483            bytes[at..at + OFFSET_SIZE].copy_from_slice(&delta.to_le_bytes());
484        }
485        Ok(position)
486    }
487
488    /// Writes a table and the vtable that describes it.
489    ///
490    /// `size`, `align`, `slots` and each field's `offset` are the projection's
491    /// facts about the type, not this builder's: it writes the layout it is
492    /// given. `slots` is the number of vtable entries, which is the number of
493    /// fields the type declares, whether or not each one is present here.
494    ///
495    /// A field the caller leaves out of `fields` is absent from the buffer,
496    /// which is how a FlatBuffers table carries a field equal to its default.
497    pub fn push_table(
498        &mut self,
499        size: usize,
500        align: usize,
501        slots: u16,
502        fields: &[TableField],
503    ) -> Result<Pos, EncodeError> {
504        debug_assert!(
505            size >= OFFSET_SIZE,
506            "a table begins with the offset to its vtable"
507        );
508        // A vtable states its own size and its table's size as `u16`, so a
509        // table larger than `u16::MAX` is not representable. The projection
510        // guarantees it does not happen: a type whose table does not fit has
511        // no finite size bound, and the Rust backend refuses it at generation time
512        // with a diagnostic. This assertion catches a projection that stops
513        // upholding that.
514        debug_assert!(
515            size <= usize::from(u16::MAX),
516            "a table's size is stated in its vtable as a u16"
517        );
518        let table = {
519            let (table, bytes) = self.reserve(size, align)?;
520            for f in fields {
521                let from = usize::from(f.offset);
522                let width = f.value.size();
523                debug_assert!(
524                    from >= OFFSET_SIZE && from + width <= size,
525                    "a field lies inside the table and after the vtable offset"
526                );
527                let slot = &mut bytes[from..from + width];
528                match f.value {
529                    Field::Bool(v) => slot.copy_from_slice(&u8::from(v).to_le_bytes()),
530                    Field::U8(v) => slot.copy_from_slice(&v.to_le_bytes()),
531                    Field::I8(v) => slot.copy_from_slice(&v.to_le_bytes()),
532                    Field::U16(v) => slot.copy_from_slice(&v.to_le_bytes()),
533                    Field::I16(v) => slot.copy_from_slice(&v.to_le_bytes()),
534                    Field::U32(v) => slot.copy_from_slice(&v.to_le_bytes()),
535                    Field::I32(v) => slot.copy_from_slice(&v.to_le_bytes()),
536                    Field::U64(v) => slot.copy_from_slice(&v.to_le_bytes()),
537                    Field::I64(v) => slot.copy_from_slice(&v.to_le_bytes()),
538                    Field::F32(v) => slot.copy_from_slice(&v.to_le_bytes()),
539                    Field::F64(v) => slot.copy_from_slice(&v.to_le_bytes()),
540                    Field::Offset(target) => {
541                        // The field's own position: the table starts at
542                        // `table`, and a field `offset` bytes further along is
543                        // that many bytes closer to the end of the buffer.
544                        let here = Pos(table.0 - from);
545                        let delta = Builder::delta(here, target) as u32;
546                        slot.copy_from_slice(&delta.to_le_bytes());
547                    }
548                }
549            }
550            table
551        };
552
553        let vtable_bytes = VTABLE_HEADER + usize::from(slots) * VOFFSET_SIZE;
554        debug_assert!(
555            vtable_bytes <= usize::from(u16::MAX),
556            "a vtable's size is stated in its first field as a u16"
557        );
558        let vtable = {
559            let (vtable, bytes) = self.reserve(vtable_bytes, VOFFSET_SIZE)?;
560            bytes[..VOFFSET_SIZE].copy_from_slice(&(vtable_bytes as u16).to_le_bytes());
561            bytes[VOFFSET_SIZE..VTABLE_HEADER].copy_from_slice(&(size as u16).to_le_bytes());
562            for f in fields {
563                debug_assert!(f.slot < slots, "a field's slot is one the vtable carries");
564                let entry = VTABLE_HEADER + usize::from(f.slot) * VOFFSET_SIZE;
565                bytes[entry..entry + VOFFSET_SIZE].copy_from_slice(&f.offset.to_le_bytes());
566            }
567            vtable
568        };
569
570        // The table names its vtable with a signed offset backwards. The
571        // vtable was written after the table, so it lies at a lower address
572        // and the offset is positive.
573        let soffset = (vtable.0 - table.0) as i32;
574        let start = self.out.len() - table.0;
575        self.out[start..start + OFFSET_SIZE].copy_from_slice(&soffset.to_le_bytes());
576        Ok(table)
577    }
578
579    /// Writes the root offset and returns the finished buffer: a subslice of
580    /// the caller's slice ending at its end.
581    ///
582    /// `align` is the buffer's alignment, the largest any object in it needs.
583    /// The finished buffer's length is a multiple of it, which is what makes
584    /// every object's own alignment hold once the root sits at offset zero.
585    pub fn finish(mut self, root: Pos, align: usize) -> Result<&'a [u8], EncodeError> {
586        let align = if align > OFFSET_SIZE {
587            align
588        } else {
589            OFFSET_SIZE
590        };
591        let position = {
592            let (position, bytes) = self.reserve(OFFSET_SIZE, align)?;
593            let delta = Builder::delta(position, root) as u32;
594            bytes.copy_from_slice(&delta.to_le_bytes());
595            position
596        };
597        let out: &'a [u8] = self.out;
598        Ok(&out[out.len() - position.0..])
599    }
600}