Skip to main content

vole_document/encode/
mod.rs

1//! Encoding: candidate portfolio, complete-cost court, and decode-before-commit.
2
3pub mod candidates;
4pub mod court;
5
6use crate::accounting::CostBreakdown;
7use crate::encode::candidates::CandidateKind;
8use crate::error::Result;
9use crate::integrity::{sha256, to_hex};
10use crate::limits::Limits;
11
12/// A report describing encode-time decisions and physical attribution.
13#[derive(Debug, Clone)]
14pub struct EncodeReport {
15    /// Winning candidate family.
16    pub kind: CandidateKind,
17    /// Source length in bytes.
18    pub source_len: u64,
19    /// Serialized `.voldoc` length in bytes.
20    pub encoded_len: u64,
21    /// Physical byte attribution of the serialized descriptor.
22    pub cost: CostBreakdown,
23    /// Lower-case hex SHA-256 of the source.
24    pub sha256_hex: String,
25    /// Number of candidates evaluated.
26    pub candidates_evaluated: u32,
27    /// Reconstruction work (DRA instruction count) of the winner.
28    pub graph_ops: usize,
29}
30
31impl EncodeReport {
32    /// Source bytes divided by encoded bytes (0.0 when the source is empty).
33    pub fn compression_ratio(&self) -> f64 {
34        if self.encoded_len == 0 {
35            return 0.0;
36        }
37        self.source_len as f64 / self.encoded_len as f64
38    }
39}
40
41/// Encode `input` exactly, returning the serialized descriptor and a report.
42///
43/// Every returned byte sequence has already been round-tripped through the
44/// normative decoder and byte-compared against `input`.
45pub fn encode(input: &[u8], limits: Limits) -> Result<(Vec<u8>, EncodeReport)> {
46    let cands = candidates::propose(input, limits)?;
47    let result = court::run(input, cands, limits)?;
48    let report = EncodeReport {
49        kind: result.kind,
50        source_len: input.len() as u64,
51        encoded_len: result.bytes.len() as u64,
52        cost: result.cost,
53        sha256_hex: to_hex(&sha256(input)),
54        candidates_evaluated: result.candidates_evaluated,
55        graph_ops: result.graph_ops,
56    };
57    Ok((result.bytes, report))
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn encode_decode_identity() {
66        let input: Vec<u8> = (0..=255u8).cycle().take(10_000).collect();
67        let (bytes, report) = encode(&input, Limits::DEFAULT).unwrap();
68        assert_eq!(report.source_len, input.len() as u64);
69        assert_eq!(report.encoded_len, bytes.len() as u64);
70        let (out, _) = crate::materialize::decode_to_bytes(&bytes, Limits::DEFAULT).unwrap();
71        assert_eq!(out, input);
72    }
73
74    #[test]
75    fn deterministic_output() {
76        let input = b"determinism check".to_vec();
77        let (a, _) = encode(&input, Limits::DEFAULT).unwrap();
78        let (b, _) = encode(&input, Limits::DEFAULT).unwrap();
79        assert_eq!(a, b);
80    }
81}