Skip to main content

vole_document/
accounting.rs

1//! Complete-cost accounting.
2//!
3//! A representation is never credited with an entropy estimate. Every byte
4//! that must be persisted to make a candidate decodable is charged, and only
5//! the complete serialized `.voldoc` size is authoritative for standalone
6//! compression claims.
7
8/// Physical byte attribution for a serialized descriptor.
9///
10/// These categories exist so that `inspect` can answer *where the bytes went*
11/// when a mechanism wins or loses. They sum to the exact `.voldoc` length.
12#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
13pub struct CostBreakdown {
14    /// Fixed header bytes.
15    pub header: u64,
16    /// Universe declaration record (payload + framing).
17    pub universe: u64,
18    /// Format-descriptor record (payload + framing).
19    pub format: u64,
20    /// Record framing overhead (tag/flags/length/crc) for all records.
21    pub record_framing: u64,
22    /// Object record payloads (raw byte objects).
23    pub objects: u64,
24    /// `EXTERNAL_REF` record payloads (Phase 9): 40 bytes per external object.
25    ///
26    /// The record framing is charged to [`CostBreakdown::record_framing`], as for
27    /// `OBJECT` records.
28    pub external_refs: u64,
29    /// Reconstruction-graph record payload.
30    pub graph: u64,
31    /// Entropy model records (0 in the exact core).
32    pub models: u64,
33    /// Entropy channel payloads (0 in the exact core).
34    pub entropy_payload: u64,
35    /// Typed residual records (0 in the exact core).
36    pub residuals: u64,
37    /// Index/checkpoint records.
38    pub index: u64,
39    /// Optional seek-directory record (payload + framing). Zero when absent.
40    pub directory: u64,
41    /// Optional checkpoint record (payload + framing). Zero when absent.
42    pub checkpoints: u64,
43    /// Integrity record payload.
44    pub integrity: u64,
45    /// Trailer record payload.
46    pub trailer: u64,
47}
48
49impl CostBreakdown {
50    /// Total serialized size in bytes.
51    pub fn total(&self) -> u64 {
52        self.header
53            + self.universe
54            + self.format
55            + self.record_framing
56            + self.objects
57            + self.external_refs
58            + self.graph
59            + self.models
60            + self.entropy_payload
61            + self.residuals
62            + self.index
63            + self.directory
64            + self.checkpoints
65            + self.integrity
66            + self.trailer
67    }
68
69    /// Stable-order JSON rendering for `inspect` and receipts.
70    pub fn to_json(&self) -> String {
71        let mut s = String::new();
72        s.push('{');
73        let fields: [(&str, u64); 15] = [
74            ("header", self.header),
75            ("universe", self.universe),
76            ("format", self.format),
77            ("record_framing", self.record_framing),
78            ("objects", self.objects),
79            ("external_refs", self.external_refs),
80            ("graph", self.graph),
81            ("models", self.models),
82            ("entropy_payload", self.entropy_payload),
83            ("residuals", self.residuals),
84            ("index", self.index),
85            ("directory", self.directory),
86            ("checkpoints", self.checkpoints),
87            ("integrity", self.integrity),
88            ("trailer", self.trailer),
89        ];
90        for (i, (name, value)) in fields.iter().enumerate() {
91            if i > 0 {
92                s.push(',');
93            }
94            s.push('"');
95            s.push_str(name);
96            s.push_str("\":");
97            s.push_str(&value.to_string());
98        }
99        s.push('}');
100        s
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn total_sums_all_fields() {
110        let c = CostBreakdown {
111            header: 64,
112            objects: 100,
113            record_framing: 24,
114            ..Default::default()
115        };
116        assert_eq!(c.total(), 188);
117    }
118
119    #[test]
120    fn json_has_all_fields() {
121        let j = CostBreakdown::default().to_json();
122        for k in [
123            "header",
124            "universe",
125            "format",
126            "record_framing",
127            "objects",
128            "external_refs",
129            "graph",
130            "models",
131            "entropy_payload",
132            "residuals",
133            "index",
134            "directory",
135            "checkpoints",
136            "integrity",
137            "trailer",
138        ] {
139            assert!(j.contains(k), "missing {k}");
140        }
141    }
142}