Skip to main content

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