1use crate::block::{DecompressError, MINMATCH};
3use crate::fastcpy_unsafe;
4#[cfg(feature = "alloc")]
5use crate::sink::PtrSink;
6use crate::sink::Sink;
7use crate::sink::SliceSink;
8#[cfg(feature = "alloc")]
9#[allow(unused_imports)]
10use alloc::vec::Vec;
11
12#[inline]
14unsafe fn duplicate(
15 output_ptr: &mut *mut u8,
16 output_end: *mut u8,
17 start: *const u8,
18 match_length: usize,
19) {
20 if (output_ptr.offset_from(start) as usize) < match_length + 16 - 1
27 || (output_end.offset_from(*output_ptr) as usize) < match_length + 16 - 1
28 {
29 duplicate_overlapping(output_ptr, start, match_length);
30 } else {
31 debug_assert!(
32 output_ptr.add(match_length / 16 * 16 + ((match_length % 16) != 0) as usize * 16)
33 <= output_end
34 );
35 wild_copy_from_src_16(start, *output_ptr, match_length);
36 *output_ptr = output_ptr.add(match_length);
37 }
38}
39
40#[inline]
41fn wild_copy_from_src_16(mut source: *const u8, mut dst_ptr: *mut u8, num_items: usize) {
42 unsafe {
45 let dst_ptr_end = dst_ptr.add(num_items);
46 loop {
47 core::ptr::copy_nonoverlapping(source, dst_ptr, 16);
48 source = source.add(16);
49 dst_ptr = dst_ptr.add(16);
50 if dst_ptr >= dst_ptr_end {
51 break;
52 }
53 }
54 }
55}
56
57#[inline]
59#[cfg_attr(feature = "nightly", optimize(size))] unsafe fn duplicate_overlapping(
61 output_ptr: &mut *mut u8,
62 mut start: *const u8,
63 match_length: usize,
64) {
65 let dst_ptr_end = output_ptr.add(match_length);
66
67 while output_ptr.add(1) < dst_ptr_end {
68 core::ptr::copy(start, *output_ptr, 1);
73 start = start.add(1);
74 *output_ptr = output_ptr.add(1);
75
76 core::ptr::copy(start, *output_ptr, 1);
77 start = start.add(1);
78 *output_ptr = output_ptr.add(1);
79 }
80
81 if *output_ptr < dst_ptr_end {
82 core::ptr::copy(start, *output_ptr, 1);
83 *output_ptr = output_ptr.add(1);
84 }
85}
86
87#[inline]
88unsafe fn copy_from_dict(
89 output_base: *mut u8,
90 output_ptr: &mut *mut u8,
91 ext_dict: &[u8],
92 offset: usize,
93 match_length: usize,
94) -> usize {
95 debug_assert!(output_ptr.offset_from(output_base) >= 0);
97 debug_assert!(offset > output_ptr.offset_from(output_base) as usize);
98 debug_assert!(ext_dict.len() + output_ptr.offset_from(output_base) as usize >= offset);
100
101 let dict_offset = ext_dict.len() + output_ptr.offset_from(output_base) as usize - offset;
102 let dict_match_length = match_length.min(ext_dict.len() - dict_offset);
104 core::ptr::copy_nonoverlapping(
106 ext_dict.as_ptr().add(dict_offset),
107 *output_ptr,
108 dict_match_length,
109 );
110 *output_ptr = output_ptr.add(dict_match_length);
111 dict_match_length
112}
113
114#[inline]
129pub(super) fn read_integer_ptr(
130 input_ptr: &mut *const u8,
131 _input_ptr_end: *const u8,
132) -> Result<usize, DecompressError> {
133 let mut n: usize = 0;
135 loop {
138 {
142 if *input_ptr >= _input_ptr_end {
143 return Err(DecompressError::ExpectedAnotherByte);
144 }
145 }
146 let extra = unsafe { input_ptr.read() };
147 *input_ptr = unsafe { input_ptr.add(1) };
148 n += extra as usize;
149
150 if extra != 0xFF {
152 break;
153 }
154 }
155
156 Ok(n)
160}
161
162#[inline]
164fn read_match_offset(input_ptr: &mut *const u8) -> Result<u16, DecompressError> {
165 let mut num: u16 = 0;
166 unsafe {
167 core::ptr::copy_nonoverlapping(*input_ptr, &mut num as *mut u16 as *mut u8, 2);
168 *input_ptr = input_ptr.add(2);
169 }
170
171 let offset = u16::from_le(num);
172 if offset == 0 {
173 Err(DecompressError::OffsetZero)
174 } else {
175 Ok(offset)
176 }
177}
178
179const FIT_TOKEN_MASK_LITERAL: u8 = 0b00001111;
180const FIT_TOKEN_MASK_MATCH: u8 = 0b11110000;
181
182#[test]
183fn check_token() {
184 assert!(!does_token_fit(15));
185 assert!(does_token_fit(14));
186 assert!(does_token_fit(114));
187 assert!(!does_token_fit(0b11110000));
188 assert!(does_token_fit(0b10110000));
189}
190
191#[inline]
195fn does_token_fit(token: u8) -> bool {
196 !((token & FIT_TOKEN_MASK_LITERAL) == FIT_TOKEN_MASK_LITERAL
197 || (token & FIT_TOKEN_MASK_MATCH) == FIT_TOKEN_MASK_MATCH)
198}
199
200#[inline]
204pub(crate) fn decompress_internal<const USE_DICT: bool, S: Sink>(
205 input: &[u8],
206 output: &mut S,
207 ext_dict: &[u8],
208) -> Result<usize, DecompressError> {
209 if input.is_empty() {
211 return Err(DecompressError::ExpectedAnotherByte);
212 }
213
214 let ext_dict = if USE_DICT {
215 ext_dict
216 } else {
217 debug_assert!(ext_dict.is_empty());
219 &[]
220 };
221 let output_base = unsafe { output.base_mut_ptr() };
222 let output_end = unsafe { output_base.add(output.capacity()) };
223 let output_start_pos_ptr = unsafe { output.base_mut_ptr().add(output.pos()) as *mut u8 };
224 let mut output_ptr = output_start_pos_ptr;
225
226 let mut input_ptr = input.as_ptr();
227 let input_ptr_end = unsafe { input.as_ptr().add(input.len()) };
228 let safe_distance_from_end = (16 + 2 + 1 ).min(input.len()) ;
229 let input_ptr_safe = unsafe { input_ptr_end.sub(safe_distance_from_end) };
230
231 let safe_output_ptr = unsafe {
232 let mut output_num_safe_bytes = output
233 .capacity()
234 .saturating_sub(16 + 18 );
235 if USE_DICT {
236 output_num_safe_bytes = output_num_safe_bytes.saturating_sub(17);
240 };
241
242 output_base.add(output_num_safe_bytes)
243 };
244
245 loop {
248 let token = unsafe { input_ptr.read() };
253 input_ptr = unsafe { input_ptr.add(1) };
254
255 if does_token_fit(token)
263 && (input_ptr as usize) <= input_ptr_safe as usize
264 && output_ptr < safe_output_ptr
265 {
266 let literal_length = (token >> 4) as usize;
267 let mut match_length = MINMATCH + (token & 0xF) as usize;
268
269 debug_assert!(
271 unsafe { output_ptr.add(literal_length + match_length) } <= output_end,
272 "{literal_length} + {match_length} {} wont fit ",
273 literal_length + match_length
274 );
275
276 unsafe {
280 core::ptr::copy_nonoverlapping(input_ptr, output_ptr, 16);
281 input_ptr = input_ptr.add(literal_length);
282 output_ptr = output_ptr.add(literal_length);
283 }
284
285 debug_assert!(input_ptr_end as usize - input_ptr as usize >= 2);
287 let offset = read_match_offset(&mut input_ptr)? as usize;
288
289 let output_len = unsafe { output_ptr.offset_from(output_base) as usize };
290 if offset > output_len + ext_dict.len() {
291 return Err(DecompressError::OffsetOutOfBounds);
292 }
293
294 if USE_DICT && offset > output_len {
296 let copied = unsafe {
297 copy_from_dict(output_base, &mut output_ptr, ext_dict, offset, match_length)
298 };
299 if copied == match_length {
300 continue;
301 }
302 match_length -= copied;
304 }
305
306 let start_ptr = unsafe { output_ptr.sub(offset) };
310 debug_assert!(start_ptr >= output_base);
311 debug_assert!(start_ptr < output_end);
312 debug_assert!(unsafe { output_end.offset_from(start_ptr) as usize } >= match_length);
313
314 if offset >= match_length {
318 unsafe {
319 core::ptr::copy(start_ptr, output_ptr, 18);
322 output_ptr = output_ptr.add(match_length);
323 }
324 } else {
325 unsafe {
326 duplicate_overlapping(&mut output_ptr, start_ptr, match_length);
327 }
328 }
329
330 continue;
331 }
332
333 let mut literal_length = (token >> 4) as usize;
338 if literal_length != 0 {
339 if literal_length == 15 {
340 literal_length += read_integer_ptr(&mut input_ptr, input_ptr_end)? as usize;
343 }
344
345 {
347 if literal_length > input_ptr_end as usize - input_ptr as usize {
350 return Err(DecompressError::LiteralOutOfBounds);
351 }
352 if literal_length > unsafe { output_end.offset_from(output_ptr) as usize } {
353 return Err(DecompressError::OutputTooSmall {
354 expected: unsafe { output_ptr.offset_from(output_base) as usize }
355 + literal_length,
356 actual: output.capacity(),
357 });
358 }
359 }
360 unsafe {
361 fastcpy_unsafe::slice_copy(input_ptr, output_ptr, literal_length);
362 output_ptr = output_ptr.add(literal_length);
363 input_ptr = input_ptr.add(literal_length);
364 }
365 }
366
367 if input_ptr >= input_ptr_end {
370 break;
371 }
372
373 {
376 if (input_ptr_end as usize) - (input_ptr as usize) < 2 {
377 return Err(DecompressError::ExpectedAnotherByte);
378 }
379 }
380 let offset = read_match_offset(&mut input_ptr)? as usize;
381 let mut match_length = MINMATCH + (token & 0xF) as usize;
390 if match_length == MINMATCH + 15 {
391 match_length += read_integer_ptr(&mut input_ptr, input_ptr_end)? as usize;
394 }
395
396 let output_len = unsafe { output_ptr.offset_from(output_base) as usize };
399
400 {
402 if offset > output_len + ext_dict.len() {
403 return Err(DecompressError::OffsetOutOfBounds);
404 }
405 if match_length > unsafe { output_end.offset_from(output_ptr) as usize } {
406 return Err(DecompressError::OutputTooSmall {
407 expected: output_len + match_length,
408 actual: output.capacity(),
409 });
410 }
411 }
412
413 if USE_DICT && offset > output_len {
414 let copied = unsafe {
415 copy_from_dict(output_base, &mut output_ptr, ext_dict, offset, match_length)
416 };
417 if copied == match_length {
418 {
420 if input_ptr >= input_ptr_end {
421 return Err(DecompressError::ExpectedAnotherByte);
422 }
423 }
424
425 continue;
426 }
427 match_length -= copied;
429 }
430
431 let start_ptr = unsafe { output_ptr.sub(offset) };
435 debug_assert!(start_ptr >= output_base);
436 debug_assert!(start_ptr < output_end);
437 debug_assert!(unsafe { output_end.offset_from(start_ptr) as usize } >= match_length);
438 unsafe {
439 duplicate(&mut output_ptr, output_end, start_ptr, match_length);
440 }
441 {
443 if input_ptr >= input_ptr_end {
444 return Err(DecompressError::ExpectedAnotherByte);
445 }
446 }
447 }
448 unsafe {
449 output.set_pos(output_ptr.offset_from(output_base) as usize);
450 Ok(output_ptr.offset_from(output_start_pos_ptr) as usize)
451 }
452}
453
454#[inline]
457pub fn decompress_into(input: &[u8], output: &mut [u8]) -> Result<usize, DecompressError> {
458 decompress_internal::<false, _>(input, &mut SliceSink::new(output, 0), b"")
459}
460
461#[inline]
465pub fn decompress_into_with_dict(
466 input: &[u8],
467 output: &mut [u8],
468 ext_dict: &[u8],
469) -> Result<usize, DecompressError> {
470 decompress_internal::<true, _>(input, &mut SliceSink::new(output, 0), ext_dict)
471}
472
473#[cfg(feature = "alloc")]
481#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
482#[inline]
483pub fn decompress_with_dict(
484 input: &[u8],
485 min_uncompressed_size: usize,
486 ext_dict: &[u8],
487) -> Result<Vec<u8>, DecompressError> {
488 let mut vec = Vec::with_capacity(min_uncompressed_size);
490 let decomp_len =
491 decompress_internal::<true, _>(input, &mut PtrSink::from_vec(&mut vec, 0), ext_dict)?;
492 unsafe {
493 vec.set_len(decomp_len);
494 }
495 Ok(vec)
496}
497
498#[cfg(feature = "alloc")]
501#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
502#[inline]
503pub fn decompress_size_prepended(input: &[u8]) -> Result<Vec<u8>, DecompressError> {
504 let (uncompressed_size, input) = super::uncompressed_size(input)?;
505 decompress(input, uncompressed_size)
506}
507
508#[cfg(feature = "alloc")]
515#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
516#[inline]
517pub fn decompress(input: &[u8], min_uncompressed_size: usize) -> Result<Vec<u8>, DecompressError> {
518 let mut vec = Vec::with_capacity(min_uncompressed_size);
520 let decomp_len =
521 decompress_internal::<true, _>(input, &mut PtrSink::from_vec(&mut vec, 0), b"")?;
522 unsafe {
523 vec.set_len(decomp_len);
524 }
525 Ok(vec)
526}
527
528#[cfg(feature = "alloc")]
531#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
532#[inline]
533pub fn decompress_size_prepended_with_dict(
534 input: &[u8],
535 ext_dict: &[u8],
536) -> Result<Vec<u8>, DecompressError> {
537 let (uncompressed_size, input) = super::uncompressed_size(input)?;
538 decompress_with_dict(input, uncompressed_size, ext_dict)
539}
540
541#[cfg(test)]
542mod test {
543 use super::*;
544
545 #[test]
546 fn all_literal() {
547 assert_eq!(decompress(&[0x30, b'a', b'4', b'9'], 3).unwrap(), b"a49");
548 }
549
550 #[test]
551 fn incomplete_input() {
552 assert!(matches!(
553 decompress(&[], 255),
554 Err(DecompressError::ExpectedAnotherByte)
555 ));
556 assert!(matches!(
557 decompress(&[0xF0], 255),
559 Err(DecompressError::ExpectedAnotherByte)
560 ));
561 assert!(matches!(
562 decompress(&[0x0F, 0], 255),
564 Err(DecompressError::ExpectedAnotherByte)
565 ));
566 assert!(matches!(
567 decompress(&[0x0F, 1, 0], 255),
569 Err(DecompressError::ExpectedAnotherByte)
570 ));
571 }
572
573 #[test]
575 fn offset_oob() {
576 assert!(matches!(
578 decompress(&[0x40, b'a', 1, 0], 4),
579 Err(DecompressError::LiteralOutOfBounds)
580 ));
581 assert!(matches!(
583 decompress(&[0x20, b'a', b'a', 1, 0], 1),
584 Err(DecompressError::OutputTooSmall {
585 expected: 2,
586 actual: 1
587 })
588 ));
589 assert!(matches!(
591 decompress(&[0x10, b'a', 1, 0], 4),
592 Err(DecompressError::OutputTooSmall {
593 expected: 5,
594 actual: 4
595 })
596 ));
597
598 assert!(matches!(
600 decompress(
601 &[0x0E, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
602 256
603 ),
604 Err(DecompressError::OffsetOutOfBounds)
605 ));
606 assert!(matches!(
608 decompress_with_dict(
609 &[0x0E, 255, 0, 0x70, 0, 0, 0, 0, 0, 0, 0],
610 256,
611 &[0_u8; 250]
612 ),
613 Err(DecompressError::OffsetOutOfBounds)
614 ));
615 assert!(matches!(
617 decompress(&[0x0F, 1, 0, 1, 0x70, 0, 0, 0, 0, 0, 0, 0], 256),
618 Err(DecompressError::OffsetOutOfBounds)
619 ));
620 assert!(matches!(
622 decompress(&[0x40, 0, 0, 0, 0, 255, 0, 0x70, 0, 0, 0, 0, 0, 0, 0], 256),
623 Err(DecompressError::OffsetOutOfBounds)
624 ));
625 }
626
627 #[test]
628 fn offset_0() {
629 assert!(matches!(
630 decompress(&[0x0E, 0, 0, 0x70, 0, 0, 0, 0, 0, 0, 0], 256),
631 Err(DecompressError::OffsetZero)
632 ));
633 }
634}