Skip to main content

links_notation/binary/
packet.rs

1//! Binary links notation: a self-delimiting packet of links.
2//!
3//! A packet stores links, each a tuple of one or more references, at
4//! implicit consecutive addresses. It knows nothing about LiNo; the LiNo
5//! mapping ([`crate::binary::encode_document`]) is one use of it.
6//!
7//! ```text
8//! byte 0     0x10 | flags      high nibble 1 = format version 1
9//!                              bit 0     external references (Hybrid encoding)
10//!                              bit 1     explicit layout
11//!                              bits 2-3  log2 of the width (compact layout only)
12//!
13//! compact layout (bit 1 clear): one section of doublets right after the markers
14//! LEB128     N                 number of links, each `source target`
15//!
16//! explicit layout (bit 1 set):
17//! LEB128     S                 number of sections, then S section headers:
18//! LEB128     shape             bits 0-1  log2 of the reference width in bytes
19//!                              bit 2     a gap follows
20//!                              bit 3     variable arity (else every link
21//!                                        holds exactly min_arity references)
22//!                              bits 4+   min_arity, at least 1
23//! LEB128     gap               addresses skipped before the section (if bit 2)
24//! LEB128     extra_arity       0 = no maximum, else max - min (if bit 3)
25//! LEB128     count             number of links in the section
26//!
27//! links, section by section; a link in a variable-arity section starts
28//! with LEB128 (length - min_arity); every reference is `width` bytes,
29//! little-endian
30//! ```
31//!
32//! Address `0` is null. The first section starts at `1 + gap` and every
33//! other section at `previous end + gap`, so gaps leave holes. The compact
34//! layout is exactly one section with gap 5 (the LiNo marker points
35//! `1..=5`), arity 2 and the header width.
36//!
37//! Each section has its own width: the narrowest of 1, 2, 4 and 8 bytes
38//! that holds every reference in it, so links that only refer to small
39//! addresses stay small wherever they live. [`LinksPacket::pack`] chooses
40//! the sections.
41//!
42//! With external references enabled the top bit of a reference marks it as
43//! external, exactly like `Platform.Data.Hybrid<T>`: value `v ≥ 1` is stored as
44//! the two's-complement negation `2^bits - v` and value `0` as `2^(bits-1)`.
45//! That halves the internal range of every width (`0..128` for 8-bit, …).
46
47use super::error::{BinaryError, BinaryResult};
48use std::fmt;
49use std::io::{self, Read, Write};
50use std::str::FromStr;
51
52/// The high nibble of the header byte. Text messages never start with a byte
53/// in `0x10..=0x1F`, so the header doubles as a protocol detector.
54pub const BINARY_VERSION_1: u8 = 0x10;
55
56const FLAG_EXTERNAL_REFERENCES: u8 = 0b0001;
57const FLAG_EXPLICIT_LAYOUT: u8 = 0b0010;
58const WIDTH_SHIFT: u8 = 2;
59const WIDTH_BITS: u8 = 0b1100;
60
61const SHAPE_WIDTH_BITS: u64 = 0b0011;
62const SHAPE_HAS_GAP: u64 = 0b0100;
63const SHAPE_VARIABLE_ARITY: u64 = 0b1000;
64const SHAPE_MIN_ARITY_SHIFT: u32 = 4;
65
66/// Null link address.
67pub const NULL: u64 = 0;
68/// Marker point `1`: the unary *one*; powers of two are `2^k = (2^(k-1) 2^(k-1))`.
69pub const ONE: u64 = 1;
70/// Marker point `2`: `(Number unary)` is a non-negative integer.
71pub const NUMBER: u64 = 2;
72/// Marker point `3`: `(String code points…)` is a Unicode string.
73pub const STRING: u64 = 3;
74/// Marker point `4`: `(List elements…)` is a list of links.
75pub const LIST: u64 = 4;
76/// Marker point `5`: `(Identified id values…)` is a link with an id.
77pub const IDENTIFIED: u64 = 5;
78/// Address of the first link after the marker points.
79pub const FIRST_LINK_ADDRESS: u64 = 6;
80
81/// The addresses the compact layout skips: the marker points.
82const COMPACT_GAP: u64 = FIRST_LINK_ADDRESS - 1;
83
84/// The reference widths, in bytes, that a packet may use.
85pub const WIDTHS: [u8; 4] = [1, 2, 4, 8];
86
87/// One reference inside a packet.
88#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
89pub enum Reference {
90    /// A link address (`0` is null).
91    Internal(u64),
92    /// An external value, e.g. a number or a Unicode code point.
93    External(u64),
94}
95
96impl Reference {
97    /// The null reference.
98    pub const NULL: Reference = Reference::Internal(NULL);
99}
100
101/// How many references the links of a section hold: `min..=max`, where
102/// `max = None` means no upper bound.
103#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
104pub struct ArityRange {
105    /// Fewest references in a link, at least 1.
106    pub min: u64,
107    /// Most references in a link, `None` for no limit.
108    pub max: Option<u64>,
109}
110
111impl ArityRange {
112    /// Links of exactly two references.
113    pub const DOUBLETS: ArityRange = ArityRange::exactly(2);
114
115    /// Links of exactly `arity` references.
116    pub const fn exactly(arity: u64) -> Self {
117        Self {
118            min: arity,
119            max: Some(arity),
120        }
121    }
122
123    /// Links of at least `min` references.
124    pub const fn at_least(min: u64) -> Self {
125        Self { min, max: None }
126    }
127
128    /// Links of `min..=max` references.
129    pub const fn between(min: u64, max: u64) -> Self {
130        Self {
131            min,
132            max: Some(max),
133        }
134    }
135
136    /// True when a link of `length` references fits the range.
137    pub fn contains(&self, length: u64) -> bool {
138        length >= self.min && self.max.is_none_or(|max| length <= max)
139    }
140
141    /// True when every link has the same number of references, so links
142    /// need no length prefix.
143    pub fn is_fixed(&self) -> bool {
144        self.max == Some(self.min)
145    }
146
147    pub(crate) fn validate(&self) -> Result<(), String> {
148        if self.min == 0 {
149            return Err("arity must be at least 1".into());
150        }
151        if self.min > u64::MAX >> SHAPE_MIN_ARITY_SHIFT {
152            return Err(format!("arity {} is too large", self.min));
153        }
154        if self.max.is_some_and(|max| max < self.min) {
155            return Err(format!("arity range {self} is empty"));
156        }
157        Ok(())
158    }
159
160    /// `0` for no maximum, otherwise `max - min`; only for variable arities.
161    fn extra(&self) -> u64 {
162        self.max.map_or(0, |max| max - self.min)
163    }
164
165    fn from_shape(min: u64, extra: Option<u64>) -> BinaryResult<Self> {
166        let range = match extra {
167            None => Self::exactly(min),
168            Some(0) => Self::at_least(min),
169            Some(extra) => Self::between(
170                min,
171                min.checked_add(extra)
172                    .ok_or_else(|| BinaryError::malformed("arity range overflows 64 bits"))?,
173            ),
174        };
175        range.validate().map_err(BinaryError::malformed)?;
176        Ok(range)
177    }
178}
179
180impl Default for ArityRange {
181    fn default() -> Self {
182        Self::DOUBLETS
183    }
184}
185
186impl fmt::Display for ArityRange {
187    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
188        match self.max {
189            Some(max) if max == self.min => write!(formatter, "{max}"),
190            Some(max) => write!(formatter, "{}..{max}", self.min),
191            None => write!(formatter, "{}..", self.min),
192        }
193    }
194}
195
196impl FromStr for ArityRange {
197    type Err = String;
198
199    /// Parses `n`, `min..max` (inclusive) or `min..` (no maximum).
200    fn from_str(text: &str) -> Result<Self, Self::Err> {
201        let number = |part: &str| {
202            part.parse::<u64>()
203                .map_err(|_| format!("invalid arity '{text}': expected n, min..max or min.."))
204        };
205        let range = match text.split_once("..") {
206            None => Self::exactly(number(text)?),
207            Some((min, "")) => Self::at_least(number(min)?),
208            Some((min, max)) => Self::between(number(min)?, number(max)?),
209        };
210        range.validate()?;
211        Ok(range)
212    }
213}
214
215/// Safety limits applied while decoding untrusted input.
216#[derive(Clone, Copy, Debug, PartialEq, Eq)]
217pub struct DecodeLimits {
218    /// Maximum number of links in a packet.
219    pub max_links: u64,
220    /// Maximum number of references in all links of a packet.
221    pub max_references: u64,
222    /// Maximum number of LiNo nodes a packet may expand to.
223    pub max_nodes: usize,
224    /// Maximum total UTF-8 bytes in expanded references, including link ids.
225    pub max_string_bytes: usize,
226    /// Maximum LiNo nesting depth.
227    pub max_depth: usize,
228}
229
230impl Default for DecodeLimits {
231    fn default() -> Self {
232        Self {
233            max_links: 1 << 22,
234            max_references: 1 << 24,
235            max_nodes: 1 << 22,
236            max_string_bytes: 64 << 20,
237            max_depth: 64,
238        }
239    }
240}
241
242impl DecodeLimits {
243    /// Limits for trusted input such as a store archive: only the address
244    /// space bounds the packet.
245    pub fn unlimited() -> Self {
246        Self {
247            max_links: u64::MAX,
248            max_references: u64::MAX,
249            max_nodes: usize::MAX,
250            max_string_bytes: usize::MAX,
251            max_depth: usize::MAX,
252        }
253    }
254}
255
256/// A run of links at consecutive addresses sharing an arity range and a
257/// reference width.
258#[derive(Clone, Debug, PartialEq, Eq)]
259pub struct Section {
260    /// Addresses skipped before the first link of the section.
261    pub gap: u64,
262    /// The number of references each link may hold.
263    pub arity: ArityRange,
264    /// Bytes per reference: 1, 2, 4 or 8.
265    pub width: u8,
266    /// The links, in address order.
267    pub links: Vec<Vec<Reference>>,
268}
269
270/// A decoded binary links packet.
271#[derive(Clone, Debug, Default, PartialEq, Eq)]
272pub struct LinksPacket {
273    /// Header bit 0: references may be external (Hybrid encoding).
274    pub external_references: bool,
275    /// The links, section by section.
276    pub sections: Vec<Section>,
277}
278
279/// Largest internal address that fits in `width` bytes.
280pub fn internal_capacity(width: u8, external_references: bool) -> u64 {
281    let bits = u32::from(width) * 8 - u32::from(external_references);
282    if bits >= 64 {
283        u64::MAX
284    } else {
285        (1u64 << bits) - 1
286    }
287}
288
289/// Largest external value that fits in `width` bytes.
290pub fn external_capacity(width: u8) -> u64 {
291    (1u64 << (u32::from(width) * 8 - 1)) - 1
292}
293
294/// The narrowest width able to hold the internal address `address`.
295pub fn address_tier(address: u64, external_references: bool) -> BinaryResult<u8> {
296    WIDTHS
297        .into_iter()
298        .find(|&width| internal_capacity(width, external_references) >= address)
299        .ok_or_else(|| {
300            BinaryError::Unencodable(format!("address {address} exceeds the internal range"))
301        })
302}
303
304fn width_mask(width: u8) -> u64 {
305    internal_capacity(width, false)
306}
307
308/// Encodes an external value at `width` the way `Platform.Data.Hybrid<T>` does.
309pub fn encode_external(value: u64, width: u8) -> u64 {
310    if value == 0 {
311        1u64 << (u32::from(width) * 8 - 1)
312    } else {
313        value.wrapping_neg() & width_mask(width)
314    }
315}
316
317/// Decodes a raw `width`-byte value, returning `Some(value)` for externals.
318pub fn decode_external(raw: u64, width: u8) -> Option<u64> {
319    let external_zero = 1u64 << (u32::from(width) * 8 - 1);
320    if raw == external_zero {
321        Some(0)
322    } else if raw > external_zero {
323        Some(raw.wrapping_neg() & width_mask(width))
324    } else {
325        None
326    }
327}
328
329fn width_code(width: u8) -> BinaryResult<u8> {
330    WIDTHS
331        .iter()
332        .position(|&candidate| candidate == width)
333        .map(|code| code as u8)
334        .ok_or_else(|| BinaryError::Unencodable(format!("invalid width {width}")))
335}
336
337/// The width of a two-bit width code; every code names a width.
338fn width_from_code(code: u64) -> u8 {
339    WIDTHS[(code & 0b11) as usize]
340}
341
342/// The narrowest width able to hold `reference`.
343pub fn reference_width(reference: Reference, external_references: bool) -> BinaryResult<u8> {
344    match reference {
345        Reference::Internal(address) => address_tier(address, external_references),
346        Reference::External(value) => {
347            if !external_references {
348                return Err(BinaryError::Unencodable(
349                    "external reference in a packet without external references".into(),
350                ));
351            }
352            WIDTHS
353                .into_iter()
354                .find(|&width| external_capacity(width) >= value)
355                .ok_or_else(|| {
356                    BinaryError::Unencodable(format!("external value {value} exceeds 63 bits"))
357                })
358        }
359    }
360}
361
362impl Section {
363    fn write_header(&self, out: &mut Vec<u8>) -> BinaryResult<()> {
364        let mut shape =
365            u64::from(width_code(self.width)?) | (self.arity.min << SHAPE_MIN_ARITY_SHIFT);
366        if self.gap != 0 {
367            shape |= SHAPE_HAS_GAP;
368        }
369        if !self.arity.is_fixed() {
370            shape |= SHAPE_VARIABLE_ARITY;
371        }
372        write_leb128(out, shape);
373        if self.gap != 0 {
374            write_leb128(out, self.gap);
375        }
376        if !self.arity.is_fixed() {
377            write_leb128(out, self.arity.extra());
378        }
379        write_leb128(out, self.links.len() as u64);
380        Ok(())
381    }
382
383    /// Reads a section header, returning the still empty section and its
384    /// link count.
385    fn read_header(reader: &mut dyn Read) -> BinaryResult<(Self, u64)> {
386        let shape = read_leb128(reader)?;
387        let width = width_from_code(shape & SHAPE_WIDTH_BITS);
388        let gap = if shape & SHAPE_HAS_GAP != 0 {
389            read_leb128(reader)?
390        } else {
391            0
392        };
393        let extra = if shape & SHAPE_VARIABLE_ARITY != 0 {
394            Some(read_leb128(reader)?)
395        } else {
396            None
397        };
398        let arity = ArityRange::from_shape(shape >> SHAPE_MIN_ARITY_SHIFT, extra)?;
399        let count = read_leb128(reader)?;
400        let section = Section {
401            gap,
402            arity,
403            width,
404            links: Vec::new(),
405        };
406        Ok((section, count))
407    }
408
409    fn validate(&self) -> BinaryResult<()> {
410        width_code(self.width)?;
411        self.arity.validate().map_err(BinaryError::Unencodable)?;
412        for link in &self.links {
413            if !self.arity.contains(link.len() as u64) {
414                return Err(BinaryError::Unencodable(format!(
415                    "a link of {} references in a section of arity {}",
416                    link.len(),
417                    self.arity
418                )));
419            }
420        }
421        Ok(())
422    }
423}
424
425impl LinksPacket {
426    /// An empty packet.
427    pub fn new(external_references: bool) -> Self {
428        Self {
429            external_references,
430            sections: Vec::new(),
431        }
432    }
433
434    /// Lays out `links` — `(address, references)` in ascending address
435    /// order — using the version 1 section planner.
436    ///
437    /// Holes between addresses start new sections. Without `packed_widths`
438    /// every section uses the width of the widest reference, so all
439    /// references have the same size. With it each section gets the
440    /// narrowest width its links need and sections split wherever that saves
441    /// bytes; the result is never larger than the uniform one.
442    pub fn pack(
443        external_references: bool,
444        links: &[(u64, Vec<Reference>)],
445        packed_widths: bool,
446    ) -> BinaryResult<Self> {
447        let planner = SectionPlanner::new(external_references, links)?;
448        let uniform_layout = planner.plan(false);
449        let uniform = Self::lay_out(external_references, links, &uniform_layout);
450        if !packed_widths {
451            return Ok(uniform);
452        }
453        let packed_layout = planner.plan(true);
454        if packed_layout == uniform_layout {
455            return Ok(uniform);
456        }
457        let packed = Self::lay_out(external_references, links, &packed_layout);
458        Ok(if packed.to_bytes()?.len() < uniform.to_bytes()?.len() {
459            packed
460        } else {
461            uniform
462        })
463    }
464
465    /// Splits `links` into sections of `(link count, width)`.
466    fn lay_out(
467        external_references: bool,
468        links: &[(u64, Vec<Reference>)],
469        layout: &[(usize, u8)],
470    ) -> Self {
471        let mut sections = Vec::with_capacity(layout.len());
472        let mut next_address = 1u64;
473        let mut remaining = links;
474        for &(count, width) in layout {
475            let (members, rest) = remaining.split_at(count);
476            remaining = rest;
477            let start = members[0].0;
478            let (shortest, longest) = members
479                .iter()
480                .map(|(_, link)| link.len() as u64)
481                .fold((u64::MAX, 0), |(shortest, longest), length| {
482                    (shortest.min(length), longest.max(length))
483                });
484            sections.push(Section {
485                gap: start - next_address,
486                arity: ArityRange::between(shortest, longest),
487                width,
488                links: members.iter().map(|(_, link)| link.clone()).collect(),
489            });
490            next_address = start + count as u64;
491        }
492        Self {
493            external_references,
494            sections,
495        }
496    }
497
498    /// Every link with its address, in address order.
499    pub fn links(&self) -> impl Iterator<Item = (u64, &[Reference])> + '_ {
500        let mut next_address = 1u64;
501        self.sections.iter().flat_map(move |section| {
502            let start = next_address.saturating_add(section.gap);
503            next_address = start.saturating_add(section.links.len() as u64);
504            section
505                .links
506                .iter()
507                .enumerate()
508                .map(move |(index, link)| (start + index as u64, link.as_slice()))
509        })
510    }
511
512    /// The number of links in the packet.
513    pub fn link_count(&self) -> u64 {
514        self.sections
515            .iter()
516            .map(|section| section.links.len() as u64)
517            .sum()
518    }
519
520    pub(crate) fn validate(&self) -> BinaryResult<()> {
521        let mut next_address = 1u64;
522        for section in &self.sections {
523            section.validate()?;
524            next_address = next_address
525                .checked_add(section.gap)
526                .and_then(|start| start.checked_add(section.links.len() as u64))
527                .ok_or_else(|| BinaryError::Unencodable("addresses overflow 64 bits".into()))?;
528            for link in &section.links {
529                for &reference in link {
530                    self.raw_reference(reference, section.width)?;
531                }
532            }
533        }
534        Ok(())
535    }
536
537    fn is_compact(&self) -> bool {
538        match self.sections.as_slice() {
539            [] => true,
540            [only] => {
541                only.gap == COMPACT_GAP
542                    && only.arity == ArityRange::DOUBLETS
543                    && !only.links.is_empty()
544            }
545            _ => false,
546        }
547    }
548
549    /// Serializes the packet.
550    pub fn to_bytes(&self) -> BinaryResult<Vec<u8>> {
551        let mut bytes = Vec::new();
552        self.write_to(&mut bytes)?;
553        Ok(bytes)
554    }
555
556    /// Writes the packet to `writer`.
557    pub fn write_to(&self, writer: &mut dyn Write) -> BinaryResult<()> {
558        let mut header = BINARY_VERSION_1;
559        if self.external_references {
560            header |= FLAG_EXTERNAL_REFERENCES;
561        }
562        let mut out = Vec::new();
563        self.validate()?;
564        if self.is_compact() {
565            let width = self.sections.first().map_or(1, |section| section.width);
566            header |= width_code(width)? << WIDTH_SHIFT;
567            out.push(header);
568            write_leb128(&mut out, self.link_count());
569        } else {
570            out.push(header | FLAG_EXPLICIT_LAYOUT);
571            write_leb128(&mut out, self.sections.len() as u64);
572            for section in &self.sections {
573                section.write_header(&mut out)?;
574            }
575        }
576        for section in &self.sections {
577            for link in &section.links {
578                if !section.arity.is_fixed() {
579                    write_leb128(&mut out, link.len() as u64 - section.arity.min);
580                }
581                for &reference in link {
582                    write_raw(
583                        &mut out,
584                        self.raw_reference(reference, section.width)?,
585                        section.width,
586                    );
587                }
588            }
589        }
590        writer.write_all(&out)?;
591        Ok(())
592    }
593
594    fn raw_reference(&self, reference: Reference, width: u8) -> BinaryResult<u64> {
595        if reference_width(reference, self.external_references)? > width {
596            return Err(BinaryError::Unencodable(format!(
597                "{reference:?} does not fit {width} byte(s)"
598            )));
599        }
600        Ok(match reference {
601            Reference::Internal(address) => address,
602            Reference::External(value) => encode_external(value, width),
603        })
604    }
605
606    /// Parses a complete packet; trailing bytes are an error.
607    pub fn from_bytes(bytes: &[u8], limits: &DecodeLimits) -> BinaryResult<Self> {
608        let mut cursor = bytes;
609        let packet = Self::read_from(&mut cursor, limits)?
610            .ok_or_else(|| BinaryError::malformed("empty input"))?;
611        if !cursor.is_empty() {
612            return Err(BinaryError::malformed("trailing bytes after the packet"));
613        }
614        Ok(packet)
615    }
616
617    /// Reads one packet from `reader`; `Ok(None)` on a clean end of stream.
618    pub fn read_from(reader: &mut dyn Read, limits: &DecodeLimits) -> BinaryResult<Option<Self>> {
619        let Some(header) = read_byte_or_eof(reader)? else {
620            return Ok(None);
621        };
622        if header & 0xF0 != BINARY_VERSION_1 {
623            return Err(BinaryError::malformed(format!(
624                "unsupported binary header byte 0x{header:02X}"
625            )));
626        }
627        let mut packet = LinksPacket::new(header & FLAG_EXTERNAL_REFERENCES != 0);
628        let mut counts = Vec::new();
629        if header & FLAG_EXPLICIT_LAYOUT == 0 {
630            let width = width_from_code(u64::from((header & WIDTH_BITS) >> WIDTH_SHIFT));
631            let count = read_leb128(reader)?;
632            if count > u64::MAX - FIRST_LINK_ADDRESS {
633                return Err(BinaryError::malformed("addresses overflow 64 bits"));
634            }
635            if count > 0 {
636                packet.sections.push(Section {
637                    gap: COMPACT_GAP,
638                    arity: ArityRange::DOUBLETS,
639                    width,
640                    links: Vec::new(),
641                });
642                counts.push(count);
643            }
644        } else {
645            if header & WIDTH_BITS != 0 {
646                return Err(BinaryError::malformed(
647                    "the explicit layout keeps the header width bits clear",
648                ));
649            }
650            let section_count = read_leb128(reader)?;
651            if section_count > limits.max_links {
652                return Err(Self::too_many_links(limits));
653            }
654            let mut next_address = 1u64;
655            for _ in 0..section_count {
656                let (section, count) = Section::read_header(reader)?;
657                next_address = next_address
658                    .checked_add(section.gap)
659                    .and_then(|start| start.checked_add(count))
660                    .ok_or_else(|| BinaryError::malformed("addresses overflow 64 bits"))?;
661                packet.sections.push(section);
662                counts.push(count);
663            }
664        }
665        counts
666            .iter()
667            .try_fold(0u64, |total, &count| total.checked_add(count))
668            .filter(|&total| total <= limits.max_links)
669            .ok_or_else(|| Self::too_many_links(limits))?;
670        let mut references_left = limits.max_references;
671        let external_references = packet.external_references;
672        for (section, count) in packet.sections.iter_mut().zip(counts) {
673            section.links.reserve(count.min(4096) as usize);
674            for _ in 0..count {
675                let length = if section.arity.is_fixed() {
676                    section.arity.min
677                } else {
678                    let length = read_leb128(reader)?
679                        .checked_add(section.arity.min)
680                        .filter(|&length| section.arity.contains(length))
681                        .ok_or_else(|| {
682                            BinaryError::malformed(format!(
683                                "link length outside the section arity {}",
684                                section.arity
685                            ))
686                        })?;
687                    length
688                };
689                references_left = references_left.checked_sub(length).ok_or_else(|| {
690                    BinaryError::LimitExceeded(format!(
691                        "references exceed the limit of {}",
692                        limits.max_references
693                    ))
694                })?;
695                let mut link = Vec::with_capacity(length.min(4096) as usize);
696                for _ in 0..length {
697                    let raw = read_raw(reader, section.width)?;
698                    link.push(
699                        match decode_external(raw, section.width).filter(|_| external_references) {
700                            Some(value) => Reference::External(value),
701                            None => Reference::Internal(raw),
702                        },
703                    );
704                }
705                section.links.push(link);
706            }
707        }
708        Ok(Some(packet))
709    }
710
711    fn too_many_links(limits: &DecodeLimits) -> BinaryError {
712        BinaryError::LimitExceeded(format!(
713            "packet declares more than {} links",
714            limits.max_links
715        ))
716    }
717}
718
719/// Estimated bytes of a section header (shape and count), used to weigh a split.
720const SECTION_HEADER_ESTIMATE: u64 = 2;
721/// Estimated extra bytes of a variable-arity section: its `extra_arity`
722/// header field, and the length prefix of each link.
723const VARIABLE_ARITY_ESTIMATE: u64 = 1;
724const UNREACHABLE: u64 = u64::MAX / 4;
725/// Planner states: a width code (0..4) times fixed (0) or variable (1) arity.
726const STATES: usize = WIDTHS.len() * 2;
727
728/// Splits links into sections with a linear dynamic program.
729///
730/// After link `k`, `cost[s]` is the fewest estimated bytes for links
731/// `0..=k` with link `k` in a section of state `s`. A link either continues
732/// the section of the previous link (same state, no hole between them and,
733/// for a fixed arity, the same length) or opens a new section after the
734/// cheapest previous state, paying for a section header.
735struct SectionPlanner<'a> {
736    links: &'a [(u64, Vec<Reference>)],
737    needs: Vec<u8>,
738}
739
740impl<'a> SectionPlanner<'a> {
741    fn new(external_references: bool, links: &'a [(u64, Vec<Reference>)]) -> BinaryResult<Self> {
742        let mut needs = Vec::with_capacity(links.len());
743        let mut previous_address = 0u64;
744        for (address, link) in links {
745            if *address <= previous_address {
746                return Err(BinaryError::Unencodable(format!(
747                    "link addresses must ascend from 1, got {address} after {previous_address}"
748                )));
749            }
750            if *address == u64::MAX {
751                return Err(BinaryError::Unencodable(
752                    "addresses overflow 64 bits".into(),
753                ));
754            }
755            if link.is_empty() {
756                return Err(BinaryError::Unencodable(format!(
757                    "link {address} has no references"
758                )));
759            }
760            previous_address = *address;
761            let mut need = 1u8;
762            for &reference in link {
763                need = need.max(reference_width(reference, external_references)?);
764            }
765            needs.push(need);
766        }
767        Ok(Self { links, needs })
768    }
769
770    fn first_cheapest(costs: &[u64; STATES]) -> usize {
771        let mut best = 0;
772        for state in 1..STATES {
773            if costs[state] < costs[best] {
774                best = state;
775            }
776        }
777        best
778    }
779
780    /// The sections as `(link count, width)` pairs; with `packed_widths`
781    /// every width may be used, otherwise only the widest one needed.
782    fn plan(&self, packed_widths: bool) -> Vec<(usize, u8)> {
783        if self.links.is_empty() {
784            return Vec::new();
785        }
786        let widest = self.needs.iter().copied().max().unwrap_or(1);
787        let allowed_widths = WIDTHS.map(|width| packed_widths || width == widest);
788        let mut opens_section = vec![0u8; self.links.len()];
789        let mut previous_best = vec![0u8; self.links.len()];
790        let mut costs = [UNREACHABLE; STATES];
791        for (index, (address, link)) in self.links.iter().enumerate() {
792            let best = Self::first_cheapest(&costs);
793            previous_best[index] = best as u8;
794            let cheapest_before = if index == 0 { 0 } else { costs[best] };
795            let continues = index > 0 && self.links[index - 1].0 + 1 == *address;
796            let same_length = index > 0 && self.links[index - 1].1.len() == link.len();
797            let mut next = [UNREACHABLE; STATES];
798            for state in 0..STATES {
799                let width_index = state / 2;
800                let variable = state % 2 == 1;
801                let width = WIDTHS[width_index];
802                if !allowed_widths[width_index] || width < self.needs[index] {
803                    continue;
804                }
805                let variable_estimate = if variable { VARIABLE_ARITY_ESTIMATE } else { 0 };
806                let body = link.len() as u64 * u64::from(width) + variable_estimate;
807                let opening_cost = cheapest_before + SECTION_HEADER_ESTIMATE + variable_estimate;
808                let continuing_cost = if continues && (variable || same_length) {
809                    costs[state]
810                } else {
811                    UNREACHABLE
812                };
813                if continuing_cost <= opening_cost {
814                    next[state] = continuing_cost + body;
815                } else {
816                    next[state] = opening_cost + body;
817                    opens_section[index] |= 1 << state;
818                }
819            }
820            costs = next;
821        }
822        let mut sections = Vec::new();
823        let mut state = Self::first_cheapest(&costs);
824        let mut end = self.links.len();
825        for index in (0..self.links.len()).rev() {
826            if opens_section[index] & (1 << state) != 0 {
827                sections.push((end - index, WIDTHS[state / 2]));
828                end = index;
829                state = usize::from(previous_best[index]);
830            }
831        }
832        sections.reverse();
833        sections
834    }
835}
836
837fn write_raw(out: &mut Vec<u8>, value: u64, width: u8) {
838    out.extend_from_slice(&value.to_le_bytes()[..usize::from(width)]);
839}
840
841fn read_raw(reader: &mut dyn Read, width: u8) -> BinaryResult<u64> {
842    let mut bytes = [0u8; 8];
843    read_exact(reader, &mut bytes[..usize::from(width)])?;
844    Ok(u64::from_le_bytes(bytes))
845}
846
847/// Appends `value` as unsigned LEB128.
848pub fn write_leb128(out: &mut Vec<u8>, mut value: u64) {
849    loop {
850        let byte = (value & 0x7F) as u8;
851        value >>= 7;
852        if value == 0 {
853            out.push(byte);
854            return;
855        }
856        out.push(byte | 0x80);
857    }
858}
859
860/// Reads an unsigned LEB128 value of at most 64 bits.
861pub fn read_leb128(reader: &mut dyn Read) -> BinaryResult<u64> {
862    let mut value = 0u64;
863    for shift in (0..64).step_by(7) {
864        let mut byte = [0u8; 1];
865        read_exact(reader, &mut byte)?;
866        let payload = u64::from(byte[0] & 0x7F);
867        if shift == 63 && payload > 1 {
868            return Err(BinaryError::malformed("LEB128 value overflows 64 bits"));
869        }
870        value |= payload << shift;
871        if byte[0] & 0x80 == 0 {
872            return Ok(value);
873        }
874    }
875    Err(BinaryError::malformed("LEB128 value overflows 64 bits"))
876}
877
878fn read_exact(reader: &mut dyn Read, buffer: &mut [u8]) -> BinaryResult<()> {
879    reader.read_exact(buffer).map_err(|error| {
880        if error.kind() == io::ErrorKind::UnexpectedEof {
881            BinaryError::malformed("unexpected end of packet")
882        } else {
883            BinaryError::Io(error)
884        }
885    })
886}
887
888/// The next byte of `reader`, or `None` at a clean end of stream.
889fn read_byte_or_eof(reader: &mut dyn Read) -> BinaryResult<Option<u8>> {
890    let mut byte = [0u8; 1];
891    loop {
892        match reader.read(&mut byte) {
893            Ok(0) => return Ok(None),
894            Ok(_) => return Ok(Some(byte[0])),
895            Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
896            Err(error) => return Err(error.into()),
897        }
898    }
899}