1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(feature = "nightly", feature(optimize_attribute))]
3#![cfg_attr(feature = "paranoid", forbid(unsafe_code))]
4
5#[cfg(feature = "alloc")]
6extern crate alloc;
7
8pub(crate) mod block_decoder;
9#[cfg(feature = "std")]
10pub mod context;
11pub(crate) mod exec;
12pub(crate) mod literals;
13pub(crate) mod ring_buffer;
14pub(crate) mod sequences;
15#[cfg(feature = "std")]
16pub mod streaming;
17
18#[cfg(all(
19 any(target_arch = "x86_64", target_arch = "aarch64"),
20 not(feature = "paranoid")
21))]
22pub(crate) mod simd_decode;
23
24#[cfg(feature = "alloc")]
25use alloc::boxed::Box;
26#[cfg(feature = "alloc")]
27use alloc::vec::Vec;
28
29use crate::exec::decode_execute_sequences;
30use crate::literals::decode_literals_ws;
31use crate::sequences::{SequenceDecodeTables, parse_sequence_count, parse_sequence_tables_ws};
32use zrip_core::block::{BlockType, parse_block_header};
33use zrip_core::error::DecompressError;
34use zrip_core::frame::MAX_WINDOW_SIZE;
35use zrip_core::frame::header::parse_frame_header;
36use zrip_core::huffman::HuffmanDecodeEntry;
37#[cfg(all(
38 any(target_arch = "x86_64", target_arch = "aarch64"),
39 not(feature = "paranoid")
40))]
41use zrip_core::simd::CpuTier;
42use zrip_core::xxhash::Xxh64State;
43
44pub(crate) struct BlockDecodeWorkspace {
45 pub literal_buf: Vec<u8>,
46 pub huf_table: Vec<HuffmanDecodeEntry>,
47 pub huf_table_log: u8,
48 pub huf_valid: bool,
49 pub huf_all_weights: Vec<u8>,
50 pub huf_rank_count: Vec<u32>,
51 pub huf_rank_start: Vec<u32>,
52 pub fse_dist: Vec<i16>,
53 pub fse_symbol_next: Vec<u16>,
54 pub fse_build_buf: Vec<zrip_core::fse::FseDecodeEntry>,
55}
56
57impl BlockDecodeWorkspace {
58 pub(crate) fn new() -> Self {
59 Self {
60 literal_buf: Vec::new(),
61 huf_table: Vec::new(),
62 huf_table_log: 0,
63 huf_valid: false,
64 huf_all_weights: Vec::new(),
65 huf_rank_count: Vec::new(),
66 huf_rank_start: Vec::new(),
67 fse_dist: Vec::new(),
68 fse_symbol_next: Vec::new(),
69 fse_build_buf: Vec::new(),
70 }
71 }
72}
73
74pub(crate) fn skip_skippable_frame(data: &[u8]) -> Option<usize> {
75 if data.len() < 8 {
76 return None;
77 }
78 let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
79 if (magic & 0xFFFF_FFF0) != 0x184D_2A50 {
80 return None;
81 }
82 let frame_size = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as usize;
83 let total = 8 + frame_size;
84 if total > data.len() {
85 return None;
86 }
87 Some(total)
88}
89
90pub fn decompress(input: &[u8]) -> Result<Vec<u8>, DecompressError> {
91 decompress_with_dict(input, None)
92}
93
94pub fn decompress_with_limit(
100 input: &[u8],
101 max_output_size: usize,
102) -> Result<Vec<u8>, DecompressError> {
103 let mut output = Vec::new();
104 let mut ws = Box::new(BlockDecodeWorkspace::new());
105 let mut offset = 0;
106 while offset < input.len() {
107 let remaining = &input[offset..];
108 if let Some(skip_len) = skip_skippable_frame(remaining) {
109 offset += skip_len;
110 continue;
111 }
112 let consumed = decompress_frame(remaining, &mut output, max_output_size, None, &mut ws)?;
113 offset += consumed;
114 }
115 Ok(output)
116}
117
118pub fn decompress_into(input: &[u8], output: &mut Vec<u8>) -> Result<usize, DecompressError> {
119 let max_output = zrip_core::DEFAULT_DECOMPRESS_LIMIT;
120 let mut ws = Box::new(BlockDecodeWorkspace::new());
121 let start = output.len();
122 let mut offset = 0;
123 while offset < input.len() {
124 let remaining = &input[offset..];
125 if let Some(skip_len) = skip_skippable_frame(remaining) {
126 offset += skip_len;
127 continue;
128 }
129 let consumed = decompress_frame(remaining, output, max_output, None, &mut ws)?;
130 offset += consumed;
131 }
132 Ok(output.len() - start)
133}
134
135pub fn decompress_with_dict(
136 input: &[u8],
137 dict: Option<&zrip_core::dict::Dictionary>,
138) -> Result<Vec<u8>, DecompressError> {
139 let max_output = zrip_core::DEFAULT_DECOMPRESS_LIMIT;
140 let mut output = Vec::new();
141 let mut ws = Box::new(BlockDecodeWorkspace::new());
142 let mut offset = 0;
143
144 while offset < input.len() {
145 let remaining = &input[offset..];
146 if let Some(skip_len) = skip_skippable_frame(remaining) {
147 offset += skip_len;
148 continue;
149 }
150 let consumed = decompress_frame(remaining, &mut output, max_output, dict, &mut ws)?;
151 offset += consumed;
152 }
153
154 Ok(output)
155}
156
157pub(crate) fn decompress_frame(
158 input: &[u8],
159 output: &mut Vec<u8>,
160 max_output: usize,
161 dict: Option<&zrip_core::dict::Dictionary>,
162 ws: &mut BlockDecodeWorkspace,
163) -> Result<usize, DecompressError> {
164 let header = parse_frame_header(input)?;
165
166 if header.window_size > MAX_WINDOW_SIZE {
167 return Err(DecompressError::WindowTooLarge {
168 requested: header.window_size,
169 max: MAX_WINDOW_SIZE,
170 });
171 }
172
173 if let Some(frame_dict_id) = header.dict_id {
174 match dict {
175 Some(d) if d.id() == frame_dict_id => {}
176 Some(d) => {
177 return Err(DecompressError::DictMismatch {
178 expected: frame_dict_id,
179 got: d.id(),
180 });
181 }
182 None => return Err(DecompressError::DictRequired),
183 }
184 }
185
186 if let Some(fcs) = header.frame_content_size {
187 if max_output < usize::MAX && fcs as usize > max_output {
188 return Err(DecompressError::OutputTooSmall);
189 }
190 let hint = (fcs as usize).min(MAX_WINDOW_SIZE as usize);
191 output.reserve(hint + 32);
192 }
193
194 let mut offset = header.header_size;
195 let output_start = output.len();
196
197 let dict_history: &[u8] = if let Some(d) = dict { d.content() } else { &[] };
198
199 let mut seq_tables = if let Some(d) = dict {
200 let mut st = SequenceDecodeTables::new_default();
201 if let Some((t, l)) = d.of_table() {
202 st.of_table = zrip_core::fse::promote_of_table(t);
203 st.of_accuracy = l;
204 st.of_set = true;
205 }
206 if let Some((t, l)) = d.ml_table() {
207 st.ml_table = zrip_core::fse::promote_ml_table(t);
208 st.ml_accuracy = l;
209 st.ml_set = true;
210 }
211 if let Some((t, l)) = d.ll_table() {
212 st.ll_table = zrip_core::fse::promote_ll_table(t);
213 st.ll_accuracy = l;
214 st.ll_set = true;
215 }
216 st
217 } else {
218 SequenceDecodeTables::new_default()
219 };
220 let mut rep_offsets: [u32; 3] = if let Some(d) = dict {
221 *d.rep_offsets()
222 } else {
223 [1, 4, 8]
224 };
225 ws.huf_valid = false;
226 if let Some(d) = dict {
227 if let Some((t, l)) = d.huf_table() {
228 ws.huf_table.clear();
229 ws.huf_table.extend_from_slice(t);
230 ws.huf_table_log = l;
231 ws.huf_valid = true;
232 }
233 }
234
235 let mut hasher = if header.content_checksum {
236 Some(Xxh64State::new(0))
237 } else {
238 None
239 };
240
241 loop {
242 if offset + 3 > input.len() {
243 return Err(DecompressError::InputExhausted);
244 }
245 let block_header = parse_block_header(&input[offset..])?;
246 offset += 3;
247
248 let block_size = block_header.block_size as usize;
249
250 if block_size > zrip_core::frame::MAX_BLOCK_SIZE {
251 match block_header.block_type {
252 BlockType::Raw | BlockType::Rle => {
253 return Err(DecompressError::BlockTooLarge);
254 }
255 BlockType::Compressed => {}
256 }
257 }
258
259 match block_header.block_type {
260 BlockType::Raw => {
261 if offset + block_size > input.len() {
262 return Err(DecompressError::InputExhausted);
263 }
264 if output.len() - output_start + block_size > max_output {
265 return Err(DecompressError::OutputTooSmall);
266 }
267 output.extend_from_slice(&input[offset..offset + block_size]);
268 offset += block_size;
269 }
270 BlockType::Rle => {
271 if offset >= input.len() {
272 return Err(DecompressError::InputExhausted);
273 }
274 if output.len() - output_start + block_size > max_output {
275 return Err(DecompressError::OutputTooSmall);
276 }
277 let byte = input[offset];
278 output.resize(output.len() + block_size, byte);
279 offset += 1;
280 }
281 BlockType::Compressed => {
282 if offset + block_size > input.len() {
283 return Err(DecompressError::InputExhausted);
284 }
285 let block_data = &input[offset..offset + block_size];
286 decode_compressed_block(
287 block_data,
288 output,
289 output_start,
290 max_output,
291 &mut seq_tables,
292 &mut rep_offsets,
293 ws,
294 dict_history,
295 )?;
296 offset += block_size;
297 }
298 }
299
300 if block_header.last_block {
301 break;
302 }
303 }
304
305 if let Some(ref mut hasher) = hasher {
306 hasher.update(&output[output_start..]);
307 let hash = hasher.finish();
308 let expected_checksum = (hash & 0xFFFF_FFFF) as u32;
309
310 if offset + 4 > input.len() {
311 return Err(DecompressError::InputExhausted);
312 }
313 let stored_checksum = u32::from_le_bytes([
314 input[offset],
315 input[offset + 1],
316 input[offset + 2],
317 input[offset + 3],
318 ]);
319 offset += 4;
320
321 if expected_checksum != stored_checksum {
322 return Err(DecompressError::ChecksumMismatch {
323 expected: stored_checksum,
324 got: expected_checksum,
325 });
326 }
327 }
328
329 if let Some(fcs) = header.frame_content_size {
330 if (output.len() - output_start) as u64 != fcs {
331 return Err(DecompressError::FrameSizeMismatch);
332 }
333 }
334
335 Ok(offset)
336}
337
338#[allow(clippy::too_many_arguments)]
339fn decode_compressed_block(
340 data: &[u8],
341 output: &mut Vec<u8>,
342 output_start: usize,
343 max_output: usize,
344 seq_tables: &mut SequenceDecodeTables,
345 rep_offsets: &mut [u32; 3],
346 ws: &mut BlockDecodeWorkspace,
347 dict_history: &[u8],
348) -> Result<(), DecompressError> {
349 let lit_consumed = decode_literals_ws(data, ws)?;
350
351 let remaining = &data[lit_consumed..];
352
353 if remaining.is_empty() {
354 if output.len() - output_start + ws.literal_buf.len() > max_output {
355 return Err(DecompressError::OutputTooSmall);
356 }
357 output.extend_from_slice(&ws.literal_buf);
358 return Ok(());
359 }
360
361 let (num_sequences, seq_count_size) = parse_sequence_count(remaining)?;
362
363 if num_sequences == 0 {
364 if output.len() - output_start + ws.literal_buf.len() > max_output {
365 return Err(DecompressError::OutputTooSmall);
366 }
367 output.extend_from_slice(&ws.literal_buf);
368 return Ok(());
369 }
370
371 let table_data = &remaining[seq_count_size..];
372 let tables_consumed = parse_sequence_tables_ws(table_data, seq_tables, ws)?;
373
374 let seq_data = &table_data[tables_consumed..];
375
376 let before = output.len();
377
378 #[cfg(all(target_arch = "x86_64", not(feature = "paranoid")))]
379 {
380 if zrip_core::simd::cpu_tier() >= CpuTier::Avx2 {
381 decode_execute_block_avx2(
382 seq_data,
383 num_sequences,
384 seq_tables,
385 rep_offsets,
386 &ws.literal_buf,
387 output,
388 dict_history,
389 )?;
390 if output.len() - before > zrip_core::frame::MAX_BLOCK_SIZE {
391 return Err(DecompressError::BlockTooLarge);
392 }
393 return Ok(());
394 }
395 }
396 #[cfg(all(target_arch = "aarch64", not(feature = "paranoid")))]
397 {
398 if zrip_core::simd::cpu_tier() >= CpuTier::Neon {
399 decode_execute_block_neon(
400 seq_data,
401 num_sequences,
402 seq_tables,
403 rep_offsets,
404 &ws.literal_buf,
405 output,
406 dict_history,
407 )?;
408 if output.len() - before > zrip_core::frame::MAX_BLOCK_SIZE {
409 return Err(DecompressError::BlockTooLarge);
410 }
411 return Ok(());
412 }
413 }
414
415 decode_execute_sequences(
416 seq_data,
417 num_sequences,
418 seq_tables,
419 rep_offsets,
420 &ws.literal_buf,
421 output,
422 dict_history,
423 )?;
424 if output.len() - before > zrip_core::frame::MAX_BLOCK_SIZE {
425 return Err(DecompressError::BlockTooLarge);
426 }
427
428 Ok(())
429}
430
431#[cfg(all(target_arch = "x86_64", not(feature = "paranoid")))]
432fn decode_execute_block_avx2(
433 seq_data: &[u8],
434 num_sequences: u32,
435 tables: &mut SequenceDecodeTables,
436 rep_offsets: &mut [u32; 3],
437 literals: &[u8],
438 output: &mut Vec<u8>,
439 history: &[u8],
440) -> Result<(), DecompressError> {
441 crate::simd_decode::x86_64::decode::decode_execute_avx2_safe(
442 seq_data,
443 num_sequences,
444 tables,
445 rep_offsets,
446 literals,
447 output,
448 history,
449 )
450}
451
452#[cfg(all(target_arch = "aarch64", not(feature = "paranoid")))]
453fn decode_execute_block_neon(
454 seq_data: &[u8],
455 num_sequences: u32,
456 tables: &mut SequenceDecodeTables,
457 rep_offsets: &mut [u32; 3],
458 literals: &[u8],
459 output: &mut Vec<u8>,
460 history: &[u8],
461) -> Result<(), DecompressError> {
462 crate::simd_decode::aarch64::decode::decode_execute_neon_safe(
463 seq_data,
464 num_sequences,
465 tables,
466 rep_offsets,
467 literals,
468 output,
469 history,
470 )
471}