1use crate::block::{DecompressError, MINMATCH};
3use crate::fastcpy_unsafe;
4use crate::sink::SliceSink;
5use crate::sink::{PtrSink, Sink};
6#[allow(unused_imports)]
7use alloc::vec::Vec;
8
9#[inline]
11unsafe fn duplicate(
12 output_ptr: &mut *mut u8,
13 output_end: *mut u8,
14 start: *const u8,
15 match_length: usize,
16) {
17 if (output_ptr.offset_from(start) as usize) < match_length + 16 - 1
24 || (output_end.offset_from(*output_ptr) as usize) < match_length + 16 - 1
25 {
26 duplicate_overlapping(output_ptr, start, match_length);
27 } else {
28 debug_assert!(
29 output_ptr.add(match_length / 16 * 16 + ((match_length % 16) != 0) as usize * 16)
30 <= output_end
31 );
32 wild_copy_from_src_16(start, *output_ptr, match_length);
33 *output_ptr = output_ptr.add(match_length);
34 }
35}
36
37#[inline]
38fn wild_copy_from_src_16(mut source: *const u8, mut dst_ptr: *mut u8, num_items: usize) {
39 unsafe {
42 let dst_ptr_end = dst_ptr.add(num_items);
43 loop {
44 core::ptr::copy_nonoverlapping(source, dst_ptr, 16);
45 source = source.add(16);
46 dst_ptr = dst_ptr.add(16);
47 if dst_ptr >= dst_ptr_end {
48 break;
49 }
50 }
51 }
52}
53
54#[inline]
56#[cfg_attr(feature = "nightly", optimize(size))] unsafe fn duplicate_overlapping(
58 output_ptr: &mut *mut u8,
59 mut start: *const u8,
60 match_length: usize,
61) {
62 let dst_ptr_end = output_ptr.add(match_length);
63
64 while output_ptr.add(1) < dst_ptr_end {
65 core::ptr::copy(start, *output_ptr, 1);
70 start = start.add(1);
71 *output_ptr = output_ptr.add(1);
72
73 core::ptr::copy(start, *output_ptr, 1);
74 start = start.add(1);
75 *output_ptr = output_ptr.add(1);
76 }
77
78 if *output_ptr < dst_ptr_end {
79 core::ptr::copy(start, *output_ptr, 1);
80 *output_ptr = output_ptr.add(1);
81 }
82}
83
84#[inline]
85unsafe fn copy_from_dict(
86 output_base: *mut u8,
87 output_ptr: &mut *mut u8,
88 ext_dict: &[u8],
89 offset: usize,
90 match_length: usize,
91) -> usize {
92 debug_assert!(output_ptr.offset_from(output_base) >= 0);
94 debug_assert!(offset > output_ptr.offset_from(output_base) as usize);
95 debug_assert!(ext_dict.len() + output_ptr.offset_from(output_base) as usize >= offset);
97
98 let dict_offset = ext_dict.len() + output_ptr.offset_from(output_base) as usize - offset;
99 let dict_match_length = match_length.min(ext_dict.len() - dict_offset);
101 core::ptr::copy_nonoverlapping(
103 ext_dict.as_ptr().add(dict_offset),
104 *output_ptr,
105 dict_match_length,
106 );
107 *output_ptr = output_ptr.add(dict_match_length);
108 dict_match_length
109}
110
111#[inline]
126pub(super) fn read_integer_ptr(
127 input_ptr: &mut *const u8,
128 _input_ptr_end: *const u8,
129) -> Result<usize, DecompressError> {
130 let mut n: usize = 0;
132 loop {
135 {
139 if *input_ptr >= _input_ptr_end {
140 return Err(DecompressError::ExpectedAnotherByte);
141 }
142 }
143 let extra = unsafe { input_ptr.read() };
144 *input_ptr = unsafe { input_ptr.add(1) };
145 n += extra as usize;
146
147 if extra != 0xFF {
149 break;
150 }
151 }
152
153 Ok(n)
157}
158
159#[inline]
161fn read_match_offset(input_ptr: &mut *const u8) -> Result<u16, DecompressError> {
162 let mut num: u16 = 0;
163 unsafe {
164 core::ptr::copy_nonoverlapping(*input_ptr, &mut num as *mut u16 as *mut u8, 2);
165 *input_ptr = input_ptr.add(2);
166 }
167
168 let offset = u16::from_le(num);
169 if offset == 0 {
170 Err(DecompressError::OffsetZero)
171 } else {
172 Ok(offset)
173 }
174}
175
176const FIT_TOKEN_MASK_LITERAL: u8 = 0b00001111;
177const FIT_TOKEN_MASK_MATCH: u8 = 0b11110000;
178
179#[test]
180fn check_token() {
181 assert!(!does_token_fit(15));
182 assert!(does_token_fit(14));
183 assert!(does_token_fit(114));
184 assert!(!does_token_fit(0b11110000));
185 assert!(does_token_fit(0b10110000));
186}
187
188#[inline]
192fn does_token_fit(token: u8) -> bool {
193 !((token & FIT_TOKEN_MASK_LITERAL) == FIT_TOKEN_MASK_LITERAL
194 || (token & FIT_TOKEN_MASK_MATCH) == FIT_TOKEN_MASK_MATCH)
195}
196
197#[inline]
201pub(crate) fn decompress_internal<const USE_DICT: bool, S: Sink>(
202 input: &[u8],
203 output: &mut S,
204 ext_dict: &[u8],
205) -> Result<usize, DecompressError> {
206 if input.is_empty() {
208 return Err(DecompressError::ExpectedAnotherByte);
209 }
210
211 let ext_dict = if USE_DICT {
212 ext_dict
213 } else {
214 debug_assert!(ext_dict.is_empty());
216 &[]
217 };
218 let output_base = unsafe { output.base_mut_ptr() };
219 let output_end = unsafe { output_base.add(output.capacity()) };
220 let output_start_pos_ptr = unsafe { output.base_mut_ptr().add(output.pos()) as *mut u8 };
221 let mut output_ptr = output_start_pos_ptr;
222
223 let mut input_ptr = input.as_ptr();
224 let input_ptr_end = unsafe { input.as_ptr().add(input.len()) };
225 let safe_distance_from_end = (16 + 2 + 1 ).min(input.len()) ;
226 let input_ptr_safe = unsafe { input_ptr_end.sub(safe_distance_from_end) };
227
228 let safe_output_ptr = unsafe {
229 let mut output_num_safe_bytes = output
230 .capacity()
231 .saturating_sub(16 + 18 );
232 if USE_DICT {
233 output_num_safe_bytes = output_num_safe_bytes.saturating_sub(17);
237 };
238
239 output_base.add(output_num_safe_bytes)
240 };
241
242 loop {
245 let token = unsafe { input_ptr.read() };
250 input_ptr = unsafe { input_ptr.add(1) };
251
252 if does_token_fit(token)
260 && (input_ptr as usize) <= input_ptr_safe as usize
261 && output_ptr < safe_output_ptr
262 {
263 let literal_length = (token >> 4) as usize;
264 let mut match_length = MINMATCH + (token & 0xF) as usize;
265
266 debug_assert!(
268 unsafe { output_ptr.add(literal_length + match_length) } <= output_end,
269 "{literal_length} + {match_length} {} wont fit ",
270 literal_length + match_length
271 );
272
273 unsafe {
277 core::ptr::copy_nonoverlapping(input_ptr, output_ptr, 16);
278 input_ptr = input_ptr.add(literal_length);
279 output_ptr = output_ptr.add(literal_length);
280 }
281
282 debug_assert!(input_ptr_end as usize - input_ptr as usize >= 2);
284 let offset = read_match_offset(&mut input_ptr)? as usize;
285
286 let output_len = unsafe { output_ptr.offset_from(output_base) as usize };
287 if offset > output_len + ext_dict.len() {
288 return Err(DecompressError::OffsetOutOfBounds);
289 }
290
291 if USE_DICT && offset > output_len {
293 let copied = unsafe {
294 copy_from_dict(output_base, &mut output_ptr, ext_dict, offset, match_length)
295 };
296 if copied == match_length {
297 continue;
298 }
299 match_length -= copied;
301 }
302
303 let start_ptr = unsafe { output_ptr.sub(offset) };
307 debug_assert!(start_ptr >= output_base);
308 debug_assert!(start_ptr < output_end);
309 debug_assert!(unsafe { output_end.offset_from(start_ptr) as usize } >= match_length);
310
311 if offset >= match_length {
315 unsafe {
316 core::ptr::copy(start_ptr, output_ptr, 18);
319 output_ptr = output_ptr.add(match_length);
320 }
321 } else {
322 unsafe {
323 duplicate_overlapping(&mut output_ptr, start_ptr, match_length);
324 }
325 }
326
327 continue;
328 }
329
330 let mut literal_length = (token >> 4) as usize;
335 if literal_length != 0 {
336 if literal_length == 15 {
337 literal_length += read_integer_ptr(&mut input_ptr, input_ptr_end)? as usize;
340 }
341
342 {
344 if literal_length > input_ptr_end as usize - input_ptr as usize {
347 return Err(DecompressError::LiteralOutOfBounds);
348 }
349 if literal_length > unsafe { output_end.offset_from(output_ptr) as usize } {
350 return Err(DecompressError::OutputTooSmall {
351 expected: unsafe { output_ptr.offset_from(output_base) as usize }
352 + literal_length,
353 actual: output.capacity(),
354 });
355 }
356 }
357 unsafe {
358 fastcpy_unsafe::slice_copy(input_ptr, output_ptr, literal_length);
359 output_ptr = output_ptr.add(literal_length);
360 input_ptr = input_ptr.add(literal_length);
361 }
362 }
363
364 if input_ptr >= input_ptr_end {
367 break;
368 }
369
370 {
373 if (input_ptr_end as usize) - (input_ptr as usize) < 2 {
374 return Err(DecompressError::ExpectedAnotherByte);
375 }
376 }
377 let offset = read_match_offset(&mut input_ptr)? as usize;
378 let mut match_length = MINMATCH + (token & 0xF) as usize;
387 if match_length == MINMATCH + 15 {
388 match_length += read_integer_ptr(&mut input_ptr, input_ptr_end)? as usize;
391 }
392
393 let output_len = unsafe { output_ptr.offset_from(output_base) as usize };
396
397 {
399 if offset > output_len + ext_dict.len() {
400 return Err(DecompressError::OffsetOutOfBounds);
401 }
402 if match_length > unsafe { output_end.offset_from(output_ptr) as usize } {
403 return Err(DecompressError::OutputTooSmall {
404 expected: output_len + match_length,
405 actual: output.capacity(),
406 });
407 }
408 }
409
410 if USE_DICT && offset > output_len {
411 let copied = unsafe {
412 copy_from_dict(output_base, &mut output_ptr, ext_dict, offset, match_length)
413 };
414 if copied == match_length {
415 {
417 if input_ptr >= input_ptr_end {
418 return Err(DecompressError::ExpectedAnotherByte);
419 }
420 }
421
422 continue;
423 }
424 match_length -= copied;
426 }
427
428 let start_ptr = unsafe { output_ptr.sub(offset) };
432 debug_assert!(start_ptr >= output_base);
433 debug_assert!(start_ptr < output_end);
434 debug_assert!(unsafe { output_end.offset_from(start_ptr) as usize } >= match_length);
435 unsafe {
436 duplicate(&mut output_ptr, output_end, start_ptr, match_length);
437 }
438 {
440 if input_ptr >= input_ptr_end {
441 return Err(DecompressError::ExpectedAnotherByte);
442 }
443 }
444 }
445 unsafe {
446 output.set_pos(output_ptr.offset_from(output_base) as usize);
447 Ok(output_ptr.offset_from(output_start_pos_ptr) as usize)
448 }
449}
450
451#[inline]
454pub fn decompress_into(input: &[u8], output: &mut [u8]) -> Result<usize, DecompressError> {
455 decompress_internal::<false, _>(input, &mut SliceSink::new(output, 0), b"")
456}
457
458#[inline]
462pub fn decompress_into_with_dict(
463 input: &[u8],
464 output: &mut [u8],
465 ext_dict: &[u8],
466) -> Result<usize, DecompressError> {
467 decompress_internal::<true, _>(input, &mut SliceSink::new(output, 0), ext_dict)
468}
469
470#[inline]
478pub fn decompress_with_dict(
479 input: &[u8],
480 min_uncompressed_size: usize,
481 ext_dict: &[u8],
482) -> Result<Vec<u8>, DecompressError> {
483 let mut vec = Vec::with_capacity(min_uncompressed_size);
485 let decomp_len =
486 decompress_internal::<true, _>(input, &mut PtrSink::from_vec(&mut vec, 0), ext_dict)?;
487 unsafe {
488 vec.set_len(decomp_len);
489 }
490 Ok(vec)
491}
492
493#[inline]
496pub fn decompress_size_prepended(input: &[u8]) -> Result<Vec<u8>, DecompressError> {
497 let (uncompressed_size, input) = super::uncompressed_size(input)?;
498 decompress(input, uncompressed_size)
499}
500
501#[inline]
508pub fn decompress(input: &[u8], min_uncompressed_size: usize) -> Result<Vec<u8>, DecompressError> {
509 let mut vec = Vec::with_capacity(min_uncompressed_size);
511 let decomp_len =
512 decompress_internal::<true, _>(input, &mut PtrSink::from_vec(&mut vec, 0), b"")?;
513 unsafe {
514 vec.set_len(decomp_len);
515 }
516 Ok(vec)
517}
518
519#[inline]
522pub fn decompress_size_prepended_with_dict(
523 input: &[u8],
524 ext_dict: &[u8],
525) -> Result<Vec<u8>, DecompressError> {
526 let (uncompressed_size, input) = super::uncompressed_size(input)?;
527 decompress_with_dict(input, uncompressed_size, ext_dict)
528}
529
530#[cfg(test)]
531mod test {
532 use super::*;
533
534 #[test]
535 fn all_literal() {
536 assert_eq!(decompress(&[0x30, b'a', b'4', b'9'], 3).unwrap(), b"a49");
537 }
538
539 #[test]
540 fn incomplete_input() {
541 assert!(matches!(
542 decompress(&[], 255),
543 Err(DecompressError::ExpectedAnotherByte)
544 ));
545 assert!(matches!(
546 decompress(&[0xF0], 255),
548 Err(DecompressError::ExpectedAnotherByte)
549 ));
550 assert!(matches!(
551 decompress(&[0x0F, 0], 255),
553 Err(DecompressError::ExpectedAnotherByte)
554 ));
555 assert!(matches!(
556 decompress(&[0x0F, 1, 0], 255),
558 Err(DecompressError::ExpectedAnotherByte)
559 ));
560 }
561
562 #[test]
564 fn offset_oob() {
565 assert!(matches!(
567 decompress(&[0x40, b'a', 1, 0], 4),
568 Err(DecompressError::LiteralOutOfBounds)
569 ));
570 assert!(matches!(
572 decompress(&[0x20, b'a', b'a', 1, 0], 1),
573 Err(DecompressError::OutputTooSmall {
574 expected: 2,
575 actual: 1
576 })
577 ));
578 assert!(matches!(
580 decompress(&[0x10, b'a', 1, 0], 4),
581 Err(DecompressError::OutputTooSmall {
582 expected: 5,
583 actual: 4
584 })
585 ));
586
587 assert!(matches!(
589 decompress(
590 &[0x0E, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
591 256
592 ),
593 Err(DecompressError::OffsetOutOfBounds)
594 ));
595 assert!(matches!(
597 decompress_with_dict(
598 &[0x0E, 255, 0, 0x70, 0, 0, 0, 0, 0, 0, 0],
599 256,
600 &[0_u8; 250]
601 ),
602 Err(DecompressError::OffsetOutOfBounds)
603 ));
604 assert!(matches!(
606 decompress(&[0x0F, 1, 0, 1, 0x70, 0, 0, 0, 0, 0, 0, 0], 256),
607 Err(DecompressError::OffsetOutOfBounds)
608 ));
609 assert!(matches!(
611 decompress(&[0x40, 0, 0, 0, 0, 255, 0, 0x70, 0, 0, 0, 0, 0, 0, 0], 256),
612 Err(DecompressError::OffsetOutOfBounds)
613 ));
614 }
615
616 #[test]
617 fn offset_0() {
618 assert!(matches!(
619 decompress(&[0x0E, 0, 0, 0x70, 0, 0, 0, 0, 0, 0, 0], 256),
620 Err(DecompressError::OffsetZero)
621 ));
622 }
623}