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 fast_vec;
13pub(crate) mod literals;
14pub(crate) mod ring_buffer;
15pub(crate) mod seq_table;
16pub(crate) mod sequences;
17#[cfg(feature = "std")]
18pub mod streaming;
19
20#[cfg(feature = "alloc")]
21use alloc::boxed::Box;
22#[cfg(feature = "alloc")]
23use alloc::vec::Vec;
24
25use crate::exec::decode_execute_sequences;
26use crate::literals::decode_literals_ws;
27use crate::sequences::{SequenceDecodeTables, parse_sequence_count, parse_sequence_tables_ws};
28use zrip_core::block::{BlockType, parse_block_header};
29use zrip_core::error::DecompressError;
30use zrip_core::frame::MAX_WINDOW_SIZE;
31use zrip_core::frame::header::parse_frame_header;
32use zrip_core::huffman::HuffmanDecodeEntry;
33use zrip_core::xxhash::Xxh64State;
34
35pub(crate) struct BlockDecodeWorkspace {
36 pub literal_buf: Vec<u8>,
37 pub huf_table: Vec<HuffmanDecodeEntry>,
38 pub huf_table_log: u8,
39 pub huf_valid: bool,
40 pub huf_all_weights: Vec<u8>,
41 pub huf_rank_count: Vec<u32>,
42 pub huf_rank_start: Vec<u32>,
43 pub huf_weights: Vec<u8>,
44 pub huf_last_weights: Vec<u8>,
45 pub huf_last_weights_valid: bool,
46 pub fse_dist: Vec<i16>,
47 pub fse_symbol_next: Vec<u16>,
48 pub fse_build_buf: Vec<zrip_core::fse::FseDecodeEntry>,
49 pub cached_dict_tables: Option<SequenceDecodeTables>,
50 pub cached_dict_rep: [u32; 3],
51 pub cached_dict_huf: Option<(Vec<HuffmanDecodeEntry>, u8)>,
52}
53
54impl BlockDecodeWorkspace {
55 pub(crate) fn new() -> Self {
56 Self {
57 literal_buf: Vec::new(),
58 huf_table: Vec::new(),
59 huf_table_log: 0,
60 huf_valid: false,
61 huf_all_weights: Vec::new(),
62 huf_rank_count: Vec::new(),
63 huf_rank_start: Vec::new(),
64 huf_weights: Vec::new(),
65 huf_last_weights: Vec::new(),
66 huf_last_weights_valid: false,
67 fse_dist: Vec::new(),
68 fse_symbol_next: Vec::new(),
69 fse_build_buf: Vec::new(),
70 cached_dict_tables: None,
71 cached_dict_rep: [1, 4, 8],
72 cached_dict_huf: None,
73 }
74 }
75
76 pub(crate) fn reset_huffman_state(&mut self) {
77 self.huf_valid = false;
78 self.huf_last_weights_valid = false;
79 }
80
81 #[cfg(feature = "std")]
82 pub(crate) fn cache_dict(&mut self, dict: &zrip_core::dict::Dictionary) {
83 let mut st = SequenceDecodeTables::new_default();
84 if let Some((t, l)) = dict.of_table() {
85 st.of_table = crate::seq_table::SeqTable::promote_of(t);
86 st.of_accuracy = l;
87 st.of_set = true;
88 }
89 if let Some((t, l)) = dict.ml_table() {
90 st.ml_table = crate::seq_table::SeqTable::promote_ml(t);
91 st.ml_accuracy = l;
92 st.ml_set = true;
93 }
94 if let Some((t, l)) = dict.ll_table() {
95 st.ll_table = crate::seq_table::SeqTable::promote_ll(t);
96 st.ll_accuracy = l;
97 st.ll_set = true;
98 }
99 self.cached_dict_tables = Some(st);
100 self.cached_dict_rep = *dict.rep_offsets();
101 if let Some((t, l)) = dict.huf_table() {
102 self.cached_dict_huf = Some((t.to_vec(), l));
103 }
104 }
105}
106
107pub(crate) fn skip_skippable_frame(data: &[u8]) -> Option<usize> {
108 if data.len() < 8 {
109 return None;
110 }
111 let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
112 if (magic & 0xFFFF_FFF0) != 0x184D_2A50 {
113 return None;
114 }
115 let frame_size = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as usize;
116 let total = 8 + frame_size;
117 if total > data.len() {
118 return None;
119 }
120 Some(total)
121}
122
123pub fn decompress(input: &[u8]) -> Result<Vec<u8>, DecompressError> {
124 decompress_with_dict(input, None)
125}
126
127pub fn decompress_with_limit(
133 input: &[u8],
134 max_output_size: usize,
135) -> Result<Vec<u8>, DecompressError> {
136 let mut output = Vec::new();
137 let mut ws = Box::new(BlockDecodeWorkspace::new());
138 let mut offset = 0;
139 while offset < input.len() {
140 let remaining = &input[offset..];
141 if let Some(skip_len) = skip_skippable_frame(remaining) {
142 offset += skip_len;
143 continue;
144 }
145 let consumed = decompress_frame(remaining, &mut output, max_output_size, None, &mut ws)?;
146 offset += consumed;
147 }
148 Ok(output)
149}
150
151pub fn decompress_into(input: &[u8], output: &mut Vec<u8>) -> Result<usize, DecompressError> {
152 let max_output = zrip_core::DEFAULT_DECOMPRESS_LIMIT;
153 let mut ws = Box::new(BlockDecodeWorkspace::new());
154 let start = output.len();
155 let mut offset = 0;
156 while offset < input.len() {
157 let remaining = &input[offset..];
158 if let Some(skip_len) = skip_skippable_frame(remaining) {
159 offset += skip_len;
160 continue;
161 }
162 let consumed = decompress_frame(remaining, output, max_output, None, &mut ws)?;
163 offset += consumed;
164 }
165 Ok(output.len() - start)
166}
167
168pub fn decompress_with_dict(
169 input: &[u8],
170 dict: Option<&zrip_core::dict::Dictionary>,
171) -> Result<Vec<u8>, DecompressError> {
172 let max_output = zrip_core::DEFAULT_DECOMPRESS_LIMIT;
173 let mut output = Vec::new();
174 let mut ws = Box::new(BlockDecodeWorkspace::new());
175 let mut offset = 0;
176
177 while offset < input.len() {
178 let remaining = &input[offset..];
179 if let Some(skip_len) = skip_skippable_frame(remaining) {
180 offset += skip_len;
181 continue;
182 }
183 let consumed = decompress_frame(remaining, &mut output, max_output, dict, &mut ws)?;
184 offset += consumed;
185 }
186
187 Ok(output)
188}
189
190pub(crate) fn decompress_frame(
191 input: &[u8],
192 output: &mut Vec<u8>,
193 max_output: usize,
194 dict: Option<&zrip_core::dict::Dictionary>,
195 ws: &mut BlockDecodeWorkspace,
196) -> Result<usize, DecompressError> {
197 let header = parse_frame_header(input)?;
198
199 if header.window_size > MAX_WINDOW_SIZE && !header.single_segment {
200 return Err(DecompressError::WindowTooLarge {
201 requested: header.window_size,
202 max: MAX_WINDOW_SIZE,
203 });
204 }
205
206 if let Some(frame_dict_id) = header.dict_id {
207 match dict {
208 Some(d) if d.id() == frame_dict_id => {}
209 Some(d) => {
210 return Err(DecompressError::DictMismatch {
211 expected: frame_dict_id,
212 got: d.id(),
213 });
214 }
215 None => return Err(DecompressError::DictRequired),
216 }
217 }
218
219 if let Some(fcs) = header.frame_content_size {
220 if max_output < usize::MAX && fcs as usize > max_output {
221 return Err(DecompressError::OutputTooSmall);
222 }
223 let hint = (fcs as usize).min(MAX_WINDOW_SIZE as usize);
224 output.reserve(hint + 32);
225 }
226
227 let mut offset = header.header_size;
228 let output_start = output.len();
229
230 let dict_history: &[u8] = if let Some(d) = dict { d.content() } else { &[] };
231
232 let (mut seq_tables, mut rep_offsets) = if let Some(ref cached) = ws.cached_dict_tables {
233 (cached.clone(), ws.cached_dict_rep)
234 } else if let Some(d) = dict {
235 let mut st = SequenceDecodeTables::new_default();
236 if let Some((t, l)) = d.of_table() {
237 st.of_table = crate::seq_table::SeqTable::promote_of(t);
238 st.of_accuracy = l;
239 st.of_set = true;
240 }
241 if let Some((t, l)) = d.ml_table() {
242 st.ml_table = crate::seq_table::SeqTable::promote_ml(t);
243 st.ml_accuracy = l;
244 st.ml_set = true;
245 }
246 if let Some((t, l)) = d.ll_table() {
247 st.ll_table = crate::seq_table::SeqTable::promote_ll(t);
248 st.ll_accuracy = l;
249 st.ll_set = true;
250 }
251 (st, *d.rep_offsets())
252 } else {
253 (SequenceDecodeTables::new_default(), [1u32, 4, 8])
254 };
255 ws.reset_huffman_state();
256 if let Some((ref t, l)) = ws.cached_dict_huf {
257 ws.huf_table.clear();
258 ws.huf_table.extend_from_slice(t);
259 ws.huf_table_log = l;
260 ws.huf_valid = true;
261 } else if let Some(d) = dict
262 && let Some((t, l)) = d.huf_table()
263 {
264 ws.huf_table.clear();
265 ws.huf_table.extend_from_slice(t);
266 ws.huf_table_log = l;
267 ws.huf_valid = true;
268 }
269
270 let mut hasher = if header.content_checksum {
271 Some(Xxh64State::new(0))
272 } else {
273 None
274 };
275
276 loop {
277 if offset + 3 > input.len() {
278 return Err(DecompressError::InputExhausted);
279 }
280 let block_header = parse_block_header(&input[offset..])?;
281 offset += 3;
282
283 let block_size = block_header.block_size as usize;
284
285 if block_size > zrip_core::frame::MAX_BLOCK_SIZE {
286 match block_header.block_type {
287 BlockType::Raw | BlockType::Rle => {
288 return Err(DecompressError::BlockTooLarge);
289 }
290 BlockType::Compressed => {}
291 }
292 }
293
294 let block_output_start = output.len();
295 match block_header.block_type {
296 BlockType::Raw => {
297 if offset + block_size > input.len() {
298 return Err(DecompressError::InputExhausted);
299 }
300 if output.len() - output_start + block_size > max_output {
301 return Err(DecompressError::OutputTooSmall);
302 }
303 output.extend_from_slice(&input[offset..offset + block_size]);
304 offset += block_size;
305 }
306 BlockType::Rle => {
307 if offset >= input.len() {
308 return Err(DecompressError::InputExhausted);
309 }
310 if output.len() - output_start + block_size > max_output {
311 return Err(DecompressError::OutputTooSmall);
312 }
313 let byte = input[offset];
314 output.resize(output.len() + block_size, byte);
315 offset += 1;
316 }
317 BlockType::Compressed => {
318 if offset + block_size > input.len() {
319 return Err(DecompressError::InputExhausted);
320 }
321 let block_data = &input[offset..offset + block_size];
322 decode_compressed_block(
323 block_data,
324 output,
325 output_start,
326 max_output,
327 &mut seq_tables,
328 &mut rep_offsets,
329 ws,
330 dict_history,
331 )?;
332 offset += block_size;
333 }
334 }
335 if let Some(ref mut hasher) = hasher {
336 hasher.update(&output[block_output_start..]);
337 }
338
339 if block_header.last_block {
340 break;
341 }
342 }
343
344 if let Some(ref mut hasher) = hasher {
345 let hash = hasher.finish();
346 let expected_checksum = (hash & 0xFFFF_FFFF) as u32;
347
348 if offset + 4 > input.len() {
349 return Err(DecompressError::InputExhausted);
350 }
351 let stored_checksum = u32::from_le_bytes([
352 input[offset],
353 input[offset + 1],
354 input[offset + 2],
355 input[offset + 3],
356 ]);
357 offset += 4;
358
359 if expected_checksum != stored_checksum {
360 return Err(DecompressError::ChecksumMismatch {
361 expected: stored_checksum,
362 got: expected_checksum,
363 });
364 }
365 }
366
367 if let Some(fcs) = header.frame_content_size
368 && (output.len() - output_start) as u64 != fcs
369 {
370 return Err(DecompressError::FrameSizeMismatch);
371 }
372
373 Ok(offset)
374}
375
376#[allow(clippy::too_many_arguments)]
377fn decode_compressed_block(
378 data: &[u8],
379 output: &mut Vec<u8>,
380 output_start: usize,
381 max_output: usize,
382 seq_tables: &mut SequenceDecodeTables,
383 rep_offsets: &mut [u32; 3],
384 ws: &mut BlockDecodeWorkspace,
385 dict_history: &[u8],
386) -> Result<(), DecompressError> {
387 let lit_consumed = decode_literals_ws(data, ws)?;
388
389 let remaining = &data[lit_consumed..];
390
391 if remaining.is_empty() {
392 if output.len() - output_start + ws.literal_buf.len() > max_output {
393 return Err(DecompressError::OutputTooSmall);
394 }
395 output.extend_from_slice(&ws.literal_buf);
396 return Ok(());
397 }
398
399 let (num_sequences, seq_count_size) = parse_sequence_count(remaining)?;
400
401 if num_sequences == 0 {
402 if output.len() - output_start + ws.literal_buf.len() > max_output {
403 return Err(DecompressError::OutputTooSmall);
404 }
405 output.extend_from_slice(&ws.literal_buf);
406 return Ok(());
407 }
408
409 let table_data = &remaining[seq_count_size..];
410 let tables_consumed = parse_sequence_tables_ws(table_data, seq_tables, ws)?;
411
412 let seq_data = &table_data[tables_consumed..];
413
414 let before = output.len();
415
416 let result = decode_sequences_dispatch(
417 seq_data,
418 num_sequences,
419 seq_tables,
420 rep_offsets,
421 &ws.literal_buf,
422 output,
423 dict_history,
424 );
425 result?;
426 if output.len() - before > zrip_core::frame::MAX_BLOCK_SIZE {
427 return Err(DecompressError::BlockTooLarge);
428 }
429
430 Ok(())
431}
432
433#[inline(always)]
434pub(crate) fn decode_sequences_dispatch(
435 seq_data: &[u8],
436 num_sequences: u32,
437 seq_tables: &mut SequenceDecodeTables,
438 rep_offsets: &mut [u32; 3],
439 literals: &[u8],
440 output: &mut Vec<u8>,
441 history: &[u8],
442) -> Result<(), DecompressError> {
443 #[cfg(all(feature = "std", feature = "simd"))]
444 {
445 use std::sync::OnceLock;
446 static LEVEL: OnceLock<fearless_simd::Level> = OnceLock::new();
447 let level = *LEVEL.get_or_init(fearless_simd::Level::new);
448 return fearless_simd::dispatch!(level, _simd => {
449 if history.is_empty() {
450 decode_execute_sequences::<false>(
451 seq_data,
452 num_sequences,
453 seq_tables,
454 rep_offsets,
455 literals,
456 output,
457 history,
458 )
459 } else {
460 decode_execute_sequences::<true>(
461 seq_data,
462 num_sequences,
463 seq_tables,
464 rep_offsets,
465 literals,
466 output,
467 history,
468 )
469 }
470 });
471 }
472
473 #[allow(unreachable_code)]
474 if history.is_empty() {
475 decode_execute_sequences::<false>(
476 seq_data,
477 num_sequences,
478 seq_tables,
479 rep_offsets,
480 literals,
481 output,
482 history,
483 )
484 } else {
485 decode_execute_sequences::<true>(
486 seq_data,
487 num_sequences,
488 seq_tables,
489 rep_offsets,
490 literals,
491 output,
492 history,
493 )
494 }
495}
496
497#[cfg(all(test, miri, not(feature = "paranoid")))]
498mod ub_tests {
499 use super::*;
500 use alloc::vec::Vec;
501 use zrip_core::bitstream::writer::BitWriter;
502 use zrip_core::frame::{MAX_BLOCK_SIZE, ZSTD_MAGIC};
503
504 fn push_block_header(out: &mut Vec<u8>, last: bool, block_type: u32, block_size: usize) {
505 let raw = ((block_size as u32) << 3) | (block_type << 1) | u32::from(last);
506 out.push(raw as u8);
507 out.push((raw >> 8) as u8);
508 out.push((raw >> 16) as u8);
509 }
510
511 fn frame_with_oversized_compressed_block_output() -> Vec<u8> {
512 let mut frame = Vec::new();
513 frame.extend_from_slice(&ZSTD_MAGIC.to_le_bytes());
514 frame.push(0x00);
515 frame.push(0x00);
516
517 push_block_header(&mut frame, false, 0, 1);
518 frame.push(b'A');
519
520 let mut block = Vec::new();
521 let trailing_literals = 65usize;
522 block.push(0x04 | (((trailing_literals & 0x0f) as u8) << 4));
523 block.push((trailing_literals >> 4) as u8);
524 block.extend(core::iter::repeat_n(b'B', trailing_literals));
525
526 block.push(1);
527 block.push(0x54);
528 block.extend_from_slice(&[0, 2, 52]);
529
530 let mut seq_bits = BitWriter::new();
531 let ml_extra = MAX_BLOCK_SIZE as u32 - 65_539;
532 seq_bits.write_bits(ml_extra, 16);
533 seq_bits.write_bits(0, 2);
534 seq_bits.close_reverse_stream();
535 block.extend_from_slice(&seq_bits.into_bytes());
536
537 push_block_header(&mut frame, true, 2, block.len());
538 frame.extend_from_slice(&block);
539 frame
540 }
541
542 #[test]
543 fn compressed_block_trailing_literals_overrun_wildcopy_headroom() {
544 let frame = frame_with_oversized_compressed_block_output();
551 let _ = decompress(&frame);
552 }
553}