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}
31
32impl CandidateKind {
33 pub const fn name(self) -> &'static str {
35 match self {
36 CandidateKind::Raw => "RAW",
37 CandidateKind::Rle => "RLE",
38 CandidateKind::ByteRans => "BYTE_RANS",
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct Candidate {
46 pub kind: CandidateKind,
48 pub descriptor: Descriptor,
50}
51
52pub fn propose(input: &[u8], limits: Limits) -> Result<Vec<Candidate>> {
56 let mut out = vec![Candidate {
57 kind: CandidateKind::Raw,
58 descriptor: opaque::propose(input, limits)?,
59 }];
60 if let Some(rle) = propose_rle(input, limits)? {
61 out.push(rle);
62 }
63 #[cfg(feature = "rans")]
64 if let Some(byte_rans) = propose_byte_rans(input, limits)? {
65 out.push(byte_rans);
66 }
67 Ok(out)
68}
69
70pub fn propose_rle(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
83 let mut runs: Vec<(u8, u64)> = Vec::new();
85 for &b in input {
86 match runs.last_mut() {
87 Some((last, len)) if *last == b => *len += 1,
88 _ => runs.push((b, 1)),
89 }
90 }
91
92 if runs.len().saturating_mul(2) > limits.max_graph_ops as usize {
94 return Ok(None);
95 }
96
97 for &(_, length) in &runs {
99 let extra = length - 1;
100 if extra > limits.max_repeat_count || extra > u32::MAX as u64 {
101 return Ok(None);
102 }
103 }
104
105 let mut ops = Vec::with_capacity(runs.len() * 2);
106 for &(byte, length) in &runs {
107 ops.push(Op::Inline { bytes: vec![byte] });
108 if length > 1 {
109 ops.push(Op::RepeatLast {
110 count: (length - 1) as u32,
111 });
112 }
113 }
114
115 let descriptor = Descriptor {
116 universe: UNIVERSE.to_string(),
117 source_format: SOURCE_FORMAT_OPAQUE,
118 format_basis: "opaque;rle-runs".to_string(),
119 models: vec![],
120 channels: vec![],
121 objects: vec![],
122 program: Program::new(ops),
123 source_sha256: sha256(input),
124 source_len: input.len() as u64,
125 };
126 Ok(Some(Candidate {
127 kind: CandidateKind::Rle,
128 descriptor,
129 }))
130}
131
132#[cfg(feature = "rans")]
146pub fn propose_byte_rans(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
147 if input.is_empty() || input.len() as u64 > limits.max_channel_symbols {
148 return Ok(None);
149 }
150
151 let mut counts = [0u64; 256];
152 for &b in input {
153 counts[b as usize] += 1;
154 }
155 let model = EntropyModel::from_counts(&counts, 12)?;
156 let capsule = encode_channel(&model, input)?;
157
158 let channel = EntropyChannelDescriptor {
159 coder: CODER_ORDER0_BYTE_RANS,
160 coder_version: CODER_VERSION_1,
161 scale_bits: model.scale_bits,
162 lane_count: 1,
163 model_id: 0,
164 symbol_count: capsule.symbol_count,
165 decoded_length: capsule.decoded_length,
166 initial_state: capsule.initial_state,
167 payload: capsule.payload,
168 };
169
170 let descriptor = Descriptor {
171 universe: UNIVERSE.to_string(),
172 source_format: SOURCE_FORMAT_OPAQUE,
173 format_basis: "opaque;byte-rans".to_string(),
174 models: vec![model],
175 channels: vec![channel],
176 objects: vec![],
177 program: Program::new(vec![Op::DecodeChannel { channel_id: 0 }]),
178 source_sha256: sha256(input),
179 source_len: input.len() as u64,
180 };
181
182 Ok(Some(Candidate {
183 kind: CandidateKind::ByteRans,
184 descriptor,
185 }))
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 fn xorshift64(state: &mut u64) -> u64 {
194 let mut x = *state;
195 x ^= x << 13;
196 x ^= x >> 7;
197 x ^= x << 17;
198 *state = x;
199 x
200 }
201
202 fn xorshift_bytes(n: usize, seed: u64) -> Vec<u8> {
203 let mut state = seed | 1; let mut out = Vec::with_capacity(n + 8);
205 while out.len() < n {
206 out.extend_from_slice(&xorshift64(&mut state).to_le_bytes());
207 }
208 out.truncate(n);
209 out
210 }
211
212 fn assert_exact(bytes: &[u8], input: &[u8], limits: Limits) {
213 let (out, parsed) = crate::materialize::decode_to_bytes(bytes, limits).unwrap();
214 assert_eq!(out, input, "materialized bytes must equal the source");
215 assert_eq!(out.len() as u64, parsed.descriptor.source_len);
216 assert_eq!(
217 crate::integrity::sha256(&out),
218 crate::integrity::sha256(input)
219 );
220 }
221
222 #[test]
223 fn rle_wins_on_zeros() {
224 let input = vec![0u8; 65536];
225 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
226 assert_eq!(report.kind, CandidateKind::Rle);
227 assert!(
228 report.encoded_len < 512,
229 "RLE encoding of zeros was {} bytes",
230 report.encoded_len
231 );
232 assert_exact(&bytes, &input, Limits::DEFAULT);
233 }
234
235 #[test]
236 fn rle_wins_on_long_runs() {
237 let mut input = Vec::new();
238 input.extend_from_slice(&[0xAAu8; 1000]);
239 input.extend_from_slice(&[0x00, 0x01]);
240 input.extend_from_slice(&[0x55u8; 5000]);
241 input.extend_from_slice(b"tail");
242
243 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
244 assert_eq!(report.kind, CandidateKind::Rle);
245 assert!(report.encoded_len < 512);
246 assert_exact(&bytes, &input, Limits::DEFAULT);
247 }
248
249 #[test]
250 fn raw_wins_on_incompressible() {
251 let input = xorshift_bytes(64 * 1024, 0x9E37_79B9_7F4A_7C15);
252 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
253 assert_eq!(
254 report.kind,
255 CandidateKind::Raw,
256 "incompressible data must be stored RAW"
257 );
258 assert_exact(&bytes, &input, Limits::DEFAULT);
259 }
260
261 #[test]
262 fn single_byte_is_rle() {
263 let input = [7u8];
264 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
265 assert_eq!(report.kind, CandidateKind::Rle);
266 assert!(report.encoded_len < 512);
267 assert_exact(&bytes, &input, Limits::DEFAULT);
268 }
269
270 #[test]
271 fn rle_declines_when_graph_too_big() {
272 let input = vec![0u8; 100];
273 let limits = Limits {
274 max_graph_ops: 1,
275 ..Limits::DEFAULT
276 };
277 assert!(
278 propose_rle(&input, limits).unwrap().is_none(),
279 "100 single-byte runs cannot fit in a one-op graph"
280 );
281 let (bytes, report) = crate::encode::encode(&input, limits).unwrap();
283 assert_eq!(report.kind, CandidateKind::Raw);
284 assert_exact(&bytes, &input, limits);
285 }
286
287 #[cfg(feature = "rans")]
288 #[test]
289 fn byte_rans_wins_on_text() {
290 let input = b"The quick brown fox jumps over the lazy dog. ".repeat(1500);
291 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
292 assert_eq!(report.kind, CandidateKind::ByteRans);
293 assert!(
294 report.encoded_len < report.source_len,
295 "order-0 rANS must beat RAW on low-entropy text: {} vs {}",
296 report.encoded_len,
297 report.source_len
298 );
299 assert_exact(&bytes, &input, Limits::DEFAULT);
300 }
301
302 #[cfg(feature = "rans")]
303 #[test]
304 fn byte_rans_exact_on_all_byte_values() {
305 let mut input: Vec<u8> = (0..=255u8).collect();
310 let mut state = 0x2545_F491_4F6C_DD1D;
311 while input.len() < 64 * 1024 {
312 let r = xorshift64(&mut state);
313 if !r.is_multiple_of(8) {
314 input.push(0x00);
315 } else {
316 input.push((r >> 32) as u8);
317 }
318 }
319 input.truncate(64 * 1024);
320
321 let (bytes, report) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
322 assert_eq!(report.kind, CandidateKind::ByteRans);
323 assert_exact(&bytes, &input, Limits::DEFAULT);
324 }
325
326 #[test]
327 fn byte_rans_is_deterministic() {
328 let input = b"deterministic byte rANS stream ".repeat(600);
329 let (a, _) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
330 let (b, _) = crate::encode::encode(&input, Limits::DEFAULT).unwrap();
331 assert_eq!(a, b, ".voldoc bytes must be identical across encodes");
332 }
333
334 #[cfg(feature = "rans")]
335 #[test]
336 fn byte_rans_declines_empty() {
337 assert!(
338 propose_byte_rans(&[], Limits::DEFAULT).unwrap().is_none(),
339 "empty input must decline: RAW is trivially smaller"
340 );
341 }
342
343 #[cfg(feature = "rans")]
344 #[test]
345 fn model_cost_is_charged() {
346 let input = b"ab";
350 let (bytes, report) = crate::encode::encode(input, Limits::DEFAULT).unwrap();
351 assert_ne!(report.kind, CandidateKind::ByteRans);
352 assert_exact(&bytes, input, Limits::DEFAULT);
353 }
354}