Skip to main content

sim_codec_binary/
types.rs

1//! Frame value types and wire constants.
2//!
3//! Defines the magic/version and flag constants, the `BinaryTag` body tags,
4//! the `BinaryFrame` and `FrameTables` carriers, and the `DecodeLimits` bounds
5//! that keep decoding fail-closed.
6
7use sim_codec::DecodeLimits as SharedDecodeLimits;
8use sim_kernel::Symbol;
9
10pub(crate) const MAGIC: &[u8; 4] = b"SLB8";
11pub(crate) const VERSION: u64 = 1;
12pub(crate) const FLAG_NONE: u64 = 0;
13pub(crate) const FLAG_ORIGIN: u64 = 1;
14pub(crate) const FLAG_TREE_ORIGIN: u64 = 2;
15
16/// Fail-closed bounds applied while decoding an untrusted binary frame.
17///
18/// Every count and length read from a frame is checked against these limits
19/// before any allocation, so a malformed or hostile frame cannot exhaust
20/// memory or recurse without bound. Construct via [`DecodeLimits::default`] or
21/// from the shared [`sim_codec::DecodeLimits`] (see the [`From`] impl).
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub struct DecodeLimits {
24    /// Maximum total size, in bytes, of the frame accepted by the reader.
25    pub max_frame_bytes: usize,
26    /// Maximum length, in bytes, of any single decoded string.
27    pub max_string_bytes: usize,
28    /// Maximum length, in bytes, of any single decoded byte blob.
29    pub max_blob_bytes: usize,
30    /// Maximum number of entries in any side table or collection.
31    pub max_table_entries: usize,
32    /// Maximum number of `Expr` nodes decoded from one frame.
33    pub max_expr_nodes: usize,
34    /// Maximum nesting depth of the decoded `Expr` graph.
35    pub max_depth: usize,
36    /// Maximum number of trivia items carried by a single origin.
37    pub max_trivia_items: usize,
38}
39
40impl Default for DecodeLimits {
41    fn default() -> Self {
42        SharedDecodeLimits::default().into()
43    }
44}
45
46impl From<SharedDecodeLimits> for DecodeLimits {
47    fn from(shared: SharedDecodeLimits) -> Self {
48        Self {
49            max_frame_bytes: shared.max_input_bytes,
50            max_string_bytes: shared.max_string_bytes,
51            max_blob_bytes: shared.max_blob_bytes,
52            max_table_entries: shared.max_collection_len,
53            max_expr_nodes: shared.max_expr_nodes,
54            max_depth: shared.max_depth,
55            max_trivia_items: shared.max_trivia_items,
56        }
57    }
58}
59
60/// A complete encoded binary frame: a magic/version header, side tables, and
61/// the tag-prefixed `Expr` body, owned as a single byte buffer.
62#[derive(Clone, Debug, PartialEq, Eq)]
63pub struct BinaryFrame(
64    /// The raw frame bytes, ready to be written to a byte transport.
65    pub Vec<u8>,
66);
67
68/// The interning side tables carried in a frame header.
69///
70/// Symbols, number-domain symbols, and their namespaces are interned once in
71/// these tables and referenced by index from the body, keeping the frame
72/// compact. Decoding reconstructs the same tables and validates every body
73/// index against them.
74#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct FrameTables {
76    /// Interned namespace (lib) strings, referenced by index from symbols.
77    pub libs: Vec<String>,
78    /// Interned symbols referenced by index from the body.
79    pub symbols: Vec<Symbol>,
80    /// Interned number-domain symbols referenced by [`crate::BinaryTag::Number`].
81    pub number_domains: Vec<Symbol>,
82}
83
84/// The one-byte tag that prefixes each node in a frame body.
85///
86/// Each variant selects the `Expr` shape that follows and fixes its wire
87/// layout. The discriminant is the on-wire byte; decoding rejects any byte
88/// without a matching variant.
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90#[repr(u8)]
91pub enum BinaryTag {
92    /// The nil value; no payload.
93    Nil = 0x00,
94    /// The boolean `false`; no payload.
95    False = 0x01,
96    /// The boolean `true`; no payload.
97    True = 0x02,
98    /// A number: a number-domain table index then the canonical text.
99    Number = 0x03,
100    /// A symbol, given as an index into the symbol table.
101    Symbol = 0x04,
102    /// A UTF-8 string, length-prefixed.
103    String = 0x05,
104    /// A raw byte blob, length-prefixed.
105    Bytes = 0x06,
106    /// A list: a count then that many body nodes.
107    List = 0x07,
108    /// A vector: a count then that many body nodes.
109    Vector = 0x08,
110    /// A map: a count then that many canonically ordered key/value node pairs.
111    Map = 0x09,
112    /// A set: a count then that many canonically ordered body nodes.
113    Set = 0x0a,
114    /// A call: an operator node, a count, then that many argument nodes.
115    Call = 0x0b,
116    /// An infix application: an operator symbol index then left and right nodes.
117    Infix = 0x0c,
118    /// A prefix application: an operator symbol index then the argument node.
119    Prefix = 0x0d,
120    /// A postfix application: an operator symbol index then the argument node.
121    Postfix = 0x0e,
122    /// A block: a count then that many body nodes.
123    Block = 0x0f,
124    /// A quote: a quote-mode byte then the quoted node.
125    Quote = 0x10,
126    /// An annotated node: the inner node then a count of symbol/value pairs.
127    Annotated = 0x11,
128    /// An extension: a tag symbol index then the payload node.
129    Extension = 0x12,
130    /// A local binding reference, given as an index into the symbol table.
131    Local = 0x13,
132}
133
134impl BinaryTag {
135    pub(crate) fn from_byte(byte: u8) -> Option<Self> {
136        match byte {
137            0x00 => Some(Self::Nil),
138            0x01 => Some(Self::False),
139            0x02 => Some(Self::True),
140            0x03 => Some(Self::Number),
141            0x04 => Some(Self::Symbol),
142            0x05 => Some(Self::String),
143            0x06 => Some(Self::Bytes),
144            0x07 => Some(Self::List),
145            0x08 => Some(Self::Vector),
146            0x09 => Some(Self::Map),
147            0x0a => Some(Self::Set),
148            0x0b => Some(Self::Call),
149            0x0c => Some(Self::Infix),
150            0x0d => Some(Self::Prefix),
151            0x0e => Some(Self::Postfix),
152            0x0f => Some(Self::Block),
153            0x10 => Some(Self::Quote),
154            0x11 => Some(Self::Annotated),
155            0x12 => Some(Self::Extension),
156            0x13 => Some(Self::Local),
157            _ => None,
158        }
159    }
160}