sim_codec_bitwise/lib.rs
1//! Canonical minimal bit-packed wire codec for the SIM runtime.
2//!
3//! `codec:bitwise` is the canonical, minimal sibling of `codec:binary`: a
4//! framed, fail-closed, general-purpose `Expr` codec whose wire format is
5//! bit-granular rather than byte-granular. It implements all six codec roles
6//! ([`Decoder`]/[`Encoder`], located, and tree) over the shared `Expr` graph,
7//! carries the same interning side tables and [`DecodeLimits`], and adds three
8//! density and determinism wins:
9//!
10//! - **One `vbits` primitive** ("size of the size, then the size") for every
11//! length, index, magnitude, and span, so no leading zero bit is ever emitted.
12//! - **Signed minimal-magnitude integers**: any-sign canonical integer encodes
13//! as a sign bit plus its exact significant bits (`-255` costs ~9 bits, not
14//! ~32); only genuine non-integers fall back to canonical text.
15//! - **A self-delimiting frame**: a small version/flags prefix, side tables,
16//! body, and optional origin payloads, with no magic and no pad-count. The
17//! final carrier byte is zero-padded and every trailing bit must be zero.
18//!
19//! Fields pack across byte boundaries, so no field is byte-aligned inside the
20//! logical frame. The plain-mode frame is deterministic (canonical map/set
21//! order, minimal magnitudes, no padding), which makes [`canonical_bytes`] the
22//! smallest canonical byte string for an `Expr` value -- the natural
23//! content-address / cassette serialization for the FABRIC_2 / MODEL_2 content
24//! store (a documentation pointer only; this crate adds no such dependency).
25//!
26//! The public carrier is unchanged: the codec reads [`Input::Bytes`] (accepting
27//! [`Input::Text`] by UTF-8 bytes for parity with binary) and emits
28//! [`Output::Bytes`]. The bit cursor lives entirely inside private
29//! reader/writer types.
30//!
31//! # Measured tradeoff (vs `codec:binary`)
32//!
33//! The density is real but has a CPU cost, and both are measured by the
34//! `sim-codec-compare` harness (run `cargo run --release -p sim-codec-compare
35//! --bin report`). As of 2026-07-01: bitwise is ~40-50% smaller than `binary` on
36//! structured / integer-dense / realistic data at a modest ~1.0-1.5x
37//! encode/decode cost, and `encode_dense` collapses repetitive data to ~0.07-0.14
38//! of `binary`. It is never larger than `binary`. The honest non-wins: raw UTF-8
39//! strings are a size tie AND up to ~9x slower to encode, so prefer `binary` on
40//! the hot path and for string-blob-heavy payloads. Bitwise earns its keep for
41//! canonical storage / content-addressing and structured runtime data.
42//!
43//! # Examples
44//!
45//! Register the codec on a runtime, round-trip through the codec surface, and
46//! confirm the canonical bytes are stable:
47//!
48//! ```
49//! use sim_codec::{Input, decode_with_codec, encode_with_codec};
50//! use sim_codec_bitwise::{BitwiseCodecLib, canonical_bytes};
51//! use sim_kernel::{Cx, DefaultFactory, EagerPolicy, Expr, ReadPolicy, Symbol};
52//!
53//! let mut cx = Cx::new(std::sync::Arc::new(EagerPolicy), std::sync::Arc::new(DefaultFactory));
54//! sim_test_support::register_core_classes(&mut cx);
55//! let lib = BitwiseCodecLib::new(cx.registry_mut().fresh_codec_id());
56//! cx.load_lib(&lib)?;
57//!
58//! let codec = Symbol::qualified("codec", "bitwise");
59//! let expr = Expr::List(vec![Expr::Nil, Expr::Bool(true)]);
60//! let sim_codec::Output::Bytes(bytes) =
61//! encode_with_codec(&mut cx, &codec, &expr, Default::default())?
62//! else { panic!("bitwise emits bytes") };
63//! let back = decode_with_codec(&mut cx, &codec, Input::Bytes(bytes), ReadPolicy::default())?;
64//! assert!(back.canonical_eq(&expr));
65//! assert_eq!(canonical_bytes(&expr)?, canonical_bytes(&back)?); // canonical + stable
66//! # Ok::<(), sim_kernel::Error>(())
67//! ```
68//!
69//! Arbitrary bytes are untrusted data, not executable input: a frame that does
70//! not decode fails closed rather than running anything.
71//!
72//! ```
73//! use sim_codec_bitwise::decode_frame;
74//! use sim_kernel::CodecId;
75//!
76//! assert!(decode_frame(CodecId(1), b"\xff\xff\xff\xff").is_err());
77//! ```
78//!
79//! Opt-in dense mode shares a repeated, value-equal subtree behind a
80//! back-reference, so a value with structural repetition encodes strictly
81//! smaller than the plain tree while still round-tripping by value. The plain
82//! [`canonical_bytes`] stay ref-free:
83//!
84//! ```
85//! use sim_codec_bitwise::{canonical_bytes, decode_frame, encode_dense};
86//! use sim_kernel::{CodecId, Expr, Symbol};
87//!
88//! let shared = Expr::List(vec![
89//! Expr::Symbol(Symbol::qualified("math", "add")),
90//! Expr::String("a repeated leaf payload".to_owned()),
91//! Expr::Bool(true),
92//! ]);
93//! let expr = Expr::List(vec![shared.clone(), shared.clone(), shared]);
94//!
95//! let dense = encode_dense(&expr)?;
96//! assert!(dense.0.len() < canonical_bytes(&expr)?.len()); // smaller than the plain tree
97//!
98//! let (_tables, decoded) = decode_frame(CodecId(1), &dense.0)?;
99//! assert!(decoded.canonical_eq(&expr));
100//! # Ok::<(), sim_kernel::Error>(())
101//! ```
102//!
103//! [`Input::Bytes`]: sim_codec::Input::Bytes
104//! [`Input::Text`]: sim_codec::Input::Text
105//! [`Output::Bytes`]: sim_codec::Output::Bytes
106//! [`Decoder`]: sim_codec::Decoder
107//! [`Encoder`]: sim_codec::Encoder
108
109#![forbid(unsafe_code)]
110#![deny(missing_docs)]
111
112mod bitio;
113mod codec;
114mod number;
115mod reader;
116mod tables;
117#[cfg(test)]
118mod tests;
119mod types;
120mod writer;
121
122use sim_kernel::{Expr, Result};
123
124pub use codec::{
125 BitwiseCodec, BitwiseCodecLib, decode_frame, decode_located_frame, decode_located_tree_frame,
126 decode_located_tree_frame_with_limits, encode_dense, encode_frame, encode_located_frame,
127 encode_located_tree_frame,
128};
129pub use types::{BitwiseFrame, DecodeLimits, FrameTables};
130
131/// Returns the canonical minimal serialization of `expr`: the plain-mode
132/// bitwise frame with no origin and no dense references.
133///
134/// This is the documented smallest canonical byte string for an `Expr` value
135/// and is suitable as a `ContentKey` input for a content-addressed store.
136/// Structurally equal values (including maps and sets in any insertion order)
137/// produce identical bytes, and re-encoding a decoded frame is idempotent.
138pub fn canonical_bytes(expr: &Expr) -> Result<Vec<u8>> {
139 Ok(encode_frame(expr)?.0)
140}