sacp_cbor/lib.rs
1//! # sacp-cbor
2//!
3//! Strict deterministic CBOR validation and canonical encoding for the **SACP-CBOR/1** profile used by
4//! the **Synext Agent Control Protocol (SACP)**.
5//!
6//! ## Design principles
7//!
8//! - **Canonical bytes are the value.**
9//! Under SACP-CBOR/1, any valid encoding is canonical; therefore, semantic equality for opaque
10//! payloads reduces to **byte equality**.
11//! - **Hot-path validation is allocation-free.**
12//! Use [`validate_canonical`] to validate a single CBOR data item with strict SACP-CBOR/1 rules.
13//! - **Encoding is streaming-first.**
14//! Use [`Encoder`] or [`cbor_bytes!`] to emit canonical bytes without building an owned tree.
15//!
16//! ## SACP-CBOR/1 profile (explicit)
17//!
18//! **Terminology note:** “canonical” in this crate means **canonical under the strict SACP-CBOR/1
19//! profile** defined here, not RFC 8949 canonical CBOR.
20//!
21//! **Allowed data model**
22//!
23//! - Single CBOR item only (no trailing bytes).
24//! - Definite-length items only (no indefinite-length encodings).
25//! - Map keys must be text strings (major 3) and valid UTF-8.
26//! - Only tags 2 and 3 are allowed (bignums), and bignums must be canonical and outside the safe-int range.
27//! - Integers (major 0/1) must be in the safe range `[-(2^53-1), +(2^53-1)]`.
28//! - Floats must be encoded as float64 (major 7, ai=27), forbid `-0.0`, and require the canonical NaN bit pattern.
29//! - Only simple values `false`, `true`, and `null` are allowed.
30//!
31//! **Canonical encoding constraints**
32//!
33//! - Minimal integer/length encoding (no overlong forms).
34//! - Map keys are strictly increasing by canonical CBOR key ordering:
35//! `(encoded length, then lexicographic encoded bytes)`.
36//!
37//! ## Feature flags
38//!
39//! - `std` *(default)*: implements `std::error::Error` for [`CborError`].
40//! - `alloc` *(default)*: enables owned canonical bytes (`CanonicalCbor`), editing, and encoding helpers.
41//! - `sha2` *(default)*: enables SHA-256 hashing helpers for canonical bytes.
42//! - `simdutf8`: enables SIMD-accelerated UTF-8 validation where supported.
43//! - `unsafe`: allows unchecked UTF-8 for canonical-trusted inputs.
44//!
45//! ## Safety
46//!
47//! This crate forbids `unsafe` code by default. Enabling the `unsafe` feature allows
48//! unchecked UTF-8 conversion on canonical-trusted inputs.
49//!
50//! ## `no_std`
51//!
52//! The crate is `no_std` compatible.
53//! - Validation-only usage works without `alloc`.
54//! - Owned APIs (canonical bytes + editor) require `alloc` and therefore an allocator provided by your environment.
55
56#![cfg_attr(not(feature = "std"), no_std)]
57#![cfg_attr(docsrs, feature(doc_cfg))]
58#![cfg_attr(not(feature = "unsafe"), forbid(unsafe_code))]
59#![deny(missing_docs)]
60#![deny(clippy::all)]
61#![warn(clippy::pedantic, clippy::nursery)]
62
63#[cfg(feature = "alloc")]
64extern crate alloc;
65
66#[cfg(feature = "alloc")]
67mod alloc_util;
68mod canonical;
69mod codec;
70mod error;
71mod limits;
72mod parse;
73mod profile;
74mod query;
75mod scalar;
76#[cfg(feature = "serde")]
77mod serde_impl;
78pub(crate) mod utf8;
79mod wire;
80
81#[cfg(feature = "alloc")]
82mod edit;
83#[cfg(feature = "alloc")]
84mod int;
85
86pub use crate::canonical::{CanonicalCborRef, EncodedTextKey};
87pub use crate::codec::{
88 decode, decode_canonical, ArrayDecoder, CborDecode, CheckedDecoder, Decoder, MapDecoder,
89 TrustedDecoder,
90};
91pub use crate::error::{CborError, ErrorCode};
92pub use crate::limits::{CborLimits, DecodeLimits};
93pub use crate::parse::{validate, validate_canonical};
94pub use crate::profile::{MAX_SAFE_INTEGER, MAX_SAFE_INTEGER_I64, MIN_SAFE_INTEGER};
95pub use crate::query::{
96 ArrayRef, BigIntRef, CborIntegerRef, CborKind, CborValueRef, MapRef, PathElem,
97};
98pub use crate::scalar::F64Bits;
99
100#[cfg(feature = "alloc")]
101mod encode;
102#[cfg(feature = "alloc")]
103mod macros;
104#[cfg(feature = "alloc")]
105mod value;
106#[cfg(feature = "alloc")]
107pub use crate::canonical::CanonicalCbor;
108#[cfg(feature = "alloc")]
109pub use crate::codec::{
110 decode_canonical_owned, encode_into, encode_to_canonical, encode_to_vec, CborArrayElem,
111 CborEncode, MapEntries,
112};
113#[cfg(feature = "alloc")]
114pub use crate::edit::{
115 ArrayPos, ArraySpliceBuilder, DeleteMode, EditEncode, EditOptions, EditValue, Editor, SetMode,
116};
117#[cfg(feature = "alloc")]
118pub use crate::encode::{ArrayEncoder, Encoder, MapEncoder};
119#[cfg(feature = "alloc")]
120#[doc(hidden)]
121pub use crate::macros::__cbor_macro;
122#[cfg(feature = "alloc")]
123pub use crate::value::{BigInt, CborInteger};
124#[cfg(feature = "alloc")]
125pub use sacp_cbor_derive::cbor_bytes;
126
127#[cfg(feature = "serde")]
128pub use crate::serde_impl::{
129 from_canonical_bytes, from_canonical_bytes_ref, from_slice, from_slice_borrowed, to_vec,
130 to_vec_sorted_maps, DeError,
131};
132
133pub use sacp_cbor_derive::{CborDecode, CborEncode};
134
135/// Construct a path slice for query/edit operations.
136#[macro_export]
137macro_rules! path {
138 ($($seg:expr),* $(,)?) => {
139 &[$($crate::__path_elem!($seg)),*]
140 };
141}
142
143#[doc(hidden)]
144#[macro_export]
145macro_rules! __path_elem {
146 ($seg:expr) => {
147 $crate::PathElem::from($seg)
148 };
149}