sim_codec_bitwise/types.rs
1//! Frame value types and wire constants.
2//!
3//! Defines the wire version and flag constants, the 6-bit [`BitwiseTag`]
4//! alphabet, the [`BitwiseFrame`] and [`FrameTables`] carriers, and the
5//! [`DecodeLimits`] bounds that keep decoding fail-closed.
6
7use sim_codec::DecodeLimits as SharedDecodeLimits;
8use sim_kernel::Symbol;
9
10/// The current wire-format version, carried as the first `vbits` of a frame.
11pub(crate) const VERSION: u128 = 1;
12/// No optional frame sections are present.
13pub(crate) const FLAG_NONE: u128 = 0;
14/// The frame carries a single top-level origin (located form).
15pub(crate) const FLAG_ORIGIN: u128 = 1;
16/// The frame carries a per-node origin tree (tree form).
17pub(crate) const FLAG_TREE_ORIGIN: u128 = 2;
18/// The frame body uses dense mode: a repeated, value-equal subtree is written
19/// once and later occurrences are back-references ([`BitwiseTag::Ref`]).
20///
21/// Dense mode is opt-in (see [`crate::encode_dense`]); the plain body, and
22/// therefore [`crate::canonical_bytes`], never sets this bit and never emits a
23/// `Ref`.
24pub(crate) const FLAG_DENSE: u128 = 4;
25/// The set of flag bits this version understands; any other bit is rejected.
26pub(crate) const FLAG_KNOWN: u128 = FLAG_ORIGIN | FLAG_TREE_ORIGIN | FLAG_DENSE;
27
28/// Fail-closed bounds applied while decoding an untrusted bitwise frame.
29///
30/// Every count and length read from a frame is checked against these limits
31/// before any allocation, so a malformed or hostile frame cannot exhaust
32/// memory or recurse without bound. Construct via [`DecodeLimits::default`] or
33/// from the shared [`sim_codec::DecodeLimits`] (see the [`From`] impl).
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub struct DecodeLimits {
36 /// Maximum total size, in bytes, of the frame accepted by the reader.
37 pub max_frame_bytes: usize,
38 /// Maximum length, in bytes, of any single decoded string.
39 pub max_string_bytes: usize,
40 /// Maximum length, in bytes, of any single decoded byte blob.
41 pub max_blob_bytes: usize,
42 /// Maximum number of entries in any side table or collection.
43 pub max_table_entries: usize,
44 /// Maximum number of `Expr` nodes decoded from one frame.
45 pub max_expr_nodes: usize,
46 /// Maximum nesting depth of the decoded `Expr` graph.
47 pub max_depth: usize,
48 /// Maximum number of trivia items carried by a single origin.
49 pub max_trivia_items: usize,
50}
51
52impl Default for DecodeLimits {
53 fn default() -> Self {
54 SharedDecodeLimits::default().into()
55 }
56}
57
58impl From<SharedDecodeLimits> for DecodeLimits {
59 fn from(shared: SharedDecodeLimits) -> Self {
60 Self {
61 max_frame_bytes: shared.max_input_bytes,
62 max_string_bytes: shared.max_string_bytes,
63 max_blob_bytes: shared.max_blob_bytes,
64 max_table_entries: shared.max_collection_len,
65 max_expr_nodes: shared.max_expr_nodes,
66 max_depth: shared.max_depth,
67 max_trivia_items: shared.max_trivia_items,
68 }
69 }
70}
71
72/// A complete encoded bitwise frame: the self-delimiting header, side tables,
73/// and the bit-packed `Expr` body, owned as a single byte buffer.
74///
75/// The final carrier byte is zero-padded in its unused low bits; decode rejects
76/// any nonzero trailing bit and any trailing whole byte, so the plain-mode
77/// buffer is the smallest canonical byte string for its `Expr` value.
78#[derive(Clone, Debug, PartialEq, Eq)]
79pub struct BitwiseFrame(
80 /// The raw frame bytes, ready to be written to a byte transport.
81 pub Vec<u8>,
82);
83
84/// The interning side tables carried in a frame header.
85///
86/// Symbols, number-domain symbols, and their namespaces are interned once in
87/// these tables and referenced by index from the body, keeping the frame
88/// compact. Decoding reconstructs the same tables and validates every body
89/// index against them. The collection order matches `sim-codec-binary` so the
90/// two codecs agree on interning.
91#[derive(Clone, Debug, PartialEq, Eq)]
92pub struct FrameTables {
93 /// Interned namespace (lib) strings, referenced by index from symbols.
94 pub libs: Vec<String>,
95 /// Interned symbols referenced by index from the body.
96 pub symbols: Vec<Symbol>,
97 /// Interned number-domain symbols referenced by number-body nodes.
98 pub number_domains: Vec<Symbol>,
99}
100
101/// The fixed-width 6-bit tag that prefixes each node in a frame body.
102///
103/// The first sixteen variants are inline small unsigned integer literals
104/// carrying only a domain index; the next twenty are the structural `Expr`
105/// kinds (one generic tag each -- lengths ride `vbits`); [`BitwiseTag::Ref`] is
106/// emitted only in dense mode (a back-reference to an earlier subtree). Raw
107/// values `37..=63` are reserved and rejected on decode.
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109#[repr(u8)]
110pub(crate) enum BitwiseTag {
111 UInt0 = 0,
112 UInt1 = 1,
113 UInt2 = 2,
114 UInt3 = 3,
115 UInt4 = 4,
116 UInt5 = 5,
117 UInt6 = 6,
118 UInt7 = 7,
119 UInt8 = 8,
120 UInt9 = 9,
121 UInt10 = 10,
122 UInt11 = 11,
123 UInt12 = 12,
124 UInt13 = 13,
125 UInt14 = 14,
126 UInt15 = 15,
127 Nil = 16,
128 False = 17,
129 True = 18,
130 Number = 19,
131 Symbol = 20,
132 Local = 21,
133 String = 22,
134 Bytes = 23,
135 List = 24,
136 Vector = 25,
137 Map = 26,
138 Set = 27,
139 Call = 28,
140 Infix = 29,
141 Prefix = 30,
142 Postfix = 31,
143 Block = 32,
144 Quote = 33,
145 Annotated = 34,
146 Extension = 35,
147 /// Dense-mode back-reference to an earlier subtree. The plain codec never
148 /// emits it; only [`crate::encode_dense`] does.
149 Ref = 36,
150}
151
152impl BitwiseTag {
153 /// The fixed on-wire width of a tag, in bits.
154 pub(crate) const WIDTH_BITS: usize = 6;
155
156 /// Maps a raw 6-bit value to a tag, or `None` for a reserved/invalid slot.
157 ///
158 /// Written as an exhaustive `match` (the crate forbids `unsafe`), so no
159 /// out-of-range or reserved value can ever transmute into a tag.
160 pub(crate) fn from_u6(value: u8) -> Option<Self> {
161 let tag = match value {
162 0 => Self::UInt0,
163 1 => Self::UInt1,
164 2 => Self::UInt2,
165 3 => Self::UInt3,
166 4 => Self::UInt4,
167 5 => Self::UInt5,
168 6 => Self::UInt6,
169 7 => Self::UInt7,
170 8 => Self::UInt8,
171 9 => Self::UInt9,
172 10 => Self::UInt10,
173 11 => Self::UInt11,
174 12 => Self::UInt12,
175 13 => Self::UInt13,
176 14 => Self::UInt14,
177 15 => Self::UInt15,
178 16 => Self::Nil,
179 17 => Self::False,
180 18 => Self::True,
181 19 => Self::Number,
182 20 => Self::Symbol,
183 21 => Self::Local,
184 22 => Self::String,
185 23 => Self::Bytes,
186 24 => Self::List,
187 25 => Self::Vector,
188 26 => Self::Map,
189 27 => Self::Set,
190 28 => Self::Call,
191 29 => Self::Infix,
192 30 => Self::Prefix,
193 31 => Self::Postfix,
194 32 => Self::Block,
195 33 => Self::Quote,
196 34 => Self::Annotated,
197 35 => Self::Extension,
198 36 => Self::Ref,
199 _ => return None,
200 };
201 Some(tag)
202 }
203
204 /// The inline literal value `0..=15` for a `UInt*` tag, else `None`.
205 pub(crate) fn small_uint(self) -> Option<u8> {
206 let value = self as u8;
207 (value < 16).then_some(value)
208 }
209}