Skip to main content

vole_document/encode/
candidates.rs

1//! Candidate generators.
2//!
3//! Each generator proposes a bounded, deterministic reconstruction hypothesis.
4//! Generators are cheap to decline and never trusted because their logic
5//! "looks obvious": every candidate reaches the common court.
6
7use crate::SOURCE_FORMAT_OPAQUE;
8use crate::adapter::opaque;
9use crate::container::{Descriptor, UNIVERSE};
10use crate::dra::{Op, Program};
11#[cfg(feature = "rans")]
12use crate::entropy::{
13    CODER_ORDER0_BYTE_RANS, CODER_VERSION_1, EntropyChannelDescriptor, EntropyModel, encode_channel,
14};
15use crate::error::Result;
16use crate::integrity::sha256;
17use crate::limits::Limits;
18
19/// Stable candidate identity. The discriminant order is the final
20/// deterministic tie-breaker when prices and work are equal.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
22#[repr(u8)]
23pub enum CandidateKind {
24    /// Single literal object, literal program (Phase 1 floor).
25    Raw = 0,
26    /// Run-length generated repeats (Phase 2).
27    Rle = 1,
28    /// Typed byte rANS entropy channel (Phase 2).
29    ByteRans = 2,
30    /// PDF physical span partition as literal-span DRA ops (Phase 3).
31    PdfPhysical = 3,
32    /// PDF typed lexical channels, each entropy-coded (Phase 4).
33    PdfChannels = 4,
34    /// PDF classic cross-reference offsets regenerated from marked positions
35    /// (Phase 5).
36    PdfLayout = 5,
37    /// PDF layout plan (data object + item table) carried through two rANS
38    /// entropy channels (Phase 5.8).
39    PdfLayoutRans = 6,
40    /// PDF exact DEFLATE replay: eligible `/FlateDecode` streams are
41    /// inverse-proceduralized to (plaintext, corrections) and replayed (Phase 6).
42    PdfDeflateReplay = 7,
43    /// PDF DEFLATE replay whose plaintexts are carried as shared order-0 byte-rANS
44    /// entropy channels (Phase 6.5).
45    PdfDeflateReplayRans = 8,
46    /// `PDF_DEFLATE_REPLAY_RANS` plus an advisory `OBSERVATION_INDEX` built from
47    /// the same physical scan, so narrow views can be served without walking the
48    /// whole program (Phase 7.3).
49    PdfDeflateReplayRansIndexed = 9,
50}
51
52impl CandidateKind {
53    /// Stable short name for reports and receipts.
54    pub const fn name(self) -> &'static str {
55        match self {
56            CandidateKind::Raw => "RAW",
57            CandidateKind::Rle => "RLE",
58            CandidateKind::ByteRans => "BYTE_RANS",
59            CandidateKind::PdfPhysical => "PDF_PHYSICAL",
60            CandidateKind::PdfChannels => "PDF_CHANNELS",
61            CandidateKind::PdfLayout => "PDF_LAYOUT",
62            CandidateKind::PdfLayoutRans => "PDF_LAYOUT_RANS",
63            CandidateKind::PdfDeflateReplay => "PDF_DEFLATE_REPLAY",
64            CandidateKind::PdfDeflateReplayRans => "PDF_DEFLATE_REPLAY_RANS",
65            CandidateKind::PdfDeflateReplayRansIndexed => "PDF_DEFLATE_REPLAY_RANS_INDEXED",
66        }
67    }
68}
69
70/// A proposed descriptor together with its originating family.
71#[derive(Debug, Clone)]
72pub struct Candidate {
73    /// Which family produced this proposal.
74    pub kind: CandidateKind,
75    /// The proposed descriptor.
76    pub descriptor: Descriptor,
77}
78
79/// Generate the complete bounded candidate set for `input`.
80///
81/// Every candidate that currently applies is returned: RAW (always), then RLE,
82/// BYTE_RANS, PDF_PHYSICAL, PDF_CHANNELS, and PDF_LAYOUT, each only when its
83/// generator can express the input within `limits`. Order is deterministic and
84/// matches the [`CandidateKind`] discriminant order, so the court's final
85/// tie-break is stable.
86///
87/// This is the honest ablation surface: forcing a single kind must select from
88/// exactly the same set the unforced court would have priced.
89pub fn propose_all(input: &[u8], limits: Limits) -> Result<Vec<Candidate>> {
90    let mut out = vec![Candidate {
91        kind: CandidateKind::Raw,
92        descriptor: opaque::propose(input, limits)?,
93    }];
94    if let Some(rle) = propose_rle(input, limits)? {
95        out.push(rle);
96    }
97    #[cfg(feature = "rans")]
98    if let Some(byte_rans) = propose_byte_rans(input, limits)? {
99        out.push(byte_rans);
100    }
101    if let Some(pdf) = crate::adapter::pdf::propose_pdf(input, limits)? {
102        out.push(pdf);
103    }
104    #[cfg(feature = "rans")]
105    if let Some(pdf_channels) = crate::adapter::pdf::propose_pdf_channels(input, limits)? {
106        out.push(pdf_channels);
107    }
108    if let Some(pdf_layout) = crate::adapter::pdf::propose_pdf_layout(input, limits)? {
109        out.push(pdf_layout);
110    }
111    #[cfg(feature = "rans")]
112    if let Some(pdf_layout_rans) = crate::adapter::pdf::propose_pdf_layout_rans(input, limits)? {
113        out.push(pdf_layout_rans);
114    }
115    #[cfg(feature = "deflate-replay")]
116    if let Some(pdf_deflate) = crate::adapter::pdf::propose_pdf_deflate_replay(input, limits)? {
117        out.push(pdf_deflate);
118    }
119    #[cfg(all(feature = "deflate-replay", feature = "rans"))]
120    if let Some(pdf_deflate_rans) =
121        crate::adapter::pdf::propose_pdf_deflate_replay_rans(input, limits)?
122    {
123        out.push(pdf_deflate_rans);
124    }
125    #[cfg(all(feature = "deflate-replay", feature = "rans"))]
126    if let Some(pdf_deflate_rans_indexed) =
127        crate::adapter::pdf::propose_pdf_deflate_replay_rans_indexed(input, limits)?
128    {
129        out.push(pdf_deflate_rans_indexed);
130    }
131    Ok(out)
132}
133
134/// Generate the bounded candidate set for `input`.
135///
136/// Thin alias for [`propose_all`] kept for existing callers; it proposes exactly
137/// the same set in the same order.
138pub fn propose(input: &[u8], limits: Limits) -> Result<Vec<Candidate>> {
139    propose_all(input, limits)
140}
141
142/// Propose an inline-run + `REPEAT_LAST` representation of `input`.
143///
144/// Each maximal run of equal bytes becomes one `INLINE` of that single byte,
145/// optionally followed by a `REPEAT_LAST` repeating it `length - 1` more times.
146/// The sequence is therefore always `INLINE, REPEAT_LAST, INLINE, ...`, so no
147/// two `REPEAT_LAST` instructions are ever adjacent. `objects` is empty because
148/// every byte is carried in the graph.
149///
150/// Returns `Ok(None)`, declining honestly, when the run list cannot be
151/// expressed within `limits`: the program would need more instructions than
152/// `max_graph_ops`, or a run is too long to reconstruct with a single
153/// `REPEAT_LAST` (`length - 1` exceeds `max_repeat_count` or `u32::MAX`).
154pub fn propose_rle(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
155    // Maximal runs of equal bytes: (byte value, run length).
156    let mut runs: Vec<(u8, u64)> = Vec::new();
157    for &b in input {
158        match runs.last_mut() {
159            Some((last, len)) if *last == b => *len += 1,
160            _ => runs.push((b, 1)),
161        }
162    }
163
164    // Worst case is one INLINE plus one REPEAT_LAST per run.
165    if runs.len().saturating_mul(2) > limits.max_graph_ops as usize {
166        return Ok(None);
167    }
168
169    // A run must be reconstructible by a single INLINE + REPEAT_LAST pair.
170    for &(_, length) in &runs {
171        let extra = length - 1;
172        if extra > limits.max_repeat_count || extra > u32::MAX as u64 {
173            return Ok(None);
174        }
175    }
176
177    let mut ops = Vec::with_capacity(runs.len() * 2);
178    for &(byte, length) in &runs {
179        ops.push(Op::Inline { bytes: vec![byte] });
180        if length > 1 {
181            ops.push(Op::RepeatLast {
182                count: (length - 1) as u32,
183            });
184        }
185    }
186
187    let descriptor = Descriptor {
188        universe: UNIVERSE.to_string(),
189        source_format: SOURCE_FORMAT_OPAQUE,
190        format_basis: "opaque;rle-runs".to_string(),
191        models: vec![],
192        channels: vec![],
193        objects: vec![],
194        program: Program::new(ops),
195        observation_index: None,
196        seek_directory: false,
197        source_sha256: sha256(input),
198        source_len: input.len() as u64,
199    };
200    Ok(Some(Candidate {
201        kind: CandidateKind::Rle,
202        descriptor,
203    }))
204}
205
206/// Propose an order-0 byte-rANS representation of the whole `input` as one
207/// entropy channel.
208///
209/// The 256-entry normalized model and the channel header are fully serialized
210/// and charged by the court, so this candidate only wins when order-0 coding
211/// recovers more bytes than the model costs. Declines (`Ok(None)`) honestly:
212///
213/// - empty input: RAW is trivially smaller and there is nothing to code;
214/// - `input.len() > limits.max_channel_symbols`: a single channel cannot carry
215///   it, so the candidate is not expressible within the declared bounds.
216///
217/// Determinism follows from the pure `from_counts` normalizer and from
218/// `encode_channel`, which both depend only on their inputs.
219#[cfg(feature = "rans")]
220pub fn propose_byte_rans(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
221    if input.is_empty() || input.len() as u64 > limits.max_channel_symbols {
222        return Ok(None);
223    }
224
225    let mut counts = [0u64; 256];
226    for &b in input {
227        counts[b as usize] += 1;
228    }
229    let model = EntropyModel::from_counts(&counts, 12)?;
230    let capsule = encode_channel(&model, input)?;
231
232    let channel = EntropyChannelDescriptor {
233        coder: CODER_ORDER0_BYTE_RANS,
234        coder_version: CODER_VERSION_1,
235        scale_bits: model.scale_bits,
236        lane_count: 1,
237        model_id: 0,
238        symbol_count: capsule.symbol_count,
239        decoded_length: capsule.decoded_length,
240        initial_state: capsule.initial_state,
241        payload: capsule.payload,
242    };
243
244    let descriptor = Descriptor {
245        universe: UNIVERSE.to_string(),
246        source_format: SOURCE_FORMAT_OPAQUE,
247        format_basis: "opaque;byte-rans".to_string(),
248        models: vec![model],
249        channels: vec![channel],
250        objects: vec![],
251        program: Program::new(vec![Op::DecodeChannel { channel_id: 0 }]),
252        observation_index: None,
253        seek_directory: false,
254        source_sha256: sha256(input),
255        source_len: input.len() as u64,
256    };
257
258    Ok(Some(Candidate {
259        kind: CandidateKind::ByteRans,
260        descriptor,
261    }))
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267
268    /// Deterministic xorshift64 PRNG for incompressible test data.
269    fn xorshift64(state: &mut u64) -> u64 {
270        let mut x = *state;
271        x ^= x << 13;
272        x ^= x >> 7;
273        x ^= x << 17;
274        *state = x;
275        x
276    }
277
278    fn xorshift_bytes(n: usize, seed: u64) -> Vec<u8> {
279        let mut state = seed | 1; // avoid the zero fixed point
280        let mut out = Vec::with_capacity(n + 8);
281        while out.len() < n {
282            out.extend_from_slice(&xorshift64(&mut state).to_le_bytes());
283        }
284        out.truncate(n);
285        out
286    }
287
288    fn assert_exact(bytes: &[u8], input: &[u8], limits: Limits) {
289        let (out, parsed) = crate::materialize::decode_to_bytes(bytes, limits).unwrap();
290        assert_eq!(out, input, "materialized bytes must equal the source");
291        assert_eq!(out.len() as u64, parsed.descriptor.source_len);
292        assert_eq!(
293            crate::integrity::sha256(&out),
294            crate::integrity::sha256(input)
295        );
296    }
297
298    #[test]
299    fn rle_wins_on_zeros() {
300        let input = vec![0u8; 65536];
301        let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
302        assert_eq!(report.kind, CandidateKind::Rle);
303        assert!(
304            report.encoded_len < 512,
305            "RLE encoding of zeros was {} bytes",
306            report.encoded_len
307        );
308        assert_exact(&bytes, &input, Limits::DEFAULT);
309    }
310
311    #[test]
312    fn rle_wins_on_long_runs() {
313        let mut input = Vec::new();
314        input.extend_from_slice(&[0xAAu8; 1000]);
315        input.extend_from_slice(&[0x00, 0x01]);
316        input.extend_from_slice(&[0x55u8; 5000]);
317        input.extend_from_slice(b"tail");
318
319        let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
320        assert_eq!(report.kind, CandidateKind::Rle);
321        assert!(report.encoded_len < 512);
322        assert_exact(&bytes, &input, Limits::DEFAULT);
323    }
324
325    #[test]
326    fn raw_wins_on_incompressible() {
327        let input = xorshift_bytes(64 * 1024, 0x9E37_79B9_7F4A_7C15);
328        let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
329        assert_eq!(
330            report.kind,
331            CandidateKind::Raw,
332            "incompressible data must be stored RAW"
333        );
334        assert_exact(&bytes, &input, Limits::DEFAULT);
335    }
336
337    #[test]
338    fn single_byte_is_rle() {
339        let input = [7u8];
340        let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
341        assert_eq!(report.kind, CandidateKind::Rle);
342        assert!(report.encoded_len < 512);
343        assert_exact(&bytes, &input, Limits::DEFAULT);
344    }
345
346    #[test]
347    fn rle_declines_when_graph_too_big() {
348        let input = vec![0u8; 100];
349        let limits = Limits {
350            max_graph_ops: 1,
351            ..Limits::DEFAULT
352        };
353        assert!(
354            propose_rle(&input, limits).unwrap().is_none(),
355            "100 single-byte runs cannot fit in a one-op graph"
356        );
357        // With RLE declined, the court must fall back to another exact lane.
358        // (The compact v2 model makes BYTE_RANS the winner here; whether it or
359        // RAW wins depends on model cost, so only "not RLE" is pinned.)
360        let (bytes, report) = crate::encode::encode(&input, limits).unwrap();
361        assert_ne!(report.kind, CandidateKind::Rle);
362        assert_exact(&bytes, &input, limits);
363    }
364
365    #[cfg(feature = "rans")]
366    #[test]
367    fn byte_rans_wins_on_text() {
368        let input = b"The quick brown fox jumps over the lazy dog. ".repeat(1500);
369        let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
370        assert_eq!(report.kind, CandidateKind::ByteRans);
371        assert!(
372            report.encoded_len < report.source_len,
373            "order-0 rANS must beat RAW on low-entropy text: {} vs {}",
374            report.encoded_len,
375            report.source_len
376        );
377        assert_exact(&bytes, &input, Limits::DEFAULT);
378    }
379
380    #[cfg(feature = "rans")]
381    #[test]
382    fn byte_rans_exact_on_all_byte_values() {
383        // Exercise every byte value through the channel. A uniform `0..=255`
384        // stream is incompressible at order 0 (and the 516-byte model makes rANS
385        // lose to RAW), so the body is a skewed, deterministic stream whose head
386        // guarantees all 256 symbols are present and nonzero-frequency.
387        let mut input: Vec<u8> = (0..=255u8).collect();
388        let mut state = 0x2545_F491_4F6C_DD1D;
389        while input.len() < 64 * 1024 {
390            let r = xorshift64(&mut state);
391            if !r.is_multiple_of(8) {
392                input.push(0x00);
393            } else {
394                input.push((r >> 32) as u8);
395            }
396        }
397        input.truncate(64 * 1024);
398
399        let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
400        assert_eq!(report.kind, CandidateKind::ByteRans);
401        assert_exact(&bytes, &input, Limits::DEFAULT);
402    }
403
404    #[test]
405    fn byte_rans_is_deterministic() {
406        let input = b"deterministic byte rANS stream ".repeat(600);
407        let (a, _) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
408        let (b, _) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
409        assert_eq!(a, b, ".voldoc bytes must be identical across encodes");
410    }
411
412    #[cfg(feature = "rans")]
413    #[test]
414    fn byte_rans_declines_empty() {
415        assert!(
416            propose_byte_rans(&[], Limits::DEFAULT).unwrap().is_none(),
417            "empty input must decline: RAW is trivially smaller"
418        );
419    }
420
421    #[cfg(feature = "rans")]
422    #[test]
423    fn model_cost_is_charged() {
424        // On a two-byte input the 516-byte canonical model cannot pay for
425        // itself, so BYTE_RANS must lose the complete-cost court. This documents
426        // model-cost honesty.
427        let input = b"ab";
428        let (bytes, report) = crate::encode::encode(input, Limits::DEFAULT).unwrap();
429        assert_ne!(report.kind, CandidateKind::ByteRans);
430        assert_exact(&bytes, input, Limits::DEFAULT);
431    }
432}