Skip to main content

sim_codec_bitwise/
codec.rs

1//! The `BitwiseCodec` runtime object, `Lib` registration, and frame functions.
2//!
3//! Exposes the free `encode_*` / `decode_*` frame helpers and wires the bitwise
4//! reader and writer into the codec decoder/encoder, located, and tree roles.
5
6use std::sync::Arc;
7
8use sim_codec::{
9    CodecDefaultDecode, CodecRuntime, Decoder, Encoder, Input, LocatedDecoder, LocatedEncoder,
10    Output, ReadCx, TreeDecoder, TreeEncoder, codec_value, validate_expr_tree,
11};
12use sim_kernel::{
13    AbiVersion, Dependency, Export, Expr, Lib, LibManifest, LibTarget, Linker, LocatedExpr,
14    LocatedExprTree, Result, Symbol, Version, WriteCx,
15};
16
17use crate::reader::FrameReader;
18use crate::types::{FLAG_DENSE, FLAG_NONE, FLAG_ORIGIN, FLAG_TREE_ORIGIN};
19use crate::writer::FrameWriter;
20use crate::{BitwiseFrame, DecodeLimits, FrameTables};
21
22/// Bitwise codec runtime object: the canonical, minimal sibling of
23/// `codec:binary`.
24///
25/// As a general-purpose expression codec it round-trips kernel `Expr` values as
26/// bit-packed, self-delimiting frames, implementing every codec role --
27/// [`Decoder`]/[`Encoder`], located [`LocatedDecoder`]/[`LocatedEncoder`], and
28/// tree [`TreeDecoder`]/[`TreeEncoder`] -- over the shared `Expr` graph. It
29/// fails closed (under [`DecodeLimits`]) on any input that is not a well-formed
30/// frame, and its plain-mode output is the smallest canonical byte string for a
31/// value. Decoded bytes are treated strictly as data, never as executable input.
32pub struct BitwiseCodec;
33
34impl Decoder for BitwiseCodec {
35    fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
36        let bytes = input_bytes(input);
37        decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
38            .map(|(_, tree)| tree.located().expr)
39    }
40}
41
42impl Encoder for BitwiseCodec {
43    fn encode(&self, _cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
44        Ok(Output::Bytes(encode_frame(expr)?.0))
45    }
46}
47
48impl LocatedDecoder for BitwiseCodec {
49    fn decode_located(
50        &self,
51        cx: &mut ReadCx<'_>,
52        input: Input,
53        _source_id: String,
54    ) -> Result<LocatedExpr> {
55        let bytes = input_bytes(input);
56        decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
57            .map(|(_, tree)| tree.located())
58    }
59}
60
61impl LocatedEncoder for BitwiseCodec {
62    fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output> {
63        Ok(Output::Bytes(
64            encode_located_frame(expr, cx.options.lossless_origin)?.0,
65        ))
66    }
67}
68
69impl TreeDecoder for BitwiseCodec {
70    fn decode_tree(
71        &self,
72        cx: &mut ReadCx<'_>,
73        input: Input,
74        _source_id: String,
75    ) -> Result<LocatedExprTree> {
76        let bytes = input_bytes(input);
77        decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
78            .map(|(_, tree)| tree)
79    }
80}
81
82impl TreeEncoder for BitwiseCodec {
83    fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
84        validate_expr_tree(cx.codec, expr)?;
85        Ok(Output::Bytes(
86            encode_located_tree_frame(expr, cx.options.lossless_origin)?.0,
87        ))
88    }
89}
90
91fn input_bytes(input: Input) -> Vec<u8> {
92    match input {
93        Input::Text(text) => text.into_bytes(),
94        Input::Bytes(bytes) => bytes,
95    }
96}
97
98/// Encodes a bare [`Expr`] into a [`BitwiseFrame`], without source origins.
99///
100/// This is the plain, canonical form: the smallest byte string for the value,
101/// suitable as a content-address input (see [`crate::canonical_bytes`]).
102pub fn encode_frame(expr: &Expr) -> Result<BitwiseFrame> {
103    encode_located_frame(
104        &LocatedExpr {
105            expr: expr.clone(),
106            origin: None,
107        },
108        false,
109    )
110}
111
112/// Encodes a bare [`Expr`] into a dense [`BitwiseFrame`] with structural sharing.
113///
114/// Dense mode assigns every subexpression a pre-order number and, on a
115/// value-equal subtree already emitted, writes a `Ref` back-reference tag
116/// instead of re-encoding it. It is a deterministic pure function
117/// of the value tree and round-trips by [`Expr::canonical_eq`], but it is
118/// explicitly opt-in: the plain [`encode_frame`] and [`crate::canonical_bytes`]
119/// stay ref-free so the content-addressing key is unchanged.
120pub fn encode_dense(expr: &Expr) -> Result<BitwiseFrame> {
121    let tables = FrameTables::collect(expr);
122    let mut writer = FrameWriter::new(tables);
123    writer.flags = FLAG_DENSE;
124    writer.set_dense(true);
125    writer.write_header()?;
126    writer.write_expr(expr)?;
127    Ok(BitwiseFrame(writer.finish()))
128}
129
130/// Encodes a [`LocatedExpr`] into a [`BitwiseFrame`].
131///
132/// When `include_origin` is set and `located` carries an origin, the frame is
133/// flagged to carry that single origin so the located form round-trips.
134pub fn encode_located_frame(located: &LocatedExpr, include_origin: bool) -> Result<BitwiseFrame> {
135    let tables = FrameTables::collect(&located.expr);
136    let mut writer = FrameWriter::new(tables);
137    writer.flags = if include_origin && located.origin.is_some() {
138        FLAG_ORIGIN
139    } else {
140        FLAG_NONE
141    };
142    writer.write_header()?;
143    writer.write_expr(&located.expr)?;
144    if writer.flags & FLAG_ORIGIN != 0 {
145        writer.write_origin(
146            located
147                .origin
148                .as_ref()
149                .expect("origin flag requires origin payload"),
150        )?;
151    }
152    Ok(BitwiseFrame(writer.finish()))
153}
154
155/// Encodes a [`LocatedExprTree`] into a [`BitwiseFrame`].
156///
157/// When `include_origin` is set the frame carries the per-node origin tree so
158/// the full located tree round-trips; otherwise only the `Expr` body is
159/// written. The tree is validated before encoding and rejected if malformed.
160pub fn encode_located_tree_frame(
161    tree: &LocatedExprTree,
162    include_origin: bool,
163) -> Result<BitwiseFrame> {
164    validate_expr_tree(sim_kernel::CodecId(0), tree)?;
165    let tables = FrameTables::collect(&tree.expr);
166    let mut writer = FrameWriter::new(tables);
167    writer.flags = if include_origin {
168        FLAG_TREE_ORIGIN
169    } else {
170        FLAG_NONE
171    };
172    writer.write_header()?;
173    writer.write_expr(&tree.expr)?;
174    if writer.flags & FLAG_TREE_ORIGIN != 0 {
175        writer.write_origin_tree(tree)?;
176    }
177    Ok(BitwiseFrame(writer.finish()))
178}
179
180/// Decodes frame `bytes` into its side [`FrameTables`] and bare [`Expr`].
181///
182/// Any source origins carried by the frame are dropped. Decoding is bounded by
183/// the default [`DecodeLimits`] and fails closed on malformed or oversize input.
184pub fn decode_frame(codec: sim_kernel::CodecId, bytes: &[u8]) -> Result<(FrameTables, Expr)> {
185    let (tables, tree) = decode_located_tree_frame(codec, bytes)?;
186    Ok((tables, tree.located().expr))
187}
188
189/// Decodes frame `bytes` into its side [`FrameTables`] and a [`LocatedExpr`].
190///
191/// The top-level origin is recovered when the frame carries one. Decoding is
192/// bounded by the default [`DecodeLimits`] and fails closed on bad input.
193pub fn decode_located_frame(
194    codec: sim_kernel::CodecId,
195    bytes: &[u8],
196) -> Result<(FrameTables, LocatedExpr)> {
197    let (tables, tree) = decode_located_tree_frame(codec, bytes)?;
198    Ok((tables, tree.located()))
199}
200
201/// Decodes frame `bytes` into its side [`FrameTables`] and a full
202/// [`LocatedExprTree`], using the default [`DecodeLimits`].
203pub fn decode_located_tree_frame(
204    codec: sim_kernel::CodecId,
205    bytes: &[u8],
206) -> Result<(FrameTables, LocatedExprTree)> {
207    decode_located_tree_frame_with_limits(codec, bytes, DecodeLimits::default())
208}
209
210/// Decodes frame `bytes` into its side [`FrameTables`] and a
211/// [`LocatedExprTree`], enforcing the supplied `limits`.
212///
213/// This is the bounded decode primitive the codec roles call. It rejects an
214/// unknown version, unknown/dense flag bits, out-of-range table indices,
215/// oversize counts, reserved tags, and any nonzero or byte-sized trailing
216/// padding, failing closed on untrusted input.
217pub fn decode_located_tree_frame_with_limits(
218    codec: sim_kernel::CodecId,
219    bytes: &[u8],
220    limits: DecodeLimits,
221) -> Result<(FrameTables, LocatedExprTree)> {
222    let mut reader = FrameReader::new(codec, bytes, limits)?;
223    let tables = reader.read_header()?;
224    let expr = reader.read_expr()?;
225    let mut tree = if reader.flags & FLAG_TREE_ORIGIN != 0 {
226        reader.read_origin_tree(expr)?
227    } else {
228        LocatedExprTree::from_expr_recursive(expr)
229    };
230    if reader.flags & FLAG_ORIGIN != 0 {
231        tree.origin = Some(reader.read_origin()?);
232    }
233    reader.require_zero_padding()?;
234    Ok((tables, tree))
235}
236
237/// [`Lib`] that registers the bitwise codec with the runtime.
238///
239/// Its manifest exports the `codec/bitwise` codec, and loading wires a
240/// [`BitwiseCodec`] into the linker as the decode and encode surface for all
241/// six codec roles.
242pub struct BitwiseCodecLib {
243    symbol: Symbol,
244    codec_id: sim_kernel::CodecId,
245}
246
247impl BitwiseCodecLib {
248    /// Creates the codec lib bound to the runtime-assigned `id` for
249    /// `codec/bitwise`.
250    pub fn new(id: sim_kernel::CodecId) -> Self {
251        Self {
252            symbol: Symbol::qualified("codec", "bitwise"),
253            codec_id: id,
254        }
255    }
256}
257
258impl Lib for BitwiseCodecLib {
259    fn manifest(&self) -> LibManifest {
260        LibManifest {
261            id: self.symbol.clone(),
262            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
263            abi: AbiVersion { major: 0, minor: 1 },
264            target: LibTarget::HostRegistered,
265            requires: Vec::<Dependency>::new(),
266            capabilities: Vec::new(),
267            exports: vec![Export::Codec {
268                symbol: self.symbol.clone(),
269                codec_id: Some(self.codec_id),
270            }],
271        }
272    }
273
274    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
275        let expr_shape =
276            sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "BitwiseFrame"))?;
277        let options_shape = sim_codec::resolve_options_shape(linker)?;
278
279        linker.codec_value(
280            self.symbol.clone(),
281            codec_value(CodecRuntime {
282                id: self.codec_id,
283                symbol: self.symbol.clone(),
284                decoder: Some(Arc::new(BitwiseCodec)),
285                located_decoder: Some(Arc::new(BitwiseCodec)),
286                tree_decoder: Some(Arc::new(BitwiseCodec)),
287                encoder: Some(Arc::new(BitwiseCodec)),
288                located_encoder: Some(Arc::new(BitwiseCodec)),
289                tree_encoder: Some(Arc::new(BitwiseCodec)),
290                expr_shape,
291                options_shape,
292                default_decode: CodecDefaultDecode::Datum,
293            }),
294        )?;
295        Ok(())
296    }
297}