From 401f8cedf5e14befb5ffa41f200949a51fa1362f Mon Sep 17 00:00:00 2001 From: Anand Krishnamoorthi Date: Wed, 11 Feb 2026 13:06:26 -0600 Subject: [PATCH] rvm: switch binary serialization to postcard Move RVM binary encoding from bincode to postcard and bump the format version. Update test helpers, docs, changelog, and refresh lockfiles after the swap. Closes #575 Signed-off-by: Anand Krishnamoorthi --- .github/workflows/rust-clippy.yml | 2 +- CHANGELOG.md | 3 + Cargo.lock | 51 ++++-- Cargo.toml | 4 +- bindings/ffi/Cargo.lock | 51 ++++-- bindings/java/Cargo.lock | 51 ++++-- bindings/python/Cargo.lock | 51 ++++-- bindings/wasm/Cargo.lock | 51 ++++-- docs/rvm/architecture.md | 4 +- src/rvm/program/core.rs | 2 +- src/rvm/program/serialization/binary.rs | 217 ++++++------------------ src/rvm/tests/test_utils.rs | 15 +- xtask/src/tasks/dev/clippy.rs | 27 ++- 13 files changed, 261 insertions(+), 268 deletions(-) diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml index d8451a2b..1b9ce46a 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/rust-clippy.yml @@ -49,9 +49,9 @@ jobs: - name: Run rust-clippy run: cargo xtask clippy --sarif rust-clippy-results.sarif - continue-on-error: true - name: Upload analysis results to GitHub + if: ${{ hashFiles('rust-clippy-results.sarif') != '' }} uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v3.29.11 with: sarif_file: rust-clippy-results.sarif diff --git a/CHANGELOG.md b/CHANGELOG.md index 002b6c2e..f69e55d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Azure RBAC condition interpreter with builtin evaluation coverage and YAML test suite, including quantifier (ForAnyOfAnyValues/ForAllOfAllValues), datetime (DateTimeEquals), IP (IpInRange), GUID (GuidEquals), list (ListContains), and string (StringEquals) semantics. - FFI surface for Azure RBAC condition evaluation (see bindings changelog for language-specific wrappers). +### Changed +- [**breaking**] Switch RVM binary serialization to postcard, bump the format to v4, and mark v1-3 loads as partial (recompile required). + ## [0.9.1](https://github.com/microsoft/regorus/compare/regorus-v0.9.0...regorus-v0.9.1) - 2026-02-06 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 9eb995cd..a0323d5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,16 +123,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "serde", - "unty", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -300,6 +290,15 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -433,6 +432,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "ensure_no_std" version = "0.1.0" @@ -1035,6 +1046,18 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1230,7 +1253,6 @@ name = "regorus" version = "0.9.1" dependencies = [ "anyhow", - "bincode", "cfg-if", "chrono", "chrono-tz", @@ -1247,6 +1269,7 @@ dependencies = [ "num-bigint", "num-traits", "num_cpus", + "postcard", "prettydiff", "rand", "regex", @@ -1535,12 +1558,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.8" diff --git a/Cargo.toml b/Cargo.toml index 191e88e6..d1143ee8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ net = ["dep:ipnet"] no_std = ["lazy_static/spin_no_std"] opa-runtime = [] regex = ["dep:regex"] -rvm = ["dep:bincode", "dep:indexmap"] +rvm = ["dep:postcard", "dep:indexmap"] semver = ["dep:semver"] allocator-memory-limits = ["std", "mimalloc", "mimalloc/allocator-memory-limits"] std = ["rand/std", "rand/std_rng", "serde_json/std", "msvc_spectre_libs" ] @@ -128,7 +128,7 @@ mimalloc = { package = "regorus-mimalloc", path = "mimalloc", version = "2.2.6", # rvm related deps indexmap = { version = "2.12.1", default-features = false, features = ["serde"], optional = true } -bincode = { version = "2.0.1", default-features = false, features = ["alloc", "serde"], optional = true } +postcard = { version = "1.1.3", default-features = false, features = ["alloc"], optional = true } [dev-dependencies] anyhow = "1.0.45" diff --git a/bindings/ffi/Cargo.lock b/bindings/ffi/Cargo.lock index e3498a86..dec168c6 100644 --- a/bindings/ffi/Cargo.lock +++ b/bindings/ffi/Cargo.lock @@ -102,16 +102,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "serde", - "unty", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -246,6 +236,15 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -304,6 +303,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "equivalent" version = "1.0.2" @@ -805,6 +816,18 @@ dependencies = [ "siphasher", ] +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -953,7 +976,6 @@ name = "regorus" version = "0.9.1" dependencies = [ "anyhow", - "bincode", "chrono", "chrono-tz", "dashmap", @@ -966,6 +988,7 @@ dependencies = [ "msvc_spectre_libs", "num-bigint", "num-traits", + "postcard", "rand", "regex", "regorus-mimalloc", @@ -1258,12 +1281,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.8" diff --git a/bindings/java/Cargo.lock b/bindings/java/Cargo.lock index 00f15abe..280df93f 100644 --- a/bindings/java/Cargo.lock +++ b/bindings/java/Cargo.lock @@ -52,16 +52,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "serde", - "unty", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -162,6 +152,15 @@ dependencies = [ "phf", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.18", +] + [[package]] name = "combine" version = "4.6.7" @@ -204,6 +203,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "equivalent" version = "1.0.2" @@ -681,6 +692,18 @@ dependencies = [ "siphasher", ] +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -829,7 +852,6 @@ name = "regorus" version = "0.9.1" dependencies = [ "anyhow", - "bincode", "chrono", "chrono-tz", "data-encoding", @@ -841,6 +863,7 @@ dependencies = [ "msvc_spectre_libs", "num-bigint", "num-traits", + "postcard", "rand", "regex", "regorus-mimalloc", @@ -1081,12 +1104,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.8" diff --git a/bindings/python/Cargo.lock b/bindings/python/Cargo.lock index ccdd996e..d4c819f9 100644 --- a/bindings/python/Cargo.lock +++ b/bindings/python/Cargo.lock @@ -52,16 +52,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "serde", - "unty", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -150,6 +140,15 @@ dependencies = [ "phf", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -182,6 +181,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "equivalent" version = "1.0.2" @@ -676,6 +687,18 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -888,7 +911,6 @@ name = "regorus" version = "0.9.1" dependencies = [ "anyhow", - "bincode", "chrono", "chrono-tz", "data-encoding", @@ -900,6 +922,7 @@ dependencies = [ "msvc_spectre_libs", "num-bigint", "num-traits", + "postcard", "rand", "regex", "regorus-mimalloc", @@ -1124,12 +1147,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.8" diff --git a/bindings/wasm/Cargo.lock b/bindings/wasm/Cargo.lock index 1dd82e63..441e902b 100644 --- a/bindings/wasm/Cargo.lock +++ b/bindings/wasm/Cargo.lock @@ -63,16 +63,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "serde", - "unty", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -167,6 +157,15 @@ dependencies = [ "phf", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -199,6 +198,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "equivalent" version = "1.0.2" @@ -738,6 +749,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -886,7 +909,6 @@ name = "regorus" version = "0.9.1" dependencies = [ "anyhow", - "bincode", "chrono", "chrono-tz", "data-encoding", @@ -898,6 +920,7 @@ dependencies = [ "msvc_spectre_libs", "num-bigint", "num-traits", + "postcard", "rand", "regex", "semver", @@ -1113,12 +1136,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.8" diff --git a/docs/rvm/architecture.md b/docs/rvm/architecture.md index f76f5c9e..7f1e991a 100644 --- a/docs/rvm/architecture.md +++ b/docs/rvm/architecture.md @@ -90,11 +90,11 @@ version: `3`). 2. **Section manifest**: four little-endian `u32` lengths for entry points, sources, literals, and the rule tree, plus a single-byte `rego_v0` flag. -3. **Preamble payloads**: each section is encoded with `bincode` using helper +3. **Preamble payloads**: each section is encoded with `postcard` using helper wrappers (`BinaryValueSlice`, `BinaryValueRef`) to stream complex `Value` graphs without cloning. 4. **Program core**: the remaining `Program` struct is serialized once more via - `bincode`; fields skipped by serde (entry points, literals, sources, + `postcard`; fields skipped by serde (entry points, literals, sources, rule_tree, resolved builtins) are re-inserted from the preamble when the program is reconstructed. diff --git a/src/rvm/program/core.rs b/src/rvm/program/core.rs index dccc1dd9..05c781f3 100644 --- a/src/rvm/program/core.rs +++ b/src/rvm/program/core.rs @@ -85,7 +85,7 @@ pub struct Program { impl Program { /// Current serialization format version - pub const SERIALIZATION_VERSION: u32 = 3; + pub const SERIALIZATION_VERSION: u32 = 4; /// Magic bytes to identify Regorus program files pub const MAGIC: [u8; 4] = *b"REGO"; /// Maximum instructions supported (matches u16 jump targets) diff --git a/src/rvm/program/serialization/binary.rs b/src/rvm/program/serialization/binary.rs index adafa809..85f29760 100644 --- a/src/rvm/program/serialization/binary.rs +++ b/src/rvm/program/serialization/binary.rs @@ -4,8 +4,7 @@ use alloc::format; use alloc::string::{String, ToString as _}; use alloc::vec::Vec; -use bincode::config::standard; -use bincode::serde::{decode_from_slice, encode_to_vec}; +use postcard::{from_bytes, to_allocvec}; use super::{DeserializationResult, Program}; use crate::value::Value; @@ -70,7 +69,7 @@ impl Program { } /// Serialize program to binary format. - /// Uses pure bincode for all sections now that `Value` supports serde. + /// Uses postcard for all sections now that `Value` supports serde. pub fn serialize_binary(&self) -> Result, String> { self.validate_limits()?; @@ -79,19 +78,19 @@ impl Program { buffer.extend_from_slice(&Self::MAGIC); buffer.extend_from_slice(&Self::SERIALIZATION_VERSION.to_le_bytes()); - let entry_points_bin = encode_to_vec(&self.entry_points, standard()) - .map_err(|e| format!("Entry points bincode serialization failed: {}", e))?; + let entry_points_bin = to_allocvec(&self.entry_points) + .map_err(|e| format!("Entry points postcard serialization failed: {}", e))?; - let sources_bin = encode_to_vec(&self.sources, standard()) - .map_err(|e| format!("Sources bincode serialization failed: {}", e))?; + let sources_bin = to_allocvec(&self.sources) + .map_err(|e| format!("Sources postcard serialization failed: {}", e))?; - let literals_bin = encode_to_vec(BinaryValueSlice(self.literals.as_slice()), standard()) - .map_err(|e| format!("Literals bincode serialization failed: {}", e))?; + let literals_bin = to_allocvec(&BinaryValueSlice(self.literals.as_slice())) + .map_err(|e| format!("Literals postcard serialization failed: {}", e))?; - let rule_tree_bin = encode_to_vec(BinaryValueRef(&self.rule_tree), standard()) - .map_err(|e| format!("Rule tree bincode serialization failed: {}", e))?; + let rule_tree_bin = to_allocvec(&BinaryValueRef(&self.rule_tree)) + .map_err(|e| format!("Rule tree postcard serialization failed: {}", e))?; - let binary_data = encode_to_vec(self, standard()) + let binary_data = to_allocvec(self) .map_err(|e| format!("Program structure binary serialization failed: {}", e))?; buffer.extend_from_slice( @@ -152,128 +151,13 @@ impl Program { } match version { - 1 => { - if data.len() < 25 { - return Err("Data too short for header".to_string()); - } - - let entry_points_len = Self::read_u32_as_usize(data, 8)?; - let sources_len = Self::read_u32_as_usize(data, 12)?; - let rego_v0 = Self::read_byte(data, 16)? != 0; - - let mut offset = OffsetTracker::new(17); - let entry_points_start = offset.advance(entry_points_len)?; - let sources_start = offset.advance(sources_len)?; - let binary_len_start = offset.current(); - - if data.len() < binary_len_start.checked_add(4).ok_or("Offset overflow")? { - return Err("Data too short for binary length".to_string()); - } - - let binary_len = Self::read_u32_as_usize(data, binary_len_start)?; - - let mut binary_offset = OffsetTracker::new(binary_len_start); - binary_offset.advance(4)?; // Skip the binary_len u32 - let binary_start = binary_offset.advance(binary_len)?; - let json_start = binary_offset.current(); - - if data.len() < json_start.checked_add(4).ok_or("Offset overflow")? { - return Err("Data too short for JSON length".to_string()); - } - - let json_len = Self::read_u32_as_usize(data, json_start)?; - - let total_expected = json_start - .checked_add(4) - .and_then(|v| v.checked_add(json_len)) - .ok_or("Offset overflow")?; - if data.len() < total_expected { - return Err("Data truncated".to_string()); - } - - let json_end = json_start - .checked_add(4) - .and_then(|v| v.checked_add(json_len)) - .ok_or("Offset overflow")?; - - let entry_points = decode_from_slice( - Self::get_slice(data, entry_points_start, sources_start)?, - standard(), - ) - .map(|(value, _)| value) - .map_err(|e| format!("Entry points deserialization failed: {}", e))?; - - let sources = decode_from_slice( - Self::get_slice(data, sources_start, binary_len_start)?, - standard(), - ) - .map(|(value, _)| value) - .map_err(|e| format!("Sources deserialization failed: {}", e))?; - - let mut needs_recompilation = false; - - let mut program = match decode_from_slice::( - Self::get_slice(data, binary_start, json_start)?, - standard(), - ) { - Ok((prog, _)) => prog, - Err(_e) => { - needs_recompilation = true; - Program::new() - } - }; - - let (literals, rule_tree) = - match serde_json::from_slice::(Self::get_slice( - data, - json_start.checked_add(4).ok_or("Offset overflow")?, - json_end, - )?) { - Ok(combined) => { - let literals = combined - .get("literals") - .and_then(|v| serde_json::from_value::>(v.clone()).ok()) - .unwrap_or_else(|| { - needs_recompilation = true; - Vec::new() - }); - - let rule_tree = combined - .get("rule_tree") - .and_then(|v| serde_json::from_value::(v.clone()).ok()) - .unwrap_or_else(|| { - needs_recompilation = true; - Value::new_object() - }); - - (literals, rule_tree) - } - Err(_e) => { - needs_recompilation = true; - (Vec::new(), Value::new_object()) - } - }; - - program.entry_points = entry_points; - program.sources = sources; - program.literals = literals; - program.rule_tree = rule_tree; - program.rego_v0 = rego_v0; - program.needs_recompilation = needs_recompilation; - - if !program.builtin_info_table.is_empty() { - if let Err(_e) = program.initialize_resolved_builtins() { - program.needs_recompilation = true; - } - } - - if program.needs_recompilation { - Ok(DeserializationResult::Partial(program)) - } else { - Ok(DeserializationResult::Complete(program)) - } + 1..=3 => { + let mut program = Program::new(); + program.needs_recompilation = true; + program.rego_v0 = Self::legacy_rego_v0(data, version).unwrap_or(false); + Ok(DeserializationResult::Partial(program)) } - 2 | 3 => { + 4 => { if data.len() < 29 { return Err("Data too short for header".to_string()); } @@ -306,27 +190,21 @@ impl Program { return Err("Data truncated".to_string()); } - let entry_points = decode_from_slice( - Self::get_slice(data, entry_points_start, sources_start)?, - standard(), - ) - .map(|(value, _)| value) - .map_err(|e| format!("Entry points deserialization failed: {}", e))?; + let entry_points = + from_bytes(Self::get_slice(data, entry_points_start, sources_start)?) + .map_err(|e| format!("Entry points deserialization failed: {}", e))?; - let sources = decode_from_slice( - Self::get_slice(data, sources_start, literals_start)?, - standard(), - ) - .map(|(value, _)| value) - .map_err(|e| format!("Sources deserialization failed: {}", e))?; + let sources = from_bytes(Self::get_slice(data, sources_start, literals_start)?) + .map_err(|e| format!("Sources deserialization failed: {}", e))?; let mut needs_recompilation = false; - let literals = match decode_from_slice::, _>( - Self::get_slice(data, literals_start, rule_tree_start)?, - standard(), - ) { - Ok((binary_literals, _)) => match binaries_to_values(binary_literals) { + let literals = match from_bytes::>(Self::get_slice( + data, + literals_start, + rule_tree_start, + )?) { + Ok(binary_literals) => match binaries_to_values(binary_literals) { Ok(values) => values, Err(_e) => { needs_recompilation = true; @@ -339,11 +217,12 @@ impl Program { } }; - let rule_tree = match decode_from_slice::( - Self::get_slice(data, rule_tree_start, binary_len_start)?, - standard(), - ) { - Ok((binary_tree, _)) => match binary_to_value(binary_tree) { + let rule_tree = match from_bytes::(Self::get_slice( + data, + rule_tree_start, + binary_len_start, + )?) { + Ok(binary_tree) => match binary_to_value(binary_tree) { Ok(value) => value, Err(_e) => { needs_recompilation = true; @@ -356,16 +235,14 @@ impl Program { } }; - let mut program = match decode_from_slice::( - Self::get_slice(data, binary_start, binary_end)?, - standard(), - ) { - Ok((prog, _)) => prog, - Err(_e) => { - needs_recompilation = true; - Program::new() - } - }; + let mut program = + match from_bytes::(Self::get_slice(data, binary_start, binary_end)?) { + Ok(prog) => prog, + Err(_e) => { + needs_recompilation = true; + Program::new() + } + }; program.entry_points = entry_points; program.sources = sources; @@ -404,8 +281,16 @@ impl Program { let version = Self::read_u32(data, 4).ok(); match version { - Some(1..=3) => Ok(true), + Some(1..=4) => Ok(true), _ => Ok(false), } } + + fn legacy_rego_v0(data: &[u8], version: u32) -> Option { + match version { + 1 => data.get(16).map(|value| *value != 0), + 2 | 3 => data.get(24).map(|value| *value != 0), + _ => None, + } + } } diff --git a/src/rvm/tests/test_utils.rs b/src/rvm/tests/test_utils.rs index 0049fe56..eac9064a 100644 --- a/src/rvm/tests/test_utils.rs +++ b/src/rvm/tests/test_utils.rs @@ -13,8 +13,7 @@ use crate::rvm::program::{binaries_to_values, BinaryValue, Program}; use alloc::format; use alloc::string::String; use alloc::vec::Vec; -use bincode::config::standard; -use bincode::serde::decode_from_slice; +use postcard::from_bytes; /// Test utility function for round-trip serialization /// Serializes program, deserializes it, and serializes again to check for consistency @@ -31,7 +30,7 @@ pub fn test_round_trip_serialization(program: &Program) -> Result<(), String> { serialized1[7], ]); - if version == 2 && serialized1.len() >= 25 { + if version == Program::SERIALIZATION_VERSION && serialized1.len() >= 25 { let entry_points_len = u32::from_le_bytes([ serialized1[8], serialized1[9], @@ -56,11 +55,9 @@ pub fn test_round_trip_serialization(program: &Program) -> Result<(), String> { let rule_tree_start = literals_start + literals_len; if literals_len > 0 && serialized1.len() >= rule_tree_start { - match decode_from_slice::, _>( - &serialized1[literals_start..rule_tree_start], - standard(), - ) { - Ok((decoded_literals, _)) => { + match from_bytes::>(&serialized1[literals_start..rule_tree_start]) + { + Ok(decoded_literals) => { if binaries_to_values(decoded_literals).is_err() { return Err( "Failed to convert literal table from binary representation".into(), @@ -69,7 +66,7 @@ pub fn test_round_trip_serialization(program: &Program) -> Result<(), String> { } Err(err) => { return Err(format!( - "Failed to decode literal table with bincode: {}", + "Failed to decode literal table with postcard: {}", err )); } diff --git a/xtask/src/tasks/dev/clippy.rs b/xtask/src/tasks/dev/clippy.rs index a16ac624..8f0272ca 100644 --- a/xtask/src/tasks/dev/clippy.rs +++ b/xtask/src/tasks/dev/clippy.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::fs::File; +use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; @@ -131,10 +131,33 @@ fn run_clippy_with_sarif(workspace: &Path, sarif: &Path) -> Result<()> { sarif_cmd.stdout(Stdio::from(sarif_file)); run_command(sarif_cmd, "clippy-sarif")?; + let formatted_path = sarif_path.with_extension("sarif.tmp"); + let sarif_input = File::open(&sarif_path) + .with_context(|| format!("failed to reopen SARIF output at {}", sarif_path.display()))?; + let sarif_output = File::create(&formatted_path).with_context(|| { + format!( + "failed to create SARIF output at {}", + formatted_path.display() + ) + })?; + let mut fmt = Command::new("sarif-fmt"); - fmt.arg(&sarif_path); + fmt.stdin(Stdio::from(sarif_input)); + fmt.stdout(Stdio::from(sarif_output)); run_command(fmt, "sarif-fmt")?; + let formatted_len = fs::metadata(&formatted_path) + .map(|metadata| metadata.len()) + .unwrap_or(0); + if formatted_len == 0 { + let _ = fs::remove_file(&formatted_path); + println!("sarif-fmt produced empty output, keeping original SARIF"); + } else { + fs::rename(&formatted_path, &sarif_path).with_context(|| { + format!("failed to replace SARIF output at {}", sarif_path.display()) + })?; + } + println!("SARIF report written to {}", sarif_path.display()); Ok(())