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