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