1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
22#[repr(u8)]
23pub enum CandidateKind {
24 Raw = 0,
26 Rle = 1,
28 ByteRans = 2,
30 PdfPhysical = 3,
32 PdfChannels = 4,
34 PdfLayout = 5,
37 PdfLayoutRans = 6,
40 PdfDeflateReplay = 7,
43 PdfDeflateReplayRans = 8,
46 PdfDeflateReplayRansIndexed = 9,
50}
51
52impl CandidateKind {
53 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#[derive(Debug, Clone)]
72pub struct Candidate {
73 pub kind: CandidateKind,
75 pub descriptor: Descriptor,
77}
78
79pub 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
134pub fn propose(input: &[u8], limits: Limits) -> Result<Vec<Candidate>> {
139 propose_all(input, limits)
140}
141
142pub fn propose_rle(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
155 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 if runs.len().saturating_mul(2) > limits.max_graph_ops as usize {
166 return Ok(None);
167 }
168
169 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#[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 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; 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 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 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 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}