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}
41
42impl CandidateKind {
43 pub const fn name(self) -> &'static str {
45 match self {
46 CandidateKind::Raw => "RAW",
47 CandidateKind::Rle => "RLE",
48 CandidateKind::ByteRans => "BYTE_RANS",
49 CandidateKind::PdfPhysical => "PDF_PHYSICAL",
50 CandidateKind::PdfChannels => "PDF_CHANNELS",
51 CandidateKind::PdfLayout => "PDF_LAYOUT",
52 CandidateKind::PdfLayoutRans => "PDF_LAYOUT_RANS",
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct Candidate {
60 pub kind: CandidateKind,
62 pub descriptor: Descriptor,
64}
65
66pub fn propose_all(input: &[u8], limits: Limits) -> Result<Vec<Candidate>> {
77 let mut out = vec![Candidate {
78 kind: CandidateKind::Raw,
79 descriptor: opaque::propose(input, limits)?,
80 }];
81 if let Some(rle) = propose_rle(input, limits)? {
82 out.push(rle);
83 }
84 #[cfg(feature = "rans")]
85 if let Some(byte_rans) = propose_byte_rans(input, limits)? {
86 out.push(byte_rans);
87 }
88 if let Some(pdf) = crate::adapter::pdf::propose_pdf(input, limits)? {
89 out.push(pdf);
90 }
91 #[cfg(feature = "rans")]
92 if let Some(pdf_channels) = crate::adapter::pdf::propose_pdf_channels(input, limits)? {
93 out.push(pdf_channels);
94 }
95 if let Some(pdf_layout) = crate::adapter::pdf::propose_pdf_layout(input, limits)? {
96 out.push(pdf_layout);
97 }
98 #[cfg(feature = "rans")]
99 if let Some(pdf_layout_rans) = crate::adapter::pdf::propose_pdf_layout_rans(input, limits)? {
100 out.push(pdf_layout_rans);
101 }
102 Ok(out)
103}
104
105pub fn propose(input: &[u8], limits: Limits) -> Result<Vec<Candidate>> {
110 propose_all(input, limits)
111}
112
113pub fn propose_rle(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
126 let mut runs: Vec<(u8, u64)> = Vec::new();
128 for &b in input {
129 match runs.last_mut() {
130 Some((last, len)) if *last == b => *len += 1,
131 _ => runs.push((b, 1)),
132 }
133 }
134
135 if runs.len().saturating_mul(2) > limits.max_graph_ops as usize {
137 return Ok(None);
138 }
139
140 for &(_, length) in &runs {
142 let extra = length - 1;
143 if extra > limits.max_repeat_count || extra > u32::MAX as u64 {
144 return Ok(None);
145 }
146 }
147
148 let mut ops = Vec::with_capacity(runs.len() * 2);
149 for &(byte, length) in &runs {
150 ops.push(Op::Inline { bytes: vec![byte] });
151 if length > 1 {
152 ops.push(Op::RepeatLast {
153 count: (length - 1) as u32,
154 });
155 }
156 }
157
158 let descriptor = Descriptor {
159 universe: UNIVERSE.to_string(),
160 source_format: SOURCE_FORMAT_OPAQUE,
161 format_basis: "opaque;rle-runs".to_string(),
162 models: vec![],
163 channels: vec![],
164 objects: vec![],
165 program: Program::new(ops),
166 source_sha256: sha256(input),
167 source_len: input.len() as u64,
168 };
169 Ok(Some(Candidate {
170 kind: CandidateKind::Rle,
171 descriptor,
172 }))
173}
174
175#[cfg(feature = "rans")]
189pub fn propose_byte_rans(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
190 if input.is_empty() || input.len() as u64 > limits.max_channel_symbols {
191 return Ok(None);
192 }
193
194 let mut counts = [0u64; 256];
195 for &b in input {
196 counts[b as usize] += 1;
197 }
198 let model = EntropyModel::from_counts(&counts, 12)?;
199 let capsule = encode_channel(&model, input)?;
200
201 let channel = EntropyChannelDescriptor {
202 coder: CODER_ORDER0_BYTE_RANS,
203 coder_version: CODER_VERSION_1,
204 scale_bits: model.scale_bits,
205 lane_count: 1,
206 model_id: 0,
207 symbol_count: capsule.symbol_count,
208 decoded_length: capsule.decoded_length,
209 initial_state: capsule.initial_state,
210 payload: capsule.payload,
211 };
212
213 let descriptor = Descriptor {
214 universe: UNIVERSE.to_string(),
215 source_format: SOURCE_FORMAT_OPAQUE,
216 format_basis: "opaque;byte-rans".to_string(),
217 models: vec![model],
218 channels: vec![channel],
219 objects: vec![],
220 program: Program::new(vec![Op::DecodeChannel { channel_id: 0 }]),
221 source_sha256: sha256(input),
222 source_len: input.len() as u64,
223 };
224
225 Ok(Some(Candidate {
226 kind: CandidateKind::ByteRans,
227 descriptor,
228 }))
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 fn xorshift64(state: &mut u64) -> u64 {
237 let mut x = *state;
238 x ^= x << 13;
239 x ^= x >> 7;
240 x ^= x << 17;
241 *state = x;
242 x
243 }
244
245 fn xorshift_bytes(n: usize, seed: u64) -> Vec<u8> {
246 let mut state = seed | 1; let mut out = Vec::with_capacity(n + 8);
248 while out.len() < n {
249 out.extend_from_slice(&xorshift64(&mut state).to_le_bytes());
250 }
251 out.truncate(n);
252 out
253 }
254
255 fn assert_exact(bytes: &[u8], input: &[u8], limits: Limits) {
256 let (out, parsed) = crate::materialize::decode_to_bytes(bytes, limits).unwrap();
257 assert_eq!(out, input, "materialized bytes must equal the source");
258 assert_eq!(out.len() as u64, parsed.descriptor.source_len);
259 assert_eq!(
260 crate::integrity::sha256(&out),
261 crate::integrity::sha256(input)
262 );
263 }
264
265 #[test]
266 fn rle_wins_on_zeros() {
267 let input = vec![0u8; 65536];
268 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
269 assert_eq!(report.kind, CandidateKind::Rle);
270 assert!(
271 report.encoded_len < 512,
272 "RLE encoding of zeros was {} bytes",
273 report.encoded_len
274 );
275 assert_exact(&bytes, &input, Limits::DEFAULT);
276 }
277
278 #[test]
279 fn rle_wins_on_long_runs() {
280 let mut input = Vec::new();
281 input.extend_from_slice(&[0xAAu8; 1000]);
282 input.extend_from_slice(&[0x00, 0x01]);
283 input.extend_from_slice(&[0x55u8; 5000]);
284 input.extend_from_slice(b"tail");
285
286 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
287 assert_eq!(report.kind, CandidateKind::Rle);
288 assert!(report.encoded_len < 512);
289 assert_exact(&bytes, &input, Limits::DEFAULT);
290 }
291
292 #[test]
293 fn raw_wins_on_incompressible() {
294 let input = xorshift_bytes(64 * 1024, 0x9E37_79B9_7F4A_7C15);
295 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
296 assert_eq!(
297 report.kind,
298 CandidateKind::Raw,
299 "incompressible data must be stored RAW"
300 );
301 assert_exact(&bytes, &input, Limits::DEFAULT);
302 }
303
304 #[test]
305 fn single_byte_is_rle() {
306 let input = [7u8];
307 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
308 assert_eq!(report.kind, CandidateKind::Rle);
309 assert!(report.encoded_len < 512);
310 assert_exact(&bytes, &input, Limits::DEFAULT);
311 }
312
313 #[test]
314 fn rle_declines_when_graph_too_big() {
315 let input = vec![0u8; 100];
316 let limits = Limits {
317 max_graph_ops: 1,
318 ..Limits::DEFAULT
319 };
320 assert!(
321 propose_rle(&input, limits).unwrap().is_none(),
322 "100 single-byte runs cannot fit in a one-op graph"
323 );
324 let (bytes, report) = crate::encode::encode(&input, limits).unwrap();
328 assert_ne!(report.kind, CandidateKind::Rle);
329 assert_exact(&bytes, &input, limits);
330 }
331
332 #[cfg(feature = "rans")]
333 #[test]
334 fn byte_rans_wins_on_text() {
335 let input = b"The quick brown fox jumps over the lazy dog. ".repeat(1500);
336 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
337 assert_eq!(report.kind, CandidateKind::ByteRans);
338 assert!(
339 report.encoded_len < report.source_len,
340 "order-0 rANS must beat RAW on low-entropy text: {} vs {}",
341 report.encoded_len,
342 report.source_len
343 );
344 assert_exact(&bytes, &input, Limits::DEFAULT);
345 }
346
347 #[cfg(feature = "rans")]
348 #[test]
349 fn byte_rans_exact_on_all_byte_values() {
350 let mut input: Vec<u8> = (0..=255u8).collect();
355 let mut state = 0x2545_F491_4F6C_DD1D;
356 while input.len() < 64 * 1024 {
357 let r = xorshift64(&mut state);
358 if !r.is_multiple_of(8) {
359 input.push(0x00);
360 } else {
361 input.push((r >> 32) as u8);
362 }
363 }
364 input.truncate(64 * 1024);
365
366 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
367 assert_eq!(report.kind, CandidateKind::ByteRans);
368 assert_exact(&bytes, &input, Limits::DEFAULT);
369 }
370
371 #[test]
372 fn byte_rans_is_deterministic() {
373 let input = b"deterministic byte rANS stream ".repeat(600);
374 let (a, _) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
375 let (b, _) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
376 assert_eq!(a, b, ".voldoc bytes must be identical across encodes");
377 }
378
379 #[cfg(feature = "rans")]
380 #[test]
381 fn byte_rans_declines_empty() {
382 assert!(
383 propose_byte_rans(&[], Limits::DEFAULT).unwrap().is_none(),
384 "empty input must decline: RAW is trivially smaller"
385 );
386 }
387
388 #[cfg(feature = "rans")]
389 #[test]
390 fn model_cost_is_charged() {
391 let input = b"ab";
395 let (bytes, report) = crate::encode::encode(input, Limits::DEFAULT).unwrap();
396 assert_ne!(report.kind, CandidateKind::ByteRans);
397 assert_exact(&bytes, input, Limits::DEFAULT);
398 }
399}