1#![allow(non_camel_case_types)]
2#![allow(non_snake_case)]
3#![allow(clippy::missing_safety_doc)]
4#![allow(clippy::too_many_arguments)]
5#![allow(clippy::needless_range_loop)]
6
7pub use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t};
8use std::cmp;
9use std::ptr;
10use std::slice;
11
12#[derive(Clone, Copy, Debug)]
13#[repr(C)]
14pub struct LZ4FCompressionContext(pub *mut c_void);
15unsafe impl Send for LZ4FCompressionContext {}
16
17#[derive(Clone, Copy, Debug)]
18#[repr(C)]
19pub struct LZ4FDecompressionContext(pub *mut c_void);
20unsafe impl Send for LZ4FDecompressionContext {}
21
22pub type LZ4FErrorCode = size_t;
23
24#[derive(Clone, Debug)]
25#[repr(u32)]
26pub enum BlockSize {
27 Default = 0,
28 Max64KB = 4,
29 Max256KB = 5,
30 Max1MB = 6,
31 Max4MB = 7,
32}
33
34impl BlockSize {
35 pub fn get_size(&self) -> usize {
36 match self {
37 BlockSize::Default | BlockSize::Max64KB => 64 * 1024,
38 BlockSize::Max256KB => 256 * 1024,
39 BlockSize::Max1MB => 1024 * 1024,
40 BlockSize::Max4MB => 4 * 1024 * 1024,
41 }
42 }
43}
44
45#[derive(Clone, Debug)]
46#[repr(u32)]
47pub enum BlockMode {
48 Linked = 0,
49 Independent,
50}
51
52#[derive(Clone, Debug)]
53#[repr(u32)]
54pub enum ContentChecksum {
55 NoChecksum = 0,
56 ChecksumEnabled,
57}
58
59#[derive(Clone, Debug)]
60#[repr(u32)]
61pub enum FrameType {
62 Frame = 0,
63 SkippableFrame,
64}
65
66#[derive(Clone, Debug)]
67#[repr(u32)]
68pub enum BlockChecksum {
69 NoBlockChecksum = 0,
70 BlockChecksumEnabled,
71}
72
73#[derive(Clone, Debug)]
74#[repr(C)]
75pub struct LZ4FFrameInfo {
76 pub block_size_id: BlockSize,
77 pub block_mode: BlockMode,
78 pub content_checksum_flag: ContentChecksum,
79 pub frame_type: FrameType,
80 pub content_size: c_ulonglong,
81 pub dict_id: c_uint,
82 pub block_checksum_flag: BlockChecksum,
83}
84
85#[derive(Clone, Debug)]
86#[repr(C)]
87pub struct LZ4FPreferences {
88 pub frame_info: LZ4FFrameInfo,
89 pub compression_level: c_uint,
90 pub auto_flush: c_uint,
91 pub favor_dec_speed: c_uint,
92 pub reserved: [c_uint; 3],
93}
94
95#[derive(Debug)]
96#[repr(C)]
97pub struct LZ4FCompressOptions {
98 pub stable_src: c_uint,
99 pub reserved: [c_uint; 3],
100}
101
102#[derive(Debug)]
103#[repr(C)]
104pub struct LZ4FDecompressOptions {
105 pub stable_dst: c_uint,
110 pub skipChecksums: c_uint,
113 pub reserved: [c_uint; 2],
114}
115
116#[derive(Debug)]
117#[repr(C)]
118pub struct LZ4StreamEncode {
119 _private: [u8; 0],
120}
121
122#[derive(Debug)]
123#[repr(C)]
124pub struct LZ4StreamDecode {
125 _private: [u8; 0],
126}
127
128#[derive(Debug)]
129#[repr(C)]
130pub struct LZ4StreamHC {
131 _private: [u8; 0],
132}
133
134#[derive(Debug)]
135#[repr(C)]
136pub struct LZ4FCDict {
137 _private: [u8; 0],
138}
139
140pub const LZ4F_VERSION: c_uint = 100;
141
142const LZ4_VERSION_NUMBER: c_int = 11000;
143const MINMATCH: usize = 4;
144const LZ4_HASH_BITS: usize = 12;
145const LZ4_HASH_BITS_U16: usize = LZ4_HASH_BITS + 1;
146const LZ4_64K_LIMIT: usize = 64 * 1024 + MFLIMIT - 1;
147const HASH_UNIT: usize = std::mem::size_of::<usize>();
148const LZ4HC_HASH_BITS: usize = 15;
149const LZ4HC_HASH_SIZE: usize = 1 << LZ4HC_HASH_BITS;
150const LZ4MID_HASH_BITS: usize = LZ4HC_HASH_BITS - 1;
151const LZ4MID_HASH_SIZE: usize = 1 << LZ4MID_HASH_BITS;
152const LZ4MID_HASHSIZE: usize = 8;
153const LZ4_OPT_NUM: usize = 1 << 12;
154const LZ4_DISTANCE_MAX: usize = 64 * 1024 - 1;
155const LAST_LITERALS: usize = 5;
156const MFLIMIT: usize = 12;
157const OPTIMAL_ML: usize = 18;
158const LZ4_MAX_INPUT_SIZE: c_int = 0x7E00_0000;
159const LZ4HC_CLEVEL_MIN: c_int = 3;
160const LZ4HC_CLEVEL_DEFAULT: c_int = 9;
161const LZ4HC_CLEVEL_MAX: c_int = 12;
162const LZ4F_MAGIC: [u8; 4] = [0x04, 0x22, 0x4D, 0x18];
163const LZ4F_SKIPPABLE_MAGIC_MIN: u32 = 0x184D_2A50;
164const LZ4F_SKIPPABLE_MAGIC_MAX: u32 = 0x184D_2A5F;
165const LZ4F_ERROR_MAX_CODE: usize = 24;
166const LZ4F_ERROR_GENERIC_CODE: usize = 1;
167const LZ4F_ERROR_MAX_BLOCK_SIZE_INVALID_CODE: usize = 2;
168const LZ4F_ERROR_BLOCK_MODE_INVALID_CODE: usize = 3;
169const LZ4F_ERROR_PARAMETER_INVALID_CODE: usize = 4;
170const LZ4F_ERROR_COMPRESSION_LEVEL_INVALID_CODE: usize = 5;
171const LZ4F_ERROR_HEADER_VERSION_WRONG_CODE: usize = 6;
172const LZ4F_ERROR_BLOCK_CHECKSUM_INVALID_CODE: usize = 7;
173const LZ4F_ERROR_RESERVED_FLAG_SET_CODE: usize = 8;
174const LZ4F_ERROR_ALLOCATION_FAILED_CODE: usize = 9;
175const LZ4F_ERROR_SRC_SIZE_TOO_LARGE_CODE: usize = 10;
176const LZ4F_ERROR_FRAME_SIZE_WRONG_CODE: usize = 14;
177const LZ4F_ERROR_FRAME_TYPE_UNKNOWN_CODE: usize = 13;
178const LZ4F_ERROR_SRC_PTR_WRONG_CODE: usize = 15;
179const LZ4F_ERROR_DECOMPRESSION_FAILED_CODE: usize = 16;
180const LZ4F_ERROR_HEADER_CHECKSUM_INVALID_CODE: usize = 17;
181const LZ4F_ERROR_DST_MAX_SIZE_TOO_SMALL_CODE: usize = 11;
182const LZ4F_ERROR_FRAME_HEADER_INCOMPLETE_CODE: usize = 12;
183const LZ4F_ERROR_CONTENT_CHECKSUM_INVALID_CODE: usize = 18;
184const LZ4F_ERROR_FRAME_DECODING_ALREADY_STARTED_CODE: usize = 19;
185const LZ4F_ERROR_COMPRESSION_STATE_UNINITIALIZED_CODE: usize = 20;
186const LZ4F_ERROR_PARAMETER_NULL_CODE: usize = 21;
187const LZ4F_ERROR_IO_WRITE_CODE: usize = 22;
188const LZ4F_ERROR_IO_READ_CODE: usize = 23;
189const ERROR_GENERIC: usize = lz4f_error(LZ4F_ERROR_GENERIC_CODE);
190const ERROR_MAX_BLOCK_SIZE_INVALID: usize = lz4f_error(LZ4F_ERROR_MAX_BLOCK_SIZE_INVALID_CODE);
191const ERROR_BLOCK_MODE_INVALID: usize = lz4f_error(LZ4F_ERROR_BLOCK_MODE_INVALID_CODE);
192const ERROR_PARAMETER_INVALID: usize = lz4f_error(LZ4F_ERROR_PARAMETER_INVALID_CODE);
193const ERROR_COMPRESSION_LEVEL_INVALID: usize =
194 lz4f_error(LZ4F_ERROR_COMPRESSION_LEVEL_INVALID_CODE);
195const ERROR_HEADER_VERSION_WRONG: usize = lz4f_error(LZ4F_ERROR_HEADER_VERSION_WRONG_CODE);
196const ERROR_BLOCK_CHECKSUM_INVALID: usize = lz4f_error(LZ4F_ERROR_BLOCK_CHECKSUM_INVALID_CODE);
197const ERROR_RESERVED_FLAG_SET: usize = lz4f_error(LZ4F_ERROR_RESERVED_FLAG_SET_CODE);
198const ERROR_ALLOCATION_FAILED: usize = lz4f_error(LZ4F_ERROR_ALLOCATION_FAILED_CODE);
199const ERROR_SRC_SIZE_TOO_LARGE: usize = lz4f_error(LZ4F_ERROR_SRC_SIZE_TOO_LARGE_CODE);
200const ERROR_FRAME_SIZE_WRONG: usize = lz4f_error(LZ4F_ERROR_FRAME_SIZE_WRONG_CODE);
201const ERROR_FRAME_TYPE_UNKNOWN: usize = lz4f_error(LZ4F_ERROR_FRAME_TYPE_UNKNOWN_CODE);
202const ERROR_SRC_PTR_WRONG: usize = lz4f_error(LZ4F_ERROR_SRC_PTR_WRONG_CODE);
203const ERROR_DECOMPRESSION_FAILED: usize = lz4f_error(LZ4F_ERROR_DECOMPRESSION_FAILED_CODE);
204const ERROR_HEADER_CHECKSUM_INVALID: usize = lz4f_error(LZ4F_ERROR_HEADER_CHECKSUM_INVALID_CODE);
205const ERROR_DST_TOO_SMALL: usize = lz4f_error(LZ4F_ERROR_DST_MAX_SIZE_TOO_SMALL_CODE);
206const ERROR_BAD_HEADER: usize = lz4f_error(LZ4F_ERROR_FRAME_HEADER_INCOMPLETE_CODE);
207const ERROR_CHECKSUM_INVALID: usize = lz4f_error(LZ4F_ERROR_CONTENT_CHECKSUM_INVALID_CODE);
208const ERROR_FRAME_DECODING_ALREADY_STARTED: usize =
209 lz4f_error(LZ4F_ERROR_FRAME_DECODING_ALREADY_STARTED_CODE);
210const ERROR_COMPRESSION_STATE_UNINITIALIZED: usize =
211 lz4f_error(LZ4F_ERROR_COMPRESSION_STATE_UNINITIALIZED_CODE);
212const ERROR_PARAMETER_NULL: usize = lz4f_error(LZ4F_ERROR_PARAMETER_NULL_CODE);
213const ERROR_IO_WRITE: usize = lz4f_error(LZ4F_ERROR_IO_WRITE_CODE);
214const ERROR_IO_READ: usize = lz4f_error(LZ4F_ERROR_IO_READ_CODE);
215
216static ERROR_GENERIC_NAME: &[u8] = b"ERROR_GENERIC\0";
217static ERROR_MAX_BLOCK_SIZE_NAME: &[u8] = b"ERROR_maxBlockSize_invalid\0";
218static ERROR_BLOCK_MODE_NAME: &[u8] = b"ERROR_blockMode_invalid\0";
219static ERROR_PARAMETER_INVALID_NAME: &[u8] = b"ERROR_parameter_invalid\0";
220static ERROR_COMPRESSION_LEVEL_NAME: &[u8] = b"ERROR_compressionLevel_invalid\0";
221static ERROR_HEADER_VERSION_NAME: &[u8] = b"ERROR_headerVersion_wrong\0";
222static ERROR_BLOCK_CHECKSUM_NAME: &[u8] = b"ERROR_blockChecksum_invalid\0";
223static ERROR_RESERVED_FLAG_NAME: &[u8] = b"ERROR_reservedFlag_set\0";
224static ERROR_ALLOCATION_FAILED_NAME: &[u8] = b"ERROR_allocation_failed\0";
225static ERROR_SRC_SIZE_TOO_LARGE_NAME: &[u8] = b"ERROR_srcSize_tooLarge\0";
226static ERROR_FRAME_SIZE_NAME: &[u8] = b"ERROR_frameSize_wrong\0";
227static ERROR_FRAME_TYPE_NAME: &[u8] = b"ERROR_frameType_unknown\0";
228static ERROR_SRC_PTR_NAME: &[u8] = b"ERROR_srcPtr_wrong\0";
229static ERROR_DECOMPRESSION_FAILED_NAME: &[u8] = b"ERROR_decompressionFailed\0";
230static ERROR_HEADER_CHECKSUM_NAME: &[u8] = b"ERROR_headerChecksum_invalid\0";
231static ERROR_DST_NAME: &[u8] = b"ERROR_dstMaxSize_tooSmall\0";
232static ERROR_BAD_HEADER_NAME: &[u8] = b"ERROR_frameHeader_incomplete\0";
233static ERROR_CHECKSUM_NAME: &[u8] = b"ERROR_contentChecksum_invalid\0";
234static ERROR_FRAME_DECODING_ALREADY_STARTED_NAME: &[u8] = b"ERROR_frameDecoding_alreadyStarted\0";
235static ERROR_COMPRESSION_STATE_UNINITIALIZED_NAME: &[u8] =
236 b"ERROR_compressionState_uninitialized\0";
237static ERROR_PARAMETER_NULL_NAME: &[u8] = b"ERROR_parameter_null\0";
238static ERROR_IO_WRITE_NAME: &[u8] = b"ERROR_io_write\0";
239static ERROR_IO_READ_NAME: &[u8] = b"ERROR_io_read\0";
240static ERROR_UNSPECIFIED_NAME: &[u8] = b"Unspecified error code\0";
241static LZ4_VERSION_STRING_BYTES: &[u8] = b"1.10.0\0";
242
243const fn lz4f_error(code: usize) -> usize {
244 usize::MAX - (code - 1)
245}
246
247#[derive(Debug)]
248struct CompressionCtx {
249 prefs: FramePrefs,
250 content_hasher: XxHash32,
251 dictionary: Vec<u8>,
252 pending: Vec<u8>,
253 total_input: u64,
254 external_dictionary: bool,
255 started: bool,
256}
257
258#[derive(Clone, Copy, Debug)]
259struct FramePrefs {
260 block_size_id: u8,
261 block_independent: bool,
262 block_checksum: bool,
263 content_checksum: bool,
264 content_size: u64,
265 dict_id: c_uint,
266 compression_level: c_int,
267 auto_flush: bool,
268 favor_dec_speed: bool,
269}
270
271impl Default for FramePrefs {
272 fn default() -> Self {
273 Self {
277 block_size_id: 4,
278 block_independent: false,
279 block_checksum: false,
280 content_checksum: false,
281 content_size: 0,
282 dict_id: 0,
283 compression_level: 0,
284 auto_flush: false,
285 favor_dec_speed: false,
286 }
287 }
288}
289
290#[derive(Debug)]
291struct DecompressionCtx {
292 input: Vec<u8>,
293 pending: Vec<u8>,
294 pending_pos: usize,
295 pos: usize,
296 parsed_header: bool,
297 done: bool,
298 block_checksum: bool,
299 content_checksum: bool,
300 content_size: u64,
301 content_read: u64,
302 dict_id: c_uint,
303 block_independent: bool,
304 block_max: usize,
305 dictionary: Vec<u8>,
306 external_dictionary: bool,
307 content_hasher: XxHash32,
308 raw_block_remaining: usize,
309 raw_block_checksum: XxHash32,
310 skip_checksums: bool,
311}
312
313#[derive(Debug)]
314struct HcStreamCtx {
315 compression_level: c_int,
316 dictionary: Vec<u8>,
317 attached_dictionary: bool,
318 favor_dec_speed: bool,
319}
320
321#[derive(Debug, Default)]
322struct EncodeStreamCtx {
323 dictionary: Vec<u8>,
324 attached_dictionary: bool,
325}
326
327#[derive(Debug, Default)]
328struct DecodeStreamCtx {
329 dictionary: Vec<u8>,
330}
331
332#[derive(Debug, Default)]
333struct CDictCtx {
334 dictionary: Vec<u8>,
335}
336
337impl Default for HcStreamCtx {
338 fn default() -> Self {
339 Self {
340 compression_level: LZ4HC_CLEVEL_DEFAULT,
341 dictionary: Vec::new(),
342 attached_dictionary: false,
343 favor_dec_speed: false,
344 }
345 }
346}
347
348impl Default for DecompressionCtx {
349 fn default() -> Self {
350 Self {
351 input: Vec::new(),
352 pending: Vec::new(),
353 pending_pos: 0,
354 pos: 0,
355 parsed_header: false,
356 done: false,
357 block_checksum: false,
358 content_checksum: false,
359 content_size: 0,
360 content_read: 0,
361 dict_id: 0,
362 block_independent: true,
363 block_max: 64 * 1024,
364 dictionary: Vec::new(),
365 external_dictionary: false,
366 content_hasher: XxHash32::new(0),
367 raw_block_remaining: 0,
368 raw_block_checksum: XxHash32::new(0),
369 skip_checksums: false,
370 }
371 }
372}
373
374#[no_mangle]
376pub unsafe extern "C" fn LZ4_versionNumber() -> c_int {
377 LZ4_VERSION_NUMBER
378}
379
380#[no_mangle]
382pub extern "C" fn LZ4_versionString() -> *const c_char {
383 LZ4_VERSION_STRING_BYTES.as_ptr() as *const c_char
384}
385
386#[no_mangle]
396pub unsafe extern "C" fn LZ4_compressBound(size: c_int) -> c_int {
397 if !(0..=LZ4_MAX_INPUT_SIZE).contains(&size) {
398 0
399 } else {
400 size + (size / 255) + 16
401 }
402}
403
404#[no_mangle]
419pub unsafe extern "C" fn LZ4_compress_default(
420 source: *const c_char,
421 dest: *mut c_char,
422 sourceSize: c_int,
423 maxDestSize: c_int,
424) -> c_int {
425 LZ4_compress_fast(source, dest, sourceSize, maxDestSize, 1)
426}
427
428#[no_mangle]
430pub unsafe extern "C" fn LZ4_compress(
431 source: *const c_char,
432 dest: *mut c_char,
433 sourceSize: c_int,
434) -> c_int {
435 LZ4_compress_default(source, dest, sourceSize, LZ4_compressBound(sourceSize))
436}
437
438#[no_mangle]
440pub unsafe extern "C" fn LZ4_compress_limitedOutput(
441 source: *const c_char,
442 dest: *mut c_char,
443 sourceSize: c_int,
444 maxOutputSize: c_int,
445) -> c_int {
446 LZ4_compress_default(source, dest, sourceSize, maxOutputSize)
447}
448
449#[no_mangle]
451pub unsafe extern "C" fn LZ4_compress_withState(
452 state: *mut c_void,
453 source: *const c_char,
454 dest: *mut c_char,
455 inputSize: c_int,
456) -> c_int {
457 LZ4_compress_fast_extState(
458 state,
459 source,
460 dest,
461 inputSize,
462 LZ4_compressBound(inputSize),
463 1,
464 )
465}
466
467#[no_mangle]
469pub unsafe extern "C" fn LZ4_compress_limitedOutput_withState(
470 state: *mut c_void,
471 source: *const c_char,
472 dest: *mut c_char,
473 inputSize: c_int,
474 maxOutputSize: c_int,
475) -> c_int {
476 LZ4_compress_fast_extState(state, source, dest, inputSize, maxOutputSize, 1)
477}
478
479#[no_mangle]
488pub unsafe extern "C" fn LZ4_compress_fast(
489 source: *const c_char,
490 dest: *mut c_char,
491 sourceSize: c_int,
492 maxDestSize: c_int,
493 acceleration: c_int,
494) -> c_int {
495 if !(0..=LZ4_MAX_INPUT_SIZE).contains(&sourceSize)
496 || maxDestSize <= 0
497 || (sourceSize > 0 && source.is_null())
498 || dest.is_null()
499 {
500 return 0;
501 }
502 let src = if sourceSize == 0 {
503 &[]
504 } else {
505 slice::from_raw_parts(source as *const u8, sourceSize as usize)
506 };
507 let dst = slice::from_raw_parts_mut(dest as *mut u8, maxDestSize as usize);
508 compress_block(src, dst, normalize_acceleration(acceleration)).map_or(0, |n| n as c_int)
509}
510
511#[no_mangle]
514pub extern "C" fn LZ4_sizeofState() -> c_int {
515 ((1usize << (LZ4_HASH_BITS + 2)) + 32) as c_int
516}
517
518#[no_mangle]
524pub unsafe extern "C" fn LZ4_compress_fast_extState(
525 state: *mut c_void,
526 source: *const c_char,
527 dest: *mut c_char,
528 sourceSize: c_int,
529 maxDestSize: c_int,
530 acceleration: c_int,
531) -> c_int {
532 if !(0..=LZ4_MAX_INPUT_SIZE).contains(&sourceSize)
533 || maxDestSize <= 0
534 || (sourceSize > 0 && source.is_null())
535 || dest.is_null()
536 || state.is_null()
537 {
538 return 0;
539 }
540 let src = if sourceSize == 0 {
541 &[]
542 } else {
543 slice::from_raw_parts(source as *const u8, sourceSize as usize)
544 };
545 let dst = slice::from_raw_parts_mut(dest as *mut u8, maxDestSize as usize);
546 compress_block_ext_state(state, src, dst, normalize_acceleration(acceleration))
547 .map_or(0, |n| n as c_int)
548}
549
550#[no_mangle]
560pub unsafe extern "C" fn LZ4_compress_fast_extState_fastReset(
561 state: *mut c_void,
562 source: *const c_char,
563 dest: *mut c_char,
564 sourceSize: c_int,
565 maxDestSize: c_int,
566 acceleration: c_int,
567) -> c_int {
568 LZ4_compress_fast_extState(state, source, dest, sourceSize, maxDestSize, acceleration)
569}
570
571#[no_mangle]
589pub unsafe extern "C" fn LZ4_compress_destSize(
590 src: *const c_char,
591 dst: *mut c_char,
592 srcSizePtr: *mut c_int,
593 targetDstSize: c_int,
594) -> c_int {
595 if src.is_null()
596 || dst.is_null()
597 || srcSizePtr.is_null()
598 || *srcSizePtr < 0
599 || targetDstSize <= 0
600 {
601 return 0;
602 }
603 let src_slice = slice::from_raw_parts(src as *const u8, *srcSizePtr as usize);
604 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, targetDstSize as usize);
605 let Some((consumed, written)) = compress_dest_size(src_slice, dst_slice, 1) else {
606 return 0;
607 };
608 *srcSizePtr = consumed as c_int;
609 written as c_int
610}
611
612#[no_mangle]
616pub unsafe extern "C" fn LZ4_compress_destSize_extState(
617 state: *mut c_void,
618 src: *const c_char,
619 dst: *mut c_char,
620 srcSizePtr: *mut c_int,
621 targetDstSize: c_int,
622 acceleration: c_int,
623) -> c_int {
624 if state.is_null()
625 || src.is_null()
626 || dst.is_null()
627 || srcSizePtr.is_null()
628 || *srcSizePtr < 0
629 || targetDstSize <= 0
630 {
631 return 0;
632 }
633 if targetDstSize >= LZ4_compressBound(*srcSizePtr) {
634 return LZ4_compress_fast_extState(
635 state,
636 src,
637 dst,
638 *srcSizePtr,
639 targetDstSize,
640 acceleration,
641 );
642 }
643
644 let src_slice = slice::from_raw_parts(src as *const u8, *srcSizePtr as usize);
645 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, targetDstSize as usize);
646 let Some((consumed, written)) =
647 compress_dest_size(src_slice, dst_slice, normalize_acceleration(acceleration))
648 else {
649 return 0;
650 };
651 *srcSizePtr = consumed as c_int;
652 written as c_int
653}
654
655#[no_mangle]
664pub unsafe extern "C" fn LZ4_compress_HC(
665 src: *const c_char,
666 dst: *mut c_char,
667 srcSize: c_int,
668 dstCapacity: c_int,
669 compressionLevel: c_int,
670) -> c_int {
671 if srcSize < 0 || dstCapacity <= 0 || src.is_null() || dst.is_null() {
672 return 0;
673 }
674 let src = slice::from_raw_parts(src as *const u8, srcSize as usize);
675 let dst = slice::from_raw_parts_mut(dst as *mut u8, dstCapacity as usize);
676 compress_block_hc(src, dst, compressionLevel, false).map_or(0, |n| n as c_int)
677}
678
679#[no_mangle]
682pub extern "C" fn LZ4_sizeofStateHC() -> c_int {
683 cmp::max(
684 std::mem::size_of::<HcStreamCtx>(),
685 std::mem::align_of::<HcStreamCtx>(),
686 ) as c_int
687}
688
689#[no_mangle]
694pub unsafe extern "C" fn LZ4_compress_HC_extStateHC(
695 stateHC: *mut c_void,
696 src: *const c_char,
697 dst: *mut c_char,
698 srcSize: c_int,
699 maxDstSize: c_int,
700 compressionLevel: c_int,
701) -> c_int {
702 if stateHC.is_null() || srcSize < 0 || maxDstSize <= 0 || src.is_null() || dst.is_null() {
703 return 0;
704 }
705 let ctx = stateHC as *mut HcStreamCtx;
706 ptr::write(ctx, HcStreamCtx::default());
707 (*ctx).compression_level = normalize_hc_level(compressionLevel);
708 let src_slice = slice::from_raw_parts(src as *const u8, srcSize as usize);
709 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, maxDstSize as usize);
710 compress_hc_stream_block(
711 &mut *ctx,
712 src_slice,
713 dst_slice,
714 normalize_hc_level(compressionLevel),
715 false,
716 )
717}
718
719#[no_mangle]
727pub unsafe extern "C" fn LZ4_compress_HC_extStateHC_fastReset(
728 state: *mut c_void,
729 src: *const c_char,
730 dst: *mut c_char,
731 srcSize: c_int,
732 dstCapacity: c_int,
733 compressionLevel: c_int,
734) -> c_int {
735 if state.is_null() || srcSize < 0 || dstCapacity <= 0 || src.is_null() || dst.is_null() {
736 return 0;
737 }
738 let ctx = &mut *(state as *mut HcStreamCtx);
739 ctx.compression_level = normalize_hc_level(compressionLevel);
740 let src_slice = slice::from_raw_parts(src as *const u8, srcSize as usize);
741 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, dstCapacity as usize);
742 compress_hc_stream_block(
743 ctx,
744 src_slice,
745 dst_slice,
746 normalize_hc_level(compressionLevel),
747 ctx.favor_dec_speed,
748 )
749}
750
751#[no_mangle]
758pub unsafe extern "C" fn LZ4_compress_HC_destSize(
759 stateHC: *mut c_void,
760 src: *const c_char,
761 dst: *mut c_char,
762 srcSizePtr: *mut c_int,
763 targetDstSize: c_int,
764 compressionLevel: c_int,
765) -> c_int {
766 if stateHC.is_null()
767 || src.is_null()
768 || dst.is_null()
769 || srcSizePtr.is_null()
770 || *srcSizePtr < 0
771 || targetDstSize <= 0
772 {
773 return 0;
774 }
775
776 let src_len = *srcSizePtr as usize;
777 let src_slice = slice::from_raw_parts(src as *const u8, src_len);
778 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, targetDstSize as usize);
779 let Some((consumed, written)) =
780 compress_hc_dest_size(src_slice, dst_slice, compressionLevel, false)
781 else {
782 return 0;
783 };
784 *srcSizePtr = consumed as c_int;
785 written as c_int
786}
787
788#[no_mangle]
790pub unsafe extern "C" fn LZ4_compressHC(
791 src: *const c_char,
792 dst: *mut c_char,
793 srcSize: c_int,
794) -> c_int {
795 LZ4_compress_HC(src, dst, srcSize, LZ4_compressBound(srcSize), 0)
796}
797
798#[no_mangle]
800pub unsafe extern "C" fn LZ4_compressHC_limitedOutput(
801 src: *const c_char,
802 dst: *mut c_char,
803 srcSize: c_int,
804 maxDstSize: c_int,
805) -> c_int {
806 LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0)
807}
808
809#[no_mangle]
811pub unsafe extern "C" fn LZ4_compressHC2(
812 src: *const c_char,
813 dst: *mut c_char,
814 srcSize: c_int,
815 cLevel: c_int,
816) -> c_int {
817 LZ4_compress_HC(src, dst, srcSize, LZ4_compressBound(srcSize), cLevel)
818}
819
820#[no_mangle]
822pub unsafe extern "C" fn LZ4_compressHC2_limitedOutput(
823 src: *const c_char,
824 dst: *mut c_char,
825 srcSize: c_int,
826 maxDstSize: c_int,
827 cLevel: c_int,
828) -> c_int {
829 LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel)
830}
831
832#[no_mangle]
834pub unsafe extern "C" fn LZ4_compressHC_withStateHC(
835 state: *mut c_void,
836 src: *const c_char,
837 dst: *mut c_char,
838 srcSize: c_int,
839) -> c_int {
840 LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), 0)
841}
842
843#[no_mangle]
845pub unsafe extern "C" fn LZ4_compressHC_limitedOutput_withStateHC(
846 state: *mut c_void,
847 src: *const c_char,
848 dst: *mut c_char,
849 srcSize: c_int,
850 maxDstSize: c_int,
851) -> c_int {
852 LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, 0)
853}
854
855#[no_mangle]
857pub unsafe extern "C" fn LZ4_compressHC2_withStateHC(
858 state: *mut c_void,
859 src: *const c_char,
860 dst: *mut c_char,
861 srcSize: c_int,
862 cLevel: c_int,
863) -> c_int {
864 LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel)
865}
866
867#[no_mangle]
869pub unsafe extern "C" fn LZ4_compressHC2_limitedOutput_withStateHC(
870 state: *mut c_void,
871 src: *const c_char,
872 dst: *mut c_char,
873 srcSize: c_int,
874 maxDstSize: c_int,
875 cLevel: c_int,
876) -> c_int {
877 LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel)
878}
879
880#[no_mangle]
897pub unsafe extern "C" fn LZ4_decompress_safe(
898 source: *const c_char,
899 dest: *mut c_char,
900 compressedSize: c_int,
901 maxDecompressedSize: c_int,
902) -> c_int {
903 if compressedSize < 0 || maxDecompressedSize < 0 || source.is_null() || dest.is_null() {
904 return -1;
905 }
906 let src = slice::from_raw_parts(source as *const u8, compressedSize as usize);
907 let dst = slice::from_raw_parts_mut(dest as *mut u8, maxDecompressedSize as usize);
908 decompress_block(src, dst).map_or(-1, |n| n as c_int)
909}
910
911#[no_mangle]
920pub unsafe extern "C" fn LZ4_decompress_safe_usingDict(
921 source: *const c_char,
922 dest: *mut c_char,
923 compressedSize: c_int,
924 maxDecompressedSize: c_int,
925 dictStart: *const c_char,
926 dictSize: c_int,
927) -> c_int {
928 if compressedSize < 0
929 || maxDecompressedSize < 0
930 || dictSize < 0
931 || source.is_null()
932 || dest.is_null()
933 || (dictSize > 0 && dictStart.is_null())
934 {
935 return -1;
936 }
937
938 if dictSize == 0 {
941 return LZ4_decompress_safe(source, dest, compressedSize, maxDecompressedSize);
942 }
943 let src = slice::from_raw_parts(source as *const u8, compressedSize as usize);
944 let dst = slice::from_raw_parts_mut(dest as *mut u8, maxDecompressedSize as usize);
945 let dict = slice::from_raw_parts(dictStart as *const u8, dictSize as usize);
946
947 let dict_end = (dictStart as *const u8).add(dictSize as usize);
948 if dict_end == dest as *const u8 {
949 let _is_prefix_64k = dictSize as usize >= 64 * 1024 - 1;
960 return decompress_block_with_dict(src, dst, dict).map_or(-1, |n| n as c_int);
961 }
962
963 decompress_block_with_dict(src, dst, dict).map_or(-1, |n| n as c_int)
965}
966
967#[no_mangle]
984pub unsafe extern "C" fn LZ4_decompress_safe_partial(
985 source: *const c_char,
986 dest: *mut c_char,
987 compressedSize: c_int,
988 targetOutputSize: c_int,
989 dstCapacity: c_int,
990) -> c_int {
991 if compressedSize < 0
992 || targetOutputSize < 0
993 || dstCapacity < 0
994 || source.is_null()
995 || dest.is_null()
996 {
997 return -1;
998 }
999 let src = slice::from_raw_parts(source as *const u8, compressedSize as usize);
1000 let dst = slice::from_raw_parts_mut(dest as *mut u8, dstCapacity as usize);
1001 let target = cmp::min(targetOutputSize as usize, dst.len());
1002 decompress_block_partial(src, dst, target).map_or(-1, |n| n as c_int)
1003}
1004
1005#[no_mangle]
1012pub unsafe extern "C" fn LZ4_decompress_safe_partial_usingDict(
1013 source: *const c_char,
1014 dest: *mut c_char,
1015 compressedSize: c_int,
1016 targetOutputSize: c_int,
1017 dstCapacity: c_int,
1018 dictStart: *const c_char,
1019 dictSize: c_int,
1020) -> c_int {
1021 if compressedSize < 0
1022 || targetOutputSize < 0
1023 || dstCapacity < 0
1024 || dictSize < 0
1025 || source.is_null()
1026 || dest.is_null()
1027 || (dictSize > 0 && dictStart.is_null())
1028 {
1029 return -1;
1030 }
1031
1032 if dictSize == 0 {
1034 return LZ4_decompress_safe_partial(
1035 source,
1036 dest,
1037 compressedSize,
1038 targetOutputSize,
1039 dstCapacity,
1040 );
1041 }
1042 let src = slice::from_raw_parts(source as *const u8, compressedSize as usize);
1043 let dst = slice::from_raw_parts_mut(dest as *mut u8, dstCapacity as usize);
1044 let dict = slice::from_raw_parts(dictStart as *const u8, dictSize as usize);
1045
1046 let dict_end = (dictStart as *const u8).add(dictSize as usize);
1047 if dict_end == dest as *const u8 {
1048 let _is_prefix_64k = dictSize as usize >= 64 * 1024 - 1;
1052 return decompress_block_partial_with_dict(
1053 src,
1054 dst,
1055 cmp::min(targetOutputSize as usize, dst.len()),
1056 dict,
1057 )
1058 .map_or(-1, |n| n as c_int);
1059 }
1060
1061 decompress_block_partial_with_dict(
1063 src,
1064 dst,
1065 cmp::min(targetOutputSize as usize, dst.len()),
1066 dict,
1067 )
1068 .map_or(-1, |n| n as c_int)
1069}
1070
1071#[no_mangle]
1080pub extern "C" fn LZ4_decoderRingBufferSize(maxBlockSize: c_int) -> c_int {
1081 if !(0..=LZ4_MAX_INPUT_SIZE).contains(&maxBlockSize) {
1082 return 0;
1083 }
1084 let max_block_size = cmp::max(maxBlockSize, 16);
1085 max_block_size.saturating_add((LZ4_DISTANCE_MAX + 1 + 14) as c_int)
1086}
1087
1088#[no_mangle]
1105pub unsafe extern "C" fn LZ4_decompress_fast(
1106 source: *const c_char,
1107 dest: *mut c_char,
1108 originalSize: c_int,
1109) -> c_int {
1110 if originalSize < 0 || source.is_null() || dest.is_null() {
1111 return -1;
1112 }
1113 let dst = slice::from_raw_parts_mut(dest as *mut u8, originalSize as usize);
1114 decompress_block_exact_ptr(source as *const u8, dst, &[]).map_or(-1, |n| n as c_int)
1115}
1116
1117#[no_mangle]
1121pub unsafe extern "C" fn LZ4_decompress_fast_usingDict(
1122 source: *const c_char,
1123 dest: *mut c_char,
1124 originalSize: c_int,
1125 dictStart: *const c_char,
1126 dictSize: c_int,
1127) -> c_int {
1128 if originalSize < 0
1129 || dictSize < 0
1130 || source.is_null()
1131 || dest.is_null()
1132 || (dictSize > 0 && dictStart.is_null())
1133 {
1134 return -1;
1135 }
1136 let dst = slice::from_raw_parts_mut(dest as *mut u8, originalSize as usize);
1137 let dict = if dictSize > 0 {
1138 slice::from_raw_parts(dictStart as *const u8, dictSize as usize)
1139 } else {
1140 &[]
1141 };
1142 decompress_block_exact_ptr(source as *const u8, dst, dict).map_or(-1, |n| n as c_int)
1143}
1144
1145#[no_mangle]
1147pub unsafe extern "C" fn LZ4_uncompress(
1148 source: *const c_char,
1149 dest: *mut c_char,
1150 outputSize: c_int,
1151) -> c_int {
1152 LZ4_decompress_fast(source, dest, outputSize)
1153}
1154
1155#[no_mangle]
1157pub unsafe extern "C" fn LZ4_uncompress_unknownOutputSize(
1158 source: *const c_char,
1159 dest: *mut c_char,
1160 isize: c_int,
1161 maxOutputSize: c_int,
1162) -> c_int {
1163 LZ4_decompress_safe(source, dest, isize, maxOutputSize)
1164}
1165
1166#[no_mangle]
1169pub unsafe extern "C" fn LZ4_decompress_safe_withPrefix64k(
1170 source: *const c_char,
1171 dest: *mut c_char,
1172 compressedSize: c_int,
1173 maxDstSize: c_int,
1174) -> c_int {
1175 LZ4_decompress_safe_usingDict(
1176 source,
1177 dest,
1178 compressedSize,
1179 maxDstSize,
1180 if maxDstSize > 0 {
1181 dest.sub(cmp::min(maxDstSize as usize, LZ4_DISTANCE_MAX))
1182 } else {
1183 dest
1184 },
1185 cmp::min(maxDstSize.max(0) as usize, LZ4_DISTANCE_MAX) as c_int,
1186 )
1187}
1188
1189#[no_mangle]
1192pub unsafe extern "C" fn LZ4_decompress_fast_withPrefix64k(
1193 source: *const c_char,
1194 dest: *mut c_char,
1195 originalSize: c_int,
1196) -> c_int {
1197 LZ4_decompress_fast_usingDict(
1198 source,
1199 dest,
1200 originalSize,
1201 if originalSize > 0 {
1202 dest.sub(cmp::min(originalSize as usize, LZ4_DISTANCE_MAX))
1203 } else {
1204 dest
1205 },
1206 cmp::min(originalSize.max(0) as usize, LZ4_DISTANCE_MAX) as c_int,
1207 )
1208}
1209
1210#[no_mangle]
1218pub unsafe extern "C" fn LZ4_decompress_fast_continue(
1219 stream: *mut LZ4StreamDecode,
1220 source: *const c_char,
1221 dest: *mut c_char,
1222 originalSize: c_int,
1223) -> c_int {
1224 if stream.is_null() || originalSize < 0 || source.is_null() || dest.is_null() {
1225 return -1;
1226 }
1227 let ctx = &mut *(stream as *mut DecodeStreamCtx);
1228 let dst = slice::from_raw_parts_mut(dest as *mut u8, originalSize as usize);
1229 match decompress_block_exact_ptr(source as *const u8, dst, &ctx.dictionary) {
1230 Some(n) => {
1231 append_hc_dictionary(&mut ctx.dictionary, dst);
1232 n as c_int
1233 }
1234 None => -1,
1235 }
1236}
1237
1238#[no_mangle]
1240pub extern "C" fn LZ4F_isError(code: size_t) -> c_uint {
1241 (code > lz4f_error(LZ4F_ERROR_MAX_CODE)) as c_uint
1242}
1243
1244#[no_mangle]
1246pub extern "C" fn LZ4F_getErrorName(code: size_t) -> *const c_char {
1247 match code {
1248 ERROR_MAX_BLOCK_SIZE_INVALID => ERROR_MAX_BLOCK_SIZE_NAME.as_ptr() as *const c_char,
1249 ERROR_BLOCK_MODE_INVALID => ERROR_BLOCK_MODE_NAME.as_ptr() as *const c_char,
1250 ERROR_PARAMETER_INVALID => ERROR_PARAMETER_INVALID_NAME.as_ptr() as *const c_char,
1251 ERROR_COMPRESSION_LEVEL_INVALID => ERROR_COMPRESSION_LEVEL_NAME.as_ptr() as *const c_char,
1252 ERROR_HEADER_VERSION_WRONG => ERROR_HEADER_VERSION_NAME.as_ptr() as *const c_char,
1253 ERROR_BLOCK_CHECKSUM_INVALID => ERROR_BLOCK_CHECKSUM_NAME.as_ptr() as *const c_char,
1254 ERROR_RESERVED_FLAG_SET => ERROR_RESERVED_FLAG_NAME.as_ptr() as *const c_char,
1255 ERROR_ALLOCATION_FAILED => ERROR_ALLOCATION_FAILED_NAME.as_ptr() as *const c_char,
1256 ERROR_SRC_SIZE_TOO_LARGE => ERROR_SRC_SIZE_TOO_LARGE_NAME.as_ptr() as *const c_char,
1257 ERROR_FRAME_SIZE_WRONG => ERROR_FRAME_SIZE_NAME.as_ptr() as *const c_char,
1258 ERROR_FRAME_TYPE_UNKNOWN => ERROR_FRAME_TYPE_NAME.as_ptr() as *const c_char,
1259 ERROR_SRC_PTR_WRONG => ERROR_SRC_PTR_NAME.as_ptr() as *const c_char,
1260 ERROR_DECOMPRESSION_FAILED => ERROR_DECOMPRESSION_FAILED_NAME.as_ptr() as *const c_char,
1261 ERROR_HEADER_CHECKSUM_INVALID => ERROR_HEADER_CHECKSUM_NAME.as_ptr() as *const c_char,
1262 ERROR_DST_TOO_SMALL => ERROR_DST_NAME.as_ptr() as *const c_char,
1263 ERROR_BAD_HEADER => ERROR_BAD_HEADER_NAME.as_ptr() as *const c_char,
1264 ERROR_CHECKSUM_INVALID => ERROR_CHECKSUM_NAME.as_ptr() as *const c_char,
1265 ERROR_FRAME_DECODING_ALREADY_STARTED => {
1266 ERROR_FRAME_DECODING_ALREADY_STARTED_NAME.as_ptr() as *const c_char
1267 }
1268 ERROR_COMPRESSION_STATE_UNINITIALIZED => {
1269 ERROR_COMPRESSION_STATE_UNINITIALIZED_NAME.as_ptr() as *const c_char
1270 }
1271 ERROR_PARAMETER_NULL => ERROR_PARAMETER_NULL_NAME.as_ptr() as *const c_char,
1272 ERROR_IO_WRITE => ERROR_IO_WRITE_NAME.as_ptr() as *const c_char,
1273 ERROR_IO_READ => ERROR_IO_READ_NAME.as_ptr() as *const c_char,
1274 ERROR_GENERIC => ERROR_GENERIC_NAME.as_ptr() as *const c_char,
1275 _ => ERROR_UNSPECIFIED_NAME.as_ptr() as *const c_char,
1276 }
1277}
1278
1279#[no_mangle]
1282pub extern "C" fn LZ4F_getErrorCode(code: size_t) -> c_uint {
1283 if LZ4F_isError(code) == 0 {
1284 0
1285 } else {
1286 (usize::MAX - code + 1) as c_uint
1287 }
1288}
1289
1290#[no_mangle]
1292pub extern "C" fn LZ4F_getVersion() -> c_uint {
1293 LZ4F_VERSION
1294}
1295
1296#[no_mangle]
1298pub extern "C" fn LZ4F_compressionLevel_max() -> c_int {
1299 LZ4HC_CLEVEL_MAX
1300}
1301
1302#[no_mangle]
1307pub extern "C" fn LZ4F_getBlockSize(blockSizeID: c_uint) -> size_t {
1308 let blockSizeID = if blockSizeID == 0 { 4 } else { blockSizeID };
1309 if !(4..=7).contains(&blockSizeID) {
1310 return ERROR_MAX_BLOCK_SIZE_INVALID;
1311 }
1312 block_max_size(blockSizeID as u8)
1313}
1314
1315#[no_mangle]
1324pub unsafe extern "C" fn LZ4F_createCompressionContext(
1325 ctx: &mut LZ4FCompressionContext,
1326 version: c_uint,
1327) -> LZ4FErrorCode {
1328 if version != LZ4F_VERSION {
1329 return ERROR_PARAMETER_INVALID;
1330 }
1331 let inner = Box::new(CompressionCtx {
1332 prefs: FramePrefs::default(),
1333 content_hasher: XxHash32::new(0),
1334 dictionary: Vec::new(),
1335 pending: Vec::new(),
1336 total_input: 0,
1337 external_dictionary: false,
1338 started: false,
1339 });
1340 ctx.0 = Box::into_raw(inner) as *mut c_void;
1341 0
1342}
1343
1344#[no_mangle]
1348pub unsafe extern "C" fn LZ4F_freeCompressionContext(ctx: LZ4FCompressionContext) -> LZ4FErrorCode {
1349 if !ctx.0.is_null() {
1350 drop(Box::from_raw(ctx.0 as *mut CompressionCtx));
1351 }
1352 0
1353}
1354
1355#[no_mangle]
1358pub unsafe extern "C" fn LZ4F_cctx_size(ctx: LZ4FCompressionContext) -> size_t {
1359 if ctx.0.is_null() {
1360 0
1361 } else {
1362 std::mem::size_of::<CompressionCtx>()
1363 }
1364}
1365
1366#[no_mangle]
1374pub unsafe extern "C" fn LZ4F_compressBegin(
1375 ctx: LZ4FCompressionContext,
1376 dstBuffer: *mut u8,
1377 dstMaxSize: size_t,
1378 preferencesPtr: *const LZ4FPreferences,
1379) -> LZ4FErrorCode {
1380 if ctx.0.is_null() || dstBuffer.is_null() {
1381 return ERROR_PARAMETER_NULL;
1382 }
1383 let inner = &mut *(ctx.0 as *mut CompressionCtx);
1384 inner.prefs = preferences_from_ptr(preferencesPtr);
1385 inner.content_hasher = XxHash32::new(0);
1386 inner.dictionary.clear();
1387 inner.pending.clear();
1388 inner.total_input = 0;
1389 inner.external_dictionary = false;
1390 inner.started = true;
1391 let header = frame_header(inner.prefs);
1392 if dstMaxSize < header.len() {
1393 return ERROR_DST_TOO_SMALL;
1394 }
1395 ptr::copy_nonoverlapping(header.as_ptr(), dstBuffer, header.len());
1396 header.len()
1397}
1398
1399#[no_mangle]
1412pub unsafe extern "C" fn LZ4F_compressBegin_usingDict(
1413 ctx: LZ4FCompressionContext,
1414 dstBuffer: *mut c_void,
1415 dstCapacity: size_t,
1416 dictBuffer: *const c_void,
1417 dictSize: size_t,
1418 preferencesPtr: *const LZ4FPreferences,
1419) -> LZ4FErrorCode {
1420 if dictSize > 0 && dictBuffer.is_null() {
1421 return ERROR_SRC_PTR_WRONG;
1422 }
1423 let written = LZ4F_compressBegin(ctx, dstBuffer as *mut u8, dstCapacity, preferencesPtr);
1424 if LZ4F_isError(written) != 0 {
1425 return written;
1426 }
1427 if dictSize > 0 {
1428 let inner = &mut *(ctx.0 as *mut CompressionCtx);
1429 let dict = slice::from_raw_parts(dictBuffer as *const u8, dictSize);
1430 let keep = cmp::min(dict.len(), LZ4_DISTANCE_MAX);
1431 inner.dictionary.clear();
1432 inner
1433 .dictionary
1434 .extend_from_slice(&dict[dict.len() - keep..]);
1435 inner.external_dictionary = true;
1436 }
1437 written
1438}
1439
1440#[no_mangle]
1448pub unsafe extern "C" fn LZ4F_createCDict(
1449 dictBuffer: *const c_void,
1450 dictSize: size_t,
1451) -> *mut LZ4FCDict {
1452 if dictSize > 0 && dictBuffer.is_null() {
1453 return ptr::null_mut();
1454 }
1455 let mut ctx = CDictCtx::default();
1456 if dictSize > 0 {
1457 let dict = slice::from_raw_parts(dictBuffer as *const u8, dictSize);
1458 let keep = cmp::min(dict.len(), LZ4_DISTANCE_MAX);
1459 ctx.dictionary.extend_from_slice(&dict[dict.len() - keep..]);
1460 }
1461 Box::into_raw(Box::new(ctx)) as *mut LZ4FCDict
1462}
1463
1464#[no_mangle]
1466pub unsafe extern "C" fn LZ4F_freeCDict(cdict: *mut LZ4FCDict) {
1467 if !cdict.is_null() {
1468 drop(Box::from_raw(cdict as *mut CDictCtx));
1469 }
1470}
1471
1472#[no_mangle]
1481pub unsafe extern "C" fn LZ4F_compressBegin_usingCDict(
1482 ctx: LZ4FCompressionContext,
1483 dstBuffer: *mut c_void,
1484 dstCapacity: size_t,
1485 cdict: *const LZ4FCDict,
1486 preferencesPtr: *const LZ4FPreferences,
1487) -> size_t {
1488 if cdict.is_null() {
1489 return LZ4F_compressBegin(ctx, dstBuffer as *mut u8, dstCapacity, preferencesPtr);
1490 }
1491 let dict = &*(cdict as *const CDictCtx);
1492 LZ4F_compressBegin_usingDict(
1493 ctx,
1494 dstBuffer,
1495 dstCapacity,
1496 dict.dictionary.as_ptr() as *const c_void,
1497 dict.dictionary.len(),
1498 preferencesPtr,
1499 )
1500}
1501
1502#[no_mangle]
1521pub unsafe extern "C" fn LZ4F_compressBound(
1522 srcSize: size_t,
1523 preferencesPtr: *const LZ4FPreferences,
1524) -> size_t {
1525 let prefs = preferences_from_ptr(preferencesPtr);
1526 let checksums = if prefs.block_checksum { 4 } else { 0 };
1527 let block_max = block_max_size(prefs.block_size_id);
1528 let buffered = if prefs.auto_flush { 0 } else { block_max };
1529 let total = srcSize.saturating_add(buffered);
1530 let blocks = if total == 0 {
1531 1
1532 } else {
1533 total.div_ceil(block_max)
1534 };
1535 total + blocks * (4 + checksums) + 16
1536}
1537
1538#[no_mangle]
1546pub unsafe extern "C" fn LZ4F_compressFrameBound(
1547 srcSize: size_t,
1548 preferencesPtr: *const LZ4FPreferences,
1549) -> size_t {
1550 LZ4F_compressBound(srcSize, preferencesPtr) + 19 + 4
1551}
1552
1553#[no_mangle]
1565pub unsafe extern "C" fn LZ4F_compressFrame(
1566 dstBuffer: *mut c_void,
1567 dstCapacity: size_t,
1568 srcBuffer: *const c_void,
1569 srcSize: size_t,
1570 preferencesPtr: *const LZ4FPreferences,
1571) -> size_t {
1572 if dstBuffer.is_null() || (srcSize > 0 && srcBuffer.is_null()) {
1573 return ERROR_PARAMETER_NULL;
1574 }
1575 let mut preferences;
1576 let begin_preferences = if preferencesPtr.is_null() {
1577 ptr::null()
1578 } else {
1579 preferences = (*preferencesPtr).clone();
1580 if preferences.frame_info.content_size != 0 {
1581 preferences.frame_info.content_size = srcSize as u64;
1582 }
1583 &preferences
1584 };
1585 let mut ctx = LZ4FCompressionContext(ptr::null_mut());
1586 let code = LZ4F_createCompressionContext(&mut ctx, LZ4F_VERSION);
1587 if LZ4F_isError(code) != 0 {
1588 return code;
1589 }
1590
1591 let dst = dstBuffer as *mut u8;
1592 let mut pos = LZ4F_compressBegin(ctx, dst, dstCapacity, begin_preferences);
1593 if LZ4F_isError(pos) != 0 {
1594 LZ4F_freeCompressionContext(ctx);
1595 return pos;
1596 }
1597 let update = LZ4F_compressUpdate(
1598 ctx,
1599 dst.add(pos),
1600 dstCapacity.saturating_sub(pos),
1601 srcBuffer as *const u8,
1602 srcSize,
1603 ptr::null(),
1604 );
1605 if LZ4F_isError(update) != 0 {
1606 LZ4F_freeCompressionContext(ctx);
1607 return update;
1608 }
1609 pos += update;
1610 let end = LZ4F_compressEnd(
1611 ctx,
1612 dst.add(pos),
1613 dstCapacity.saturating_sub(pos),
1614 ptr::null(),
1615 );
1616 LZ4F_freeCompressionContext(ctx);
1617 if LZ4F_isError(end) != 0 {
1618 return end;
1619 }
1620 pos + end
1621}
1622
1623#[no_mangle]
1638pub unsafe extern "C" fn LZ4F_compressFrame_usingCDict(
1639 ctx: LZ4FCompressionContext,
1640 dstBuffer: *mut c_void,
1641 dstCapacity: size_t,
1642 srcBuffer: *const c_void,
1643 srcSize: size_t,
1644 cdict: *const LZ4FCDict,
1645 preferencesPtr: *const LZ4FPreferences,
1646) -> size_t {
1647 if ctx.0.is_null() || dstBuffer.is_null() || (srcSize > 0 && srcBuffer.is_null()) {
1648 return ERROR_PARAMETER_NULL;
1649 }
1650 let dst = dstBuffer as *mut u8;
1651 let mut pos = LZ4F_compressBegin_usingCDict(ctx, dstBuffer, dstCapacity, cdict, preferencesPtr);
1652 if LZ4F_isError(pos) != 0 {
1653 return pos;
1654 }
1655 let update = LZ4F_compressUpdate(
1656 ctx,
1657 dst.add(pos),
1658 dstCapacity.saturating_sub(pos),
1659 srcBuffer as *const u8,
1660 srcSize,
1661 ptr::null(),
1662 );
1663 if LZ4F_isError(update) != 0 {
1664 return update;
1665 }
1666 pos += update;
1667 let end = LZ4F_compressEnd(
1668 ctx,
1669 dst.add(pos),
1670 dstCapacity.saturating_sub(pos),
1671 ptr::null(),
1672 );
1673 if LZ4F_isError(end) != 0 {
1674 return end;
1675 }
1676 pos + end
1677}
1678
1679#[no_mangle]
1696pub unsafe extern "C" fn LZ4F_compressUpdate(
1697 ctx: LZ4FCompressionContext,
1698 dstBuffer: *mut u8,
1699 dstCapacity: size_t,
1700 srcBuffer: *const u8,
1701 srcSize: size_t,
1702 _cOptPtr: *const LZ4FCompressOptions,
1703) -> size_t {
1704 if ctx.0.is_null() || dstBuffer.is_null() || (srcSize > 0 && srcBuffer.is_null()) {
1705 return ERROR_PARAMETER_NULL;
1706 }
1707 let inner = &mut *(ctx.0 as *mut CompressionCtx);
1708 if !inner.started {
1709 return ERROR_COMPRESSION_STATE_UNINITIALIZED;
1710 }
1711 let src = if srcSize > 0 {
1712 slice::from_raw_parts(srcBuffer, srcSize)
1713 } else {
1714 &[]
1715 };
1716 let block_max = block_max_size(inner.prefs.block_size_id);
1717 let dst = slice::from_raw_parts_mut(dstBuffer, dstCapacity);
1718 if inner.prefs.auto_flush {
1719 return compress_frame_update_blocks(inner, src, dst, true);
1720 }
1721
1722 let mut written = 0usize;
1723 if !inner.pending.is_empty() {
1724 let fill = cmp::min(block_max - inner.pending.len(), src.len());
1725 inner.pending.extend_from_slice(&src[..fill]);
1726 if inner.pending.len() < block_max {
1727 return 0;
1728 }
1729
1730 let pending = std::mem::take(&mut inner.pending);
1731 let n = compress_frame_update_block(inner, &pending, dst);
1732 inner.pending = pending;
1733 inner.pending.clear();
1734 if LZ4F_isError(n) != 0 {
1735 return n;
1736 }
1737 written += n;
1738 let rest = &src[fill..];
1739 let full_len = rest.len() / block_max * block_max;
1740 if full_len > 0 {
1741 let Some(remaining_dst) = dst.get_mut(written..) else {
1742 return ERROR_DST_TOO_SMALL;
1743 };
1744 let n = compress_frame_update_blocks(inner, &rest[..full_len], remaining_dst, false);
1745 if LZ4F_isError(n) != 0 {
1746 return n;
1747 }
1748 written += n;
1749 }
1750 if full_len < rest.len() {
1751 inner.pending.extend_from_slice(&rest[full_len..]);
1752 }
1753 return written;
1754 }
1755
1756 let full_len = src.len() / block_max * block_max;
1757 if full_len > 0 {
1758 let n = compress_frame_update_blocks(inner, &src[..full_len], dst, false);
1759 if LZ4F_isError(n) != 0 {
1760 return n;
1761 }
1762 written += n;
1763 }
1764 if full_len < src.len() {
1765 inner.pending.extend_from_slice(&src[full_len..]);
1766 }
1767 written
1768}
1769
1770fn compress_frame_update_block(inner: &mut CompressionCtx, src: &[u8], dst: &mut [u8]) -> size_t {
1771 match (
1772 inner.prefs.block_checksum,
1773 inner.prefs.content_checksum,
1774 inner.prefs.block_independent,
1775 ) {
1776 (false, false, false) => {
1777 compress_frame_update_block_flags::<false, false, false>(inner, src, dst)
1778 }
1779 (false, false, true) => {
1780 compress_frame_update_block_flags::<false, false, true>(inner, src, dst)
1781 }
1782 (false, true, false) => {
1783 compress_frame_update_block_flags::<false, true, false>(inner, src, dst)
1784 }
1785 (false, true, true) => {
1786 compress_frame_update_block_flags::<false, true, true>(inner, src, dst)
1787 }
1788 (true, false, false) => {
1789 compress_frame_update_block_flags::<true, false, false>(inner, src, dst)
1790 }
1791 (true, false, true) => {
1792 compress_frame_update_block_flags::<true, false, true>(inner, src, dst)
1793 }
1794 (true, true, false) => {
1795 compress_frame_update_block_flags::<true, true, false>(inner, src, dst)
1796 }
1797 (true, true, true) => {
1798 compress_frame_update_block_flags::<true, true, true>(inner, src, dst)
1799 }
1800 }
1801}
1802
1803fn compress_frame_update_block_flags<
1804 const BLOCK_CHECKSUM: bool,
1805 const CONTENT_CHECKSUM: bool,
1806 const BLOCK_INDEPENDENT: bool,
1807>(
1808 inner: &mut CompressionCtx,
1809 src: &[u8],
1810 dst: &mut [u8],
1811) -> size_t {
1812 let checksum_len = if BLOCK_CHECKSUM { 4 } else { 0 };
1813 let raw_needed = 4 + src.len() + checksum_len;
1814 if dst.len() < raw_needed {
1815 return ERROR_DST_TOO_SMALL;
1816 }
1817
1818 let compressed_len = compress_frame_block(src, &mut dst[4..], &inner.prefs, &inner.dictionary);
1819 let (block_len, raw) = match compressed_len {
1820 Some(len) if len < src.len() => (len, false),
1821 _ => {
1822 dst[4..4 + src.len()].copy_from_slice(src);
1823 (src.len(), true)
1824 }
1825 };
1826
1827 let block_size = (block_len as u32) | if raw { 0x8000_0000 } else { 0 };
1828 dst[..4].copy_from_slice(&block_size.to_le_bytes());
1829 if CONTENT_CHECKSUM {
1830 inner.content_hasher.update(src);
1831 }
1832 inner.total_input = inner.total_input.saturating_add(src.len() as u64);
1833 if !BLOCK_INDEPENDENT {
1834 append_hc_dictionary(&mut inner.dictionary, src);
1835 inner.external_dictionary = false;
1836 } else if inner.external_dictionary {
1837 inner.dictionary.clear();
1838 inner.external_dictionary = false;
1839 }
1840 let needed = 4 + block_len + checksum_len;
1841 if BLOCK_CHECKSUM {
1842 let checksum = xxhash32(&dst[4..4 + block_len], 0);
1843 dst[4 + block_len..needed].copy_from_slice(&checksum.to_le_bytes());
1844 }
1845 needed
1846}
1847
1848fn compress_frame_update_blocks(
1849 inner: &mut CompressionCtx,
1850 src: &[u8],
1851 dst: &mut [u8],
1852 emit_partial: bool,
1853) -> size_t {
1854 match (
1855 inner.prefs.block_checksum,
1856 inner.prefs.content_checksum,
1857 inner.prefs.block_independent,
1858 ) {
1859 (false, false, false) => {
1860 compress_frame_update_blocks_flags::<false, false, false>(inner, src, dst, emit_partial)
1861 }
1862 (false, false, true) => {
1863 compress_frame_update_blocks_flags::<false, false, true>(inner, src, dst, emit_partial)
1864 }
1865 (false, true, false) => {
1866 compress_frame_update_blocks_flags::<false, true, false>(inner, src, dst, emit_partial)
1867 }
1868 (false, true, true) => {
1869 compress_frame_update_blocks_flags::<false, true, true>(inner, src, dst, emit_partial)
1870 }
1871 (true, false, false) => {
1872 compress_frame_update_blocks_flags::<true, false, false>(inner, src, dst, emit_partial)
1873 }
1874 (true, false, true) => {
1875 compress_frame_update_blocks_flags::<true, false, true>(inner, src, dst, emit_partial)
1876 }
1877 (true, true, false) => {
1878 compress_frame_update_blocks_flags::<true, true, false>(inner, src, dst, emit_partial)
1879 }
1880 (true, true, true) => {
1881 compress_frame_update_blocks_flags::<true, true, true>(inner, src, dst, emit_partial)
1882 }
1883 }
1884}
1885
1886fn compress_frame_update_blocks_flags<
1887 const BLOCK_CHECKSUM: bool,
1888 const CONTENT_CHECKSUM: bool,
1889 const BLOCK_INDEPENDENT: bool,
1890>(
1891 inner: &mut CompressionCtx,
1892 src: &[u8],
1893 dst: &mut [u8],
1894 emit_partial: bool,
1895) -> size_t {
1896 let block_max = block_max_size(inner.prefs.block_size_id);
1897 let mut written = 0usize;
1898 let mut chunks = src.chunks_exact(block_max);
1899 for chunk in &mut chunks {
1900 let Some(remaining_dst) = dst.get_mut(written..) else {
1901 return ERROR_DST_TOO_SMALL;
1902 };
1903 let n = compress_frame_update_block_flags::<
1904 BLOCK_CHECKSUM,
1905 CONTENT_CHECKSUM,
1906 BLOCK_INDEPENDENT,
1907 >(inner, chunk, remaining_dst);
1908 if LZ4F_isError(n) != 0 {
1909 return n;
1910 }
1911 written += n;
1912 }
1913 let remainder = chunks.remainder();
1914 if emit_partial && !remainder.is_empty() {
1915 let Some(remaining_dst) = dst.get_mut(written..) else {
1916 return ERROR_DST_TOO_SMALL;
1917 };
1918 let n = compress_frame_update_block_flags::<
1919 BLOCK_CHECKSUM,
1920 CONTENT_CHECKSUM,
1921 BLOCK_INDEPENDENT,
1922 >(inner, remainder, remaining_dst);
1923 if LZ4F_isError(n) != 0 {
1924 return n;
1925 }
1926 written += n;
1927 }
1928 written
1929}
1930
1931#[no_mangle]
1949pub unsafe extern "C" fn LZ4F_uncompressedUpdate(
1950 ctx: LZ4FCompressionContext,
1951 dstBuffer: *mut c_void,
1952 dstCapacity: size_t,
1953 srcBuffer: *const c_void,
1954 srcSize: size_t,
1955 _cOptPtr: *const LZ4FCompressOptions,
1956) -> size_t {
1957 if ctx.0.is_null() || dstBuffer.is_null() || (srcSize > 0 && srcBuffer.is_null()) {
1958 return ERROR_PARAMETER_NULL;
1959 }
1960 let inner = &mut *(ctx.0 as *mut CompressionCtx);
1961 if !inner.started {
1962 return ERROR_COMPRESSION_STATE_UNINITIALIZED;
1963 }
1964 let src = if srcSize > 0 {
1965 slice::from_raw_parts(srcBuffer as *const u8, srcSize)
1966 } else {
1967 &[]
1968 };
1969 let dst = slice::from_raw_parts_mut(dstBuffer as *mut u8, dstCapacity);
1970 compress_frame_raw_blocks(inner, src, dst)
1971}
1972
1973fn compress_frame_raw_block_flags<
1974 const BLOCK_CHECKSUM: bool,
1975 const CONTENT_CHECKSUM: bool,
1976 const BLOCK_INDEPENDENT: bool,
1977>(
1978 inner: &mut CompressionCtx,
1979 src: &[u8],
1980 dst: &mut [u8],
1981) -> size_t {
1982 let checksum_len = if BLOCK_CHECKSUM { 4 } else { 0 };
1983 let needed = 4 + src.len() + checksum_len;
1984 if dst.len() < needed {
1985 return ERROR_DST_TOO_SMALL;
1986 }
1987 let block_size = (src.len() as u32) | 0x8000_0000;
1988 dst[..4].copy_from_slice(&block_size.to_le_bytes());
1989 dst[4..4 + src.len()].copy_from_slice(src);
1990 if BLOCK_CHECKSUM {
1991 let checksum = xxhash32(&dst[4..4 + src.len()], 0);
1992 dst[4 + src.len()..needed].copy_from_slice(&checksum.to_le_bytes());
1993 }
1994 if CONTENT_CHECKSUM {
1995 inner.content_hasher.update(src);
1996 }
1997 inner.total_input = inner.total_input.saturating_add(src.len() as u64);
1998 if !BLOCK_INDEPENDENT {
1999 append_hc_dictionary(&mut inner.dictionary, src);
2000 } else if inner.external_dictionary {
2001 inner.dictionary.clear();
2002 inner.external_dictionary = false;
2003 }
2004 needed
2005}
2006
2007fn compress_frame_raw_blocks(inner: &mut CompressionCtx, src: &[u8], dst: &mut [u8]) -> size_t {
2008 match (
2009 inner.prefs.block_checksum,
2010 inner.prefs.content_checksum,
2011 inner.prefs.block_independent,
2012 ) {
2013 (false, false, false) => {
2014 compress_frame_raw_blocks_flags::<false, false, false>(inner, src, dst)
2015 }
2016 (false, false, true) => {
2017 compress_frame_raw_blocks_flags::<false, false, true>(inner, src, dst)
2018 }
2019 (false, true, false) => {
2020 compress_frame_raw_blocks_flags::<false, true, false>(inner, src, dst)
2021 }
2022 (false, true, true) => {
2023 compress_frame_raw_blocks_flags::<false, true, true>(inner, src, dst)
2024 }
2025 (true, false, false) => {
2026 compress_frame_raw_blocks_flags::<true, false, false>(inner, src, dst)
2027 }
2028 (true, false, true) => {
2029 compress_frame_raw_blocks_flags::<true, false, true>(inner, src, dst)
2030 }
2031 (true, true, false) => {
2032 compress_frame_raw_blocks_flags::<true, true, false>(inner, src, dst)
2033 }
2034 (true, true, true) => compress_frame_raw_blocks_flags::<true, true, true>(inner, src, dst),
2035 }
2036}
2037
2038fn compress_frame_raw_blocks_flags<
2039 const BLOCK_CHECKSUM: bool,
2040 const CONTENT_CHECKSUM: bool,
2041 const BLOCK_INDEPENDENT: bool,
2042>(
2043 inner: &mut CompressionCtx,
2044 src: &[u8],
2045 dst: &mut [u8],
2046) -> size_t {
2047 let block_max = block_max_size(inner.prefs.block_size_id);
2048 let mut written = 0usize;
2049 for chunk in src.chunks(block_max) {
2050 let Some(remaining_dst) = dst.get_mut(written..) else {
2051 return ERROR_DST_TOO_SMALL;
2052 };
2053 let n = compress_frame_raw_block_flags::<BLOCK_CHECKSUM, CONTENT_CHECKSUM, BLOCK_INDEPENDENT>(
2054 inner,
2055 chunk,
2056 remaining_dst,
2057 );
2058 if LZ4F_isError(n) != 0 {
2059 return n;
2060 }
2061 written += n;
2062 }
2063 written
2064}
2065
2066#[no_mangle]
2081pub unsafe extern "C" fn LZ4F_flush(
2082 ctx: LZ4FCompressionContext,
2083 dstBuffer: *mut u8,
2084 dstCapacity: size_t,
2085 _cOptPtr: *const LZ4FCompressOptions,
2086) -> size_t {
2087 if ctx.0.is_null() || dstBuffer.is_null() {
2088 return ERROR_PARAMETER_NULL;
2089 }
2090 let inner = &mut *(ctx.0 as *mut CompressionCtx);
2091 if !inner.started {
2092 return ERROR_COMPRESSION_STATE_UNINITIALIZED;
2093 }
2094 if inner.pending.is_empty() {
2095 return 0;
2096 }
2097 let dst = slice::from_raw_parts_mut(dstBuffer, dstCapacity);
2098 let pending = std::mem::take(&mut inner.pending);
2099 let n = compress_frame_update_block(inner, &pending, dst);
2100 if LZ4F_isError(n) == 0 {
2101 inner.pending.clear();
2102 } else {
2103 inner.pending = pending;
2104 }
2105 n
2106}
2107
2108#[no_mangle]
2121pub unsafe extern "C" fn LZ4F_compressEnd(
2122 ctx: LZ4FCompressionContext,
2123 dstBuffer: *mut u8,
2124 dstCapacity: size_t,
2125 _cOptPtr: *const LZ4FCompressOptions,
2126) -> size_t {
2127 if ctx.0.is_null() || dstBuffer.is_null() {
2128 return ERROR_PARAMETER_NULL;
2129 }
2130 let inner = &mut *(ctx.0 as *mut CompressionCtx);
2131 if !inner.started {
2132 return ERROR_COMPRESSION_STATE_UNINITIALIZED;
2133 }
2134 let footer_len = 4 + if inner.prefs.content_checksum { 4 } else { 0 };
2135 if dstCapacity < footer_len {
2136 return ERROR_DST_TOO_SMALL;
2137 }
2138 let dst = slice::from_raw_parts_mut(dstBuffer, dstCapacity);
2139 let mut pos = 0usize;
2140 if !inner.pending.is_empty() {
2141 let checksum_len = if inner.prefs.block_checksum { 4 } else { 0 };
2142 let max_pending_len = 4 + inner.pending.len() + checksum_len + footer_len;
2143 if dstCapacity < max_pending_len {
2144 return ERROR_DST_TOO_SMALL;
2145 }
2146 let pending = std::mem::take(&mut inner.pending);
2147 let n = compress_frame_update_block(inner, &pending, dst);
2148 if LZ4F_isError(n) != 0 {
2149 inner.pending = pending;
2150 return n;
2151 }
2152 pos += n;
2153 }
2154 if inner.prefs.content_size != 0 && inner.total_input != inner.prefs.content_size {
2155 return ERROR_FRAME_SIZE_WRONG;
2156 }
2157 if dstCapacity - pos < footer_len {
2158 return ERROR_DST_TOO_SMALL;
2159 }
2160 dst[pos..pos + 4].copy_from_slice(&0u32.to_le_bytes());
2161 if inner.prefs.content_checksum {
2162 dst[pos + 4..pos + 8].copy_from_slice(&inner.content_hasher.digest().to_le_bytes());
2163 }
2164 inner.started = false;
2165 pos + footer_len
2166}
2167
2168#[no_mangle]
2177pub unsafe extern "C" fn LZ4F_createDecompressionContext(
2178 ctx: &mut LZ4FDecompressionContext,
2179 version: c_uint,
2180) -> LZ4FErrorCode {
2181 if version != LZ4F_VERSION {
2182 return ERROR_PARAMETER_INVALID;
2183 }
2184 ctx.0 = Box::into_raw(Box::<DecompressionCtx>::default()) as *mut c_void;
2185 0
2186}
2187
2188#[no_mangle]
2190pub unsafe extern "C" fn LZ4F_freeDecompressionContext(
2191 ctx: LZ4FDecompressionContext,
2192) -> LZ4FErrorCode {
2193 if !ctx.0.is_null() {
2194 let boxed = Box::from_raw(ctx.0 as *mut DecompressionCtx);
2195 let status = decompression_free_status(&boxed);
2196 drop(boxed);
2197 return status;
2198 }
2199 0
2200}
2201
2202#[no_mangle]
2205pub unsafe extern "C" fn LZ4F_dctx_size(ctx: LZ4FDecompressionContext) -> size_t {
2206 if ctx.0.is_null() {
2207 0
2208 } else {
2209 std::mem::size_of::<DecompressionCtx>()
2210 }
2211}
2212
2213#[no_mangle]
2247pub unsafe extern "C" fn LZ4F_getFrameInfo(
2248 ctx: LZ4FDecompressionContext,
2249 frameInfoPtr: *mut LZ4FFrameInfo,
2250 srcBuffer: *const u8,
2251 srcSizePtr: *mut size_t,
2252) -> size_t {
2253 if ctx.0.is_null() || frameInfoPtr.is_null() || srcSizePtr.is_null() {
2254 return ERROR_PARAMETER_NULL;
2255 }
2256 let inner = &mut *(ctx.0 as *mut DecompressionCtx);
2257 if inner.parsed_header {
2258 *srcSizePtr = 0;
2259 *frameInfoPtr = frame_info_from_decompression_ctx(inner);
2260 return frame_hint(inner);
2261 }
2262 if !inner.input.is_empty() {
2263 *srcSizePtr = 0;
2264 return ERROR_FRAME_DECODING_ALREADY_STARTED;
2265 }
2266 let src_size = *srcSizePtr;
2267 if srcBuffer.is_null() {
2268 *srcSizePtr = 0;
2269 return ERROR_SRC_PTR_WRONG;
2270 }
2271 if src_size < 7 {
2272 *srcSizePtr = 0;
2273 return ERROR_BAD_HEADER;
2274 }
2275 let src = slice::from_raw_parts(srcBuffer, src_size);
2276 if is_skippable_magic_prefix(src) {
2277 let Some(skip_len) = parse_skippable_frame_len(src) else {
2278 return ERROR_BAD_HEADER;
2279 };
2280 *srcSizePtr = cmp::min(skip_len, src_size);
2281 *frameInfoPtr = LZ4FFrameInfo {
2282 block_size_id: BlockSize::Default,
2283 block_mode: BlockMode::Independent,
2284 content_checksum_flag: ContentChecksum::NoChecksum,
2285 frame_type: FrameType::SkippableFrame,
2286 content_size: 0,
2287 dict_id: 0,
2288 block_checksum_flag: BlockChecksum::NoBlockChecksum,
2289 };
2290 return 0;
2291 }
2292 let (prefs, header_len) = match parse_frame_header(src) {
2293 Ok(parsed) => parsed,
2294 Err(code) => {
2295 *srcSizePtr = 0;
2296 return code;
2297 }
2298 };
2299 if !inner.parsed_header && !inner.done {
2300 apply_frame_prefs(inner, prefs);
2301 }
2302 *srcSizePtr = header_len;
2303 *frameInfoPtr = LZ4FFrameInfo {
2304 block_size_id: block_size_enum(prefs.block_size_id),
2305 block_mode: if prefs.block_independent {
2306 BlockMode::Independent
2307 } else {
2308 BlockMode::Linked
2309 },
2310 content_checksum_flag: if prefs.content_checksum {
2311 ContentChecksum::ChecksumEnabled
2312 } else {
2313 ContentChecksum::NoChecksum
2314 },
2315 frame_type: FrameType::Frame,
2316 content_size: prefs.content_size,
2317 dict_id: prefs.dict_id,
2318 block_checksum_flag: if prefs.block_checksum {
2319 BlockChecksum::BlockChecksumEnabled
2320 } else {
2321 BlockChecksum::NoBlockChecksum
2322 },
2323 };
2324 0
2325}
2326
2327#[no_mangle]
2337pub unsafe extern "C" fn LZ4F_headerSize(src: *const c_void, srcSize: size_t) -> size_t {
2338 if src.is_null() {
2339 return ERROR_SRC_PTR_WRONG;
2340 }
2341 if srcSize < 5 {
2342 return ERROR_BAD_HEADER;
2343 }
2344 let src = slice::from_raw_parts(src as *const u8, srcSize);
2345 if is_skippable_magic_prefix(src) {
2346 return 8;
2347 }
2348 if src[..4] != LZ4F_MAGIC {
2349 return ERROR_FRAME_TYPE_UNKNOWN;
2350 }
2351 expected_frame_header_len(src).unwrap_or(ERROR_BAD_HEADER)
2352}
2353
2354#[no_mangle]
2385pub unsafe extern "C" fn LZ4F_decompress(
2386 ctx: LZ4FDecompressionContext,
2387 dstBuffer: *mut u8,
2388 dstSizePtr: *mut size_t,
2389 srcBuffer: *const u8,
2390 srcSizePtr: *mut size_t,
2391 dOptPtr: *const LZ4FDecompressOptions,
2392) -> size_t {
2393 if ctx.0.is_null() || dstSizePtr.is_null() || srcSizePtr.is_null() {
2394 return ERROR_PARAMETER_NULL;
2395 }
2396 let _stable_dst = !dOptPtr.is_null() && (*dOptPtr).stable_dst != 0;
2400 let inner = &mut *(ctx.0 as *mut DecompressionCtx);
2401 if !dOptPtr.is_null() && (*dOptPtr).skipChecksums != 0 {
2402 inner.skip_checksums = true;
2403 }
2404 let skip_checksums = inner.skip_checksums;
2405 let src_size = *srcSizePtr;
2406 let dst_capacity = *dstSizePtr;
2407 if dst_capacity > 0 && dstBuffer.is_null() {
2408 return ERROR_PARAMETER_NULL;
2409 }
2410
2411 if src_size > 0
2412 && dst_capacity > 0
2413 && !srcBuffer.is_null()
2414 && inner.input.is_empty()
2415 && pending_is_empty(inner)
2416 && inner.parsed_header
2417 && !inner.done
2418 {
2419 let src = slice::from_raw_parts(srcBuffer, src_size);
2420 if let Some(result) = try_copy_raw_block_slice_to_dst(
2421 inner,
2422 src,
2423 slice::from_raw_parts_mut(dstBuffer, dst_capacity),
2424 skip_checksums,
2425 ) {
2426 match result {
2427 Ok((consumed, written)) => {
2428 *srcSizePtr = consumed;
2429 *dstSizePtr = written;
2430 if inner.done && pending_is_empty(inner) {
2431 *inner = DecompressionCtx::default();
2432 return 0;
2433 }
2434 return frame_hint(inner);
2435 }
2436 Err(code) => return code,
2437 }
2438 }
2439 if let Some(result) = try_decompress_frame_block_slice_to_dst(
2440 inner,
2441 src,
2442 slice::from_raw_parts_mut(dstBuffer, dst_capacity),
2443 skip_checksums,
2444 ) {
2445 match result {
2446 Ok((consumed, written)) => {
2447 *srcSizePtr = consumed;
2448 *dstSizePtr = written;
2449 if inner.done && pending_is_empty(inner) {
2450 *inner = DecompressionCtx::default();
2451 return 0;
2452 }
2453 return frame_hint(inner);
2454 }
2455 Err(code) => return code,
2456 }
2457 }
2458 }
2459
2460 if src_size > 0 {
2461 if srcBuffer.is_null() {
2462 return ERROR_SRC_PTR_WRONG;
2463 }
2464 inner
2465 .input
2466 .extend_from_slice(slice::from_raw_parts(srcBuffer, src_size));
2467 }
2468
2469 if pending_is_empty(inner) && dst_capacity > 0 {
2470 if let Err(code) = parse_frame_header_if_available(inner) {
2471 return code;
2472 }
2473 let dst_slice = slice::from_raw_parts_mut(dstBuffer, dst_capacity);
2480 let mut total_written = 0usize;
2481 loop {
2482 let remaining = &mut dst_slice[total_written..];
2483 if remaining.is_empty() {
2484 break;
2485 }
2486 if inner.raw_block_remaining > 0 {
2487 match copy_raw_block_from_input_to_dst(inner, remaining, skip_checksums) {
2488 Some(Ok(written)) => {
2489 total_written += written;
2490 if inner.done {
2491 break;
2492 }
2493 if written == 0 {
2494 break;
2495 }
2496 continue;
2497 }
2498 Some(Err(code)) => return code,
2499 None => break,
2500 }
2501 }
2502 match try_decompress_frame_block_to_dst(inner, remaining, skip_checksums) {
2503 Some(Ok(written)) => {
2504 total_written += written;
2505 if inner.done {
2506 break;
2507 }
2508 continue;
2514 }
2515 Some(Err(code)) => return code,
2516 None => break,
2517 }
2518 }
2519 if total_written > 0 || inner.done {
2520 let consumed = consumed_from_call(inner.done, src_size, inner.input.len());
2521 *srcSizePtr = consumed;
2522 *dstSizePtr = total_written;
2523 if inner.done && pending_is_empty(inner) {
2524 *inner = DecompressionCtx::default();
2525 return 0;
2526 }
2527 return frame_hint(inner);
2528 }
2529 }
2530
2531 if let Err(code) = parse_available_frame(inner, skip_checksums) {
2532 return code;
2533 }
2534 let pending_len = pending_len(inner);
2535 let to_copy = cmp::min(dst_capacity, pending_len);
2536 if to_copy > 0 {
2537 ptr::copy_nonoverlapping(
2538 inner.pending.as_ptr().add(inner.pending_pos),
2539 dstBuffer,
2540 to_copy,
2541 );
2542 inner.pending_pos += to_copy;
2543 if inner.pending_pos == inner.pending.len() {
2544 inner.pending.clear();
2545 inner.pending_pos = 0;
2546 }
2547 }
2548 *dstSizePtr = to_copy;
2549 if pending_is_empty(inner) && !inner.done {
2550 if let Err(code) = parse_available_frame(inner, skip_checksums) {
2551 return code;
2552 }
2553 }
2554 *srcSizePtr = consumed_from_call(inner.done, src_size, inner.input.len());
2555 if inner.done && pending_is_empty(inner) {
2556 *inner = DecompressionCtx::default();
2557 return 0;
2558 }
2559 frame_hint(inner)
2560}
2561
2562#[no_mangle]
2566pub unsafe extern "C" fn LZ4F_decompress_usingDict(
2567 ctx: LZ4FDecompressionContext,
2568 dstBuffer: *mut c_void,
2569 dstSizePtr: *mut size_t,
2570 srcBuffer: *const c_void,
2571 srcSizePtr: *mut size_t,
2572 dict: *const c_void,
2573 dictSize: size_t,
2574 decompressOptionsPtr: *const LZ4FDecompressOptions,
2575) -> size_t {
2576 if ctx.0.is_null() {
2577 return ERROR_PARAMETER_NULL;
2578 }
2579 if dictSize > 0 && dict.is_null() {
2580 return ERROR_SRC_PTR_WRONG;
2581 }
2582 let inner = &mut *(ctx.0 as *mut DecompressionCtx);
2583 if !inner.parsed_header && dictSize > 0 {
2584 let dict = slice::from_raw_parts(dict as *const u8, dictSize);
2585 let keep = cmp::min(dict.len(), LZ4_DISTANCE_MAX);
2586 inner.dictionary.clear();
2587 inner
2588 .dictionary
2589 .extend_from_slice(&dict[dict.len() - keep..]);
2590 inner.external_dictionary = true;
2591 }
2592 LZ4F_decompress(
2593 ctx,
2594 dstBuffer as *mut u8,
2595 dstSizePtr,
2596 srcBuffer as *const u8,
2597 srcSizePtr,
2598 decompressOptionsPtr,
2599 )
2600}
2601
2602#[no_mangle]
2608pub unsafe extern "C" fn LZ4F_resetDecompressionContext(ctx: LZ4FDecompressionContext) {
2609 if !ctx.0.is_null() {
2610 *(ctx.0 as *mut DecompressionCtx) = DecompressionCtx::default();
2611 }
2612}
2613
2614#[no_mangle]
2620pub unsafe extern "C" fn LZ4_createStream() -> *mut LZ4StreamEncode {
2621 Box::into_raw(Box::<EncodeStreamCtx>::default()) as *mut LZ4StreamEncode
2622}
2623
2624#[no_mangle]
2629pub unsafe extern "C" fn LZ4_freeStream(stream: *mut LZ4StreamEncode) -> c_int {
2630 if !stream.is_null() {
2631 drop(Box::from_raw(stream as *mut EncodeStreamCtx));
2632 }
2633 0
2634}
2635
2636#[no_mangle]
2644pub unsafe extern "C" fn LZ4_compress_continue(
2645 stream: *mut LZ4StreamEncode,
2646 src: *const c_char,
2647 dst: *mut c_char,
2648 srcSize: c_int,
2649 dstCapacity: c_int,
2650) -> c_int {
2651 if stream.is_null() || srcSize < 0 || dstCapacity <= 0 || src.is_null() || dst.is_null() {
2652 return 0;
2653 }
2654 let ctx = &mut *(stream as *mut EncodeStreamCtx);
2655 let src_slice = slice::from_raw_parts(src as *const u8, srcSize as usize);
2656 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, dstCapacity as usize);
2657 let written = if ctx.dictionary.is_empty() {
2658 compress_block(src_slice, dst_slice, 1)
2659 } else {
2660 compress_block_with_dict(src_slice, dst_slice, &ctx.dictionary, 1)
2661 }
2662 .map_or(0, |n| n as c_int);
2663 if written > 0 {
2664 if ctx.attached_dictionary {
2665 ctx.dictionary.clear();
2666 append_hc_dictionary(&mut ctx.dictionary, src_slice);
2667 ctx.attached_dictionary = false;
2668 } else {
2669 append_hc_dictionary(&mut ctx.dictionary, src_slice);
2670 }
2671 }
2672 written
2673}
2674
2675#[no_mangle]
2701pub unsafe extern "C" fn LZ4_compress_fast_continue(
2702 stream: *mut LZ4StreamEncode,
2703 src: *const c_char,
2704 dst: *mut c_char,
2705 srcSize: c_int,
2706 dstCapacity: c_int,
2707 acceleration: c_int,
2708) -> c_int {
2709 if stream.is_null() || srcSize < 0 || dstCapacity <= 0 || src.is_null() || dst.is_null() {
2710 return 0;
2711 }
2712 let ctx = &mut *(stream as *mut EncodeStreamCtx);
2713 let src_slice = slice::from_raw_parts(src as *const u8, srcSize as usize);
2714 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, dstCapacity as usize);
2715 let acceleration = normalize_acceleration(acceleration);
2716 let written = if ctx.dictionary.is_empty() {
2717 compress_block(src_slice, dst_slice, acceleration)
2718 } else {
2719 compress_block_with_dict(src_slice, dst_slice, &ctx.dictionary, acceleration)
2720 }
2721 .map_or(0, |n| n as c_int);
2722 if written > 0 {
2723 if ctx.attached_dictionary {
2724 ctx.dictionary.clear();
2725 append_hc_dictionary(&mut ctx.dictionary, src_slice);
2726 ctx.attached_dictionary = false;
2727 } else {
2728 append_hc_dictionary(&mut ctx.dictionary, src_slice);
2729 }
2730 }
2731 written
2732}
2733
2734#[no_mangle]
2737pub unsafe extern "C" fn LZ4_compress_limitedOutput_continue(
2738 stream: *mut LZ4StreamEncode,
2739 src: *const c_char,
2740 dst: *mut c_char,
2741 srcSize: c_int,
2742 maxOutputSize: c_int,
2743) -> c_int {
2744 LZ4_compress_continue(stream, src, dst, srcSize, maxOutputSize)
2745}
2746
2747#[no_mangle]
2766pub unsafe extern "C" fn LZ4_resetStream_fast(stream: *mut LZ4StreamEncode) {
2767 if !stream.is_null() {
2768 let ctx = &mut *(stream as *mut EncodeStreamCtx);
2769 ctx.dictionary.clear();
2770 ctx.attached_dictionary = false;
2771 }
2772}
2773
2774#[no_mangle]
2781pub unsafe extern "C" fn LZ4_resetStream(stream: *mut LZ4StreamEncode) {
2782 LZ4_resetStream_fast(stream)
2783}
2784
2785#[no_mangle]
2798pub unsafe extern "C" fn LZ4_initStream(
2799 stateBuffer: *mut c_void,
2800 size: size_t,
2801) -> *mut LZ4StreamEncode {
2802 if stateBuffer.is_null() {
2803 return ptr::null_mut();
2804 }
2805 if size < LZ4_sizeofStreamState() as usize {
2806 return ptr::null_mut();
2807 }
2808 if !(stateBuffer as usize).is_multiple_of(std::mem::align_of::<EncodeStreamCtx>()) {
2809 return ptr::null_mut();
2810 }
2811 ptr::write(
2812 stateBuffer as *mut EncodeStreamCtx,
2813 EncodeStreamCtx::default(),
2814 );
2815 stateBuffer as *mut LZ4StreamEncode
2816}
2817
2818#[no_mangle]
2824pub unsafe extern "C" fn LZ4_create(_inputBuffer: *mut c_char) -> *mut c_void {
2825 LZ4_createStream() as *mut c_void
2826}
2827
2828#[no_mangle]
2832pub extern "C" fn LZ4_sizeofStreamState() -> c_int {
2833 LZ4_sizeofState()
2834}
2835
2836#[no_mangle]
2841pub unsafe extern "C" fn LZ4_resetStreamState(
2842 state: *mut c_void,
2843 _inputBuffer: *mut c_char,
2844) -> c_int {
2845 if state.is_null() {
2846 return 1;
2847 }
2848 ptr::write(state as *mut EncodeStreamCtx, EncodeStreamCtx::default());
2849 0
2850}
2851
2852#[no_mangle]
2857pub unsafe extern "C" fn LZ4_slideInputBuffer(state: *mut c_void) -> *mut c_char {
2858 if state.is_null() {
2859 return ptr::null_mut();
2860 }
2861 let ctx = &mut *(state as *mut EncodeStreamCtx);
2862 if ctx.dictionary.is_empty() {
2863 ptr::null_mut()
2864 } else {
2865 ctx.dictionary.as_mut_ptr() as *mut c_char
2866 }
2867}
2868
2869#[no_mangle]
2892pub unsafe extern "C" fn LZ4_attach_dictionary(
2893 workingStream: *mut LZ4StreamEncode,
2894 dictionaryStream: *const LZ4StreamEncode,
2895) {
2896 if workingStream.is_null() {
2897 return;
2898 }
2899 let working = &mut *(workingStream as *mut EncodeStreamCtx);
2900 working.dictionary.clear();
2901 working.attached_dictionary = false;
2902 if !dictionaryStream.is_null() {
2903 let dictionary = &*(dictionaryStream as *const EncodeStreamCtx);
2904 working.dictionary.extend_from_slice(&dictionary.dictionary);
2905 working.attached_dictionary = !working.dictionary.is_empty();
2906 }
2907}
2908
2909#[no_mangle]
2921pub unsafe extern "C" fn LZ4_loadDict(
2922 stream: *mut LZ4StreamEncode,
2923 dictionary: *const c_char,
2924 dictSize: c_int,
2925) -> c_int {
2926 if stream.is_null() || dictSize < 0 || (dictSize > 0 && dictionary.is_null()) {
2927 return 0;
2928 }
2929 let ctx = &mut *(stream as *mut EncodeStreamCtx);
2930 ctx.dictionary.clear();
2931 ctx.attached_dictionary = false;
2932 if dictSize as usize >= HASH_UNIT {
2933 let dict = slice::from_raw_parts(dictionary as *const u8, dictSize as usize);
2934 let keep = cmp::min(dict.len(), LZ4_DISTANCE_MAX);
2935 ctx.dictionary.extend_from_slice(&dict[dict.len() - keep..]);
2936 }
2937 ctx.dictionary.len() as c_int
2938}
2939
2940#[no_mangle]
2950pub unsafe extern "C" fn LZ4_loadDictSlow(
2951 stream: *mut LZ4StreamEncode,
2952 dictionary: *const c_char,
2953 dictSize: c_int,
2954) -> c_int {
2955 LZ4_loadDict(stream, dictionary, dictSize)
2956}
2957
2958#[no_mangle]
2969pub unsafe extern "C" fn LZ4_saveDict(
2970 stream: *mut LZ4StreamEncode,
2971 safeBuffer: *mut c_char,
2972 maxDictSize: c_int,
2973) -> c_int {
2974 if stream.is_null() || maxDictSize < 0 || (maxDictSize > 0 && safeBuffer.is_null()) {
2975 return 0;
2976 }
2977 let ctx = &mut *(stream as *mut EncodeStreamCtx);
2978 let keep = cmp::min(
2979 ctx.dictionary.len(),
2980 cmp::min(maxDictSize as usize, LZ4_DISTANCE_MAX),
2981 );
2982 if keep > 0 {
2983 ptr::copy_nonoverlapping(
2984 ctx.dictionary[ctx.dictionary.len() - keep..].as_ptr(),
2985 safeBuffer as *mut u8,
2986 keep,
2987 );
2988 ctx.dictionary = ctx.dictionary[ctx.dictionary.len() - keep..].to_vec();
2989 ctx.attached_dictionary = false;
2990 } else {
2991 ctx.dictionary.clear();
2992 ctx.attached_dictionary = false;
2993 }
2994 keep as c_int
2995}
2996
2997#[no_mangle]
3004pub unsafe extern "C" fn LZ4_createStreamHC() -> *mut LZ4StreamHC {
3005 Box::into_raw(Box::<HcStreamCtx>::default()) as *mut LZ4StreamHC
3006}
3007
3008#[no_mangle]
3013pub unsafe extern "C" fn LZ4_freeStreamHC(stream: *mut LZ4StreamHC) -> c_int {
3014 if !stream.is_null() {
3015 drop(Box::from_raw(stream as *mut HcStreamCtx));
3016 }
3017 0
3018}
3019
3020#[no_mangle]
3035pub unsafe extern "C" fn LZ4_resetStreamHC_fast(stream: *mut LZ4StreamHC, compressionLevel: c_int) {
3036 if !stream.is_null() {
3037 let ctx = &mut *(stream as *mut HcStreamCtx);
3038 ctx.compression_level = normalize_hc_level(compressionLevel);
3039 ctx.dictionary.clear();
3040 ctx.attached_dictionary = false;
3041 }
3042}
3043
3044#[no_mangle]
3054pub unsafe extern "C" fn LZ4_resetStreamHC(stream: *mut LZ4StreamHC, compressionLevel: c_int) {
3055 if !stream.is_null() {
3056 let ctx = &mut *(stream as *mut HcStreamCtx);
3057 *ctx = HcStreamCtx::default();
3058 ctx.compression_level = normalize_hc_level(compressionLevel);
3059 }
3060}
3061
3062#[no_mangle]
3068pub unsafe extern "C" fn LZ4_initStreamHC(buffer: *mut c_void, size: size_t) -> *mut LZ4StreamHC {
3069 if buffer.is_null() {
3070 return ptr::null_mut();
3071 }
3072 if size < LZ4_sizeofStreamStateHC() as usize {
3073 return ptr::null_mut();
3074 }
3075 if !(buffer as usize).is_multiple_of(std::mem::align_of::<HcStreamCtx>()) {
3076 return ptr::null_mut();
3077 }
3078 ptr::write(buffer as *mut HcStreamCtx, HcStreamCtx::default());
3079 buffer as *mut LZ4StreamHC
3080}
3081
3082#[no_mangle]
3085pub unsafe extern "C" fn LZ4_setCompressionLevel(
3086 stream: *mut LZ4StreamHC,
3087 compressionLevel: c_int,
3088) {
3089 if !stream.is_null() {
3090 (*(stream as *mut HcStreamCtx)).compression_level = normalize_hc_level(compressionLevel);
3091 }
3092}
3093
3094#[no_mangle]
3099pub unsafe extern "C" fn LZ4_favorDecompressionSpeed(stream: *mut LZ4StreamHC, favor: c_int) {
3100 if !stream.is_null() {
3101 (*(stream as *mut HcStreamCtx)).favor_dec_speed = favor != 0;
3102 }
3103}
3104
3105#[no_mangle]
3125pub unsafe extern "C" fn LZ4_attach_HC_dictionary(
3126 working_stream: *mut LZ4StreamHC,
3127 dictionary_stream: *const LZ4StreamHC,
3128) {
3129 if working_stream.is_null() {
3130 return;
3131 }
3132 let working = &mut *(working_stream as *mut HcStreamCtx);
3133 working.dictionary.clear();
3134 working.attached_dictionary = false;
3135 if !dictionary_stream.is_null() {
3136 let dictionary = &*(dictionary_stream as *const HcStreamCtx);
3137 working.dictionary.extend_from_slice(&dictionary.dictionary);
3138 working.attached_dictionary = !working.dictionary.is_empty();
3139 }
3140}
3141
3142#[no_mangle]
3148pub unsafe extern "C" fn LZ4_loadDictHC(
3149 stream: *mut LZ4StreamHC,
3150 dictionary: *const c_char,
3151 dictSize: c_int,
3152) -> c_int {
3153 if stream.is_null() || dictSize < 0 || (dictSize > 0 && dictionary.is_null()) {
3154 return 0;
3155 }
3156 let ctx = &mut *(stream as *mut HcStreamCtx);
3157 let keep = cmp::min(dictSize as usize, LZ4_DISTANCE_MAX);
3158 ctx.dictionary.clear();
3159 ctx.attached_dictionary = false;
3160 if keep > 0 {
3161 let dict = slice::from_raw_parts(dictionary as *const u8, dictSize as usize);
3162 ctx.dictionary.extend_from_slice(&dict[dict.len() - keep..]);
3163 }
3164 keep as c_int
3165}
3166
3167#[no_mangle]
3175pub unsafe extern "C" fn LZ4_saveDictHC(
3176 stream: *mut LZ4StreamHC,
3177 safeBuffer: *mut c_char,
3178 maxDictSize: c_int,
3179) -> c_int {
3180 if stream.is_null() || maxDictSize < 0 || (maxDictSize > 0 && safeBuffer.is_null()) {
3181 return 0;
3182 }
3183 let ctx = &mut *(stream as *mut HcStreamCtx);
3184 if maxDictSize < 4 {
3185 ctx.dictionary.clear();
3186 ctx.attached_dictionary = false;
3187 return 0;
3188 }
3189 let keep = cmp::min(
3190 ctx.dictionary.len(),
3191 cmp::min(maxDictSize as usize, LZ4_DISTANCE_MAX),
3192 );
3193 if keep > 0 {
3194 ptr::copy_nonoverlapping(
3195 ctx.dictionary[ctx.dictionary.len() - keep..].as_ptr(),
3196 safeBuffer as *mut u8,
3197 keep,
3198 );
3199 ctx.dictionary = ctx.dictionary[ctx.dictionary.len() - keep..].to_vec();
3200 ctx.attached_dictionary = false;
3201 } else {
3202 ctx.dictionary.clear();
3203 ctx.attached_dictionary = false;
3204 }
3205 keep as c_int
3206}
3207
3208#[no_mangle]
3222pub unsafe extern "C" fn LZ4_compress_HC_continue(
3223 stream: *mut LZ4StreamHC,
3224 src: *const c_char,
3225 dst: *mut c_char,
3226 srcSize: c_int,
3227 maxDstSize: c_int,
3228) -> c_int {
3229 if stream.is_null() {
3230 return 0;
3231 }
3232 let ctx = &mut *(stream as *mut HcStreamCtx);
3233 if srcSize < 0 || maxDstSize <= 0 || src.is_null() || dst.is_null() {
3234 return 0;
3235 }
3236 let src_slice = slice::from_raw_parts(src as *const u8, srcSize as usize);
3237 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, maxDstSize as usize);
3238 compress_hc_stream_block(
3239 ctx,
3240 src_slice,
3241 dst_slice,
3242 ctx.compression_level,
3243 ctx.favor_dec_speed,
3244 )
3245}
3246
3247fn compress_hc_stream_block(
3252 ctx: &mut HcStreamCtx,
3253 src: &[u8],
3254 dst: &mut [u8],
3255 compression_level: c_int,
3256 favor_dec_speed: bool,
3257) -> c_int {
3258 let written = if ctx.dictionary.is_empty() {
3259 compress_block_hc(src, dst, compression_level, favor_dec_speed)
3260 } else {
3261 compress_block_hc_with_dict(
3262 src,
3263 dst,
3264 &ctx.dictionary,
3265 compression_level,
3266 favor_dec_speed,
3267 )
3268 }
3269 .map_or(0, |n| n as c_int);
3270 if written > 0 && !src.is_empty() {
3271 if ctx.attached_dictionary {
3272 ctx.dictionary.clear();
3273 append_hc_dictionary(&mut ctx.dictionary, src);
3274 ctx.attached_dictionary = false;
3275 } else {
3276 append_hc_dictionary(&mut ctx.dictionary, src);
3277 }
3278 }
3279 written
3280}
3281
3282#[no_mangle]
3290pub unsafe extern "C" fn LZ4_compress_HC_continue_destSize(
3291 stream: *mut LZ4StreamHC,
3292 src: *const c_char,
3293 dst: *mut c_char,
3294 srcSizePtr: *mut c_int,
3295 targetDstSize: c_int,
3296) -> c_int {
3297 if stream.is_null() {
3298 return 0;
3299 }
3300 let ctx = &mut *(stream as *mut HcStreamCtx);
3301 let original = if !srcSizePtr.is_null() {
3302 *srcSizePtr
3303 } else {
3304 0
3305 };
3306 if src.is_null()
3307 || dst.is_null()
3308 || srcSizePtr.is_null()
3309 || *srcSizePtr < 0
3310 || targetDstSize <= 0
3311 {
3312 return 0;
3313 }
3314 let src_slice = slice::from_raw_parts(src as *const u8, *srcSizePtr as usize);
3315 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, targetDstSize as usize);
3316 let result = if ctx.dictionary.is_empty() {
3317 compress_hc_dest_size(
3318 src_slice,
3319 dst_slice,
3320 ctx.compression_level,
3321 ctx.favor_dec_speed,
3322 )
3323 } else {
3324 compress_hc_dest_size_with_dict(
3325 src_slice,
3326 dst_slice,
3327 &ctx.dictionary,
3328 ctx.compression_level,
3329 ctx.favor_dec_speed,
3330 )
3331 };
3332 let Some((consumed, written_usize)) = result else {
3333 return 0;
3334 };
3335 *srcSizePtr = consumed as c_int;
3336 let written = written_usize as c_int;
3337 if written > 0 && !src.is_null() && !srcSizePtr.is_null() && *srcSizePtr > 0 {
3338 let consumed = cmp::min(*srcSizePtr, original) as usize;
3339 let src_slice = slice::from_raw_parts(src as *const u8, consumed);
3340 if ctx.attached_dictionary {
3341 ctx.dictionary.clear();
3342 append_hc_dictionary(&mut ctx.dictionary, src_slice);
3343 ctx.attached_dictionary = false;
3344 } else {
3345 append_hc_dictionary(&mut ctx.dictionary, src_slice);
3346 }
3347 }
3348 written
3349}
3350
3351#[no_mangle]
3356pub unsafe extern "C" fn LZ4_compressHC_continue(
3357 stream: *mut LZ4StreamHC,
3358 src: *const c_char,
3359 dst: *mut c_char,
3360 srcSize: c_int,
3361) -> c_int {
3362 LZ4_compress_HC_continue(stream, src, dst, srcSize, LZ4_compressBound(srcSize))
3363}
3364
3365#[no_mangle]
3370pub unsafe extern "C" fn LZ4_compressHC_limitedOutput_continue(
3371 stream: *mut LZ4StreamHC,
3372 src: *const c_char,
3373 dst: *mut c_char,
3374 srcSize: c_int,
3375 maxDstSize: c_int,
3376) -> c_int {
3377 LZ4_compress_HC_continue(stream, src, dst, srcSize, maxDstSize)
3378}
3379
3380#[no_mangle]
3385pub unsafe extern "C" fn LZ4_compressHC2_continue(
3386 stream: *mut c_void,
3387 src: *const c_char,
3388 dst: *mut c_char,
3389 srcSize: c_int,
3390 cLevel: c_int,
3391) -> c_int {
3392 if stream.is_null() {
3393 return 0;
3394 }
3395 if srcSize < 0 || src.is_null() || dst.is_null() {
3396 return 0;
3397 }
3398 let bound = LZ4_compressBound(srcSize);
3399 if bound <= 0 {
3400 return 0;
3401 }
3402 let ctx = &mut *(stream as *mut HcStreamCtx);
3403 let src_slice = slice::from_raw_parts(src as *const u8, srcSize as usize);
3404 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, bound as usize);
3405 compress_hc_stream_block(
3406 ctx,
3407 src_slice,
3408 dst_slice,
3409 normalize_hc_level(cLevel),
3410 ctx.favor_dec_speed,
3411 )
3412}
3413
3414#[no_mangle]
3419pub unsafe extern "C" fn LZ4_compressHC2_limitedOutput_continue(
3420 stream: *mut c_void,
3421 src: *const c_char,
3422 dst: *mut c_char,
3423 srcSize: c_int,
3424 maxDstSize: c_int,
3425 cLevel: c_int,
3426) -> c_int {
3427 if stream.is_null() {
3428 return 0;
3429 }
3430 if srcSize < 0 || maxDstSize <= 0 || src.is_null() || dst.is_null() {
3431 return 0;
3432 }
3433 let ctx = &mut *(stream as *mut HcStreamCtx);
3434 let src_slice = slice::from_raw_parts(src as *const u8, srcSize as usize);
3435 let dst_slice = slice::from_raw_parts_mut(dst as *mut u8, maxDstSize as usize);
3436 compress_hc_stream_block(
3437 ctx,
3438 src_slice,
3439 dst_slice,
3440 normalize_hc_level(cLevel),
3441 ctx.favor_dec_speed,
3442 )
3443}
3444
3445#[no_mangle]
3449pub extern "C" fn LZ4_sizeofStreamStateHC() -> c_int {
3450 LZ4_sizeofStateHC()
3451}
3452
3453#[no_mangle]
3458pub unsafe extern "C" fn LZ4_createHC(_inputBuffer: *const c_char) -> *mut c_void {
3459 LZ4_createStreamHC() as *mut c_void
3460}
3461
3462#[no_mangle]
3464pub unsafe extern "C" fn LZ4_freeHC(stream: *mut c_void) -> c_int {
3465 LZ4_freeStreamHC(stream as *mut LZ4StreamHC)
3466}
3467
3468#[no_mangle]
3474pub unsafe extern "C" fn LZ4_slideInputBufferHC(stream: *mut c_void) -> *mut c_char {
3475 if stream.is_null() {
3476 return ptr::null_mut();
3477 }
3478 let ctx = &mut *(stream as *mut HcStreamCtx);
3479 if ctx.dictionary.is_empty() {
3480 ptr::null_mut()
3481 } else {
3482 let ptr = ctx.dictionary.as_mut_ptr() as *mut c_char;
3483 ctx.dictionary.clear();
3484 ctx.attached_dictionary = false;
3485 ptr
3486 }
3487}
3488
3489#[no_mangle]
3494pub unsafe extern "C" fn LZ4_resetStreamStateHC(
3495 state: *mut c_void,
3496 _inputBuffer: *mut c_char,
3497) -> c_int {
3498 if state.is_null() {
3499 return 1;
3500 }
3501 ptr::write(state as *mut HcStreamCtx, HcStreamCtx::default());
3502 0
3503}
3504
3505#[no_mangle]
3510pub unsafe extern "C" fn LZ4_createStreamDecode() -> *mut LZ4StreamDecode {
3511 Box::into_raw(Box::<DecodeStreamCtx>::default()) as *mut LZ4StreamDecode
3512}
3513
3514#[no_mangle]
3519pub unsafe extern "C" fn LZ4_freeStreamDecode(stream: *mut LZ4StreamDecode) -> c_int {
3520 if !stream.is_null() {
3521 drop(Box::from_raw(stream as *mut DecodeStreamCtx));
3522 }
3523 0
3524}
3525
3526#[no_mangle]
3536pub unsafe extern "C" fn LZ4_setStreamDecode(
3537 stream: *mut LZ4StreamDecode,
3538 dictionary: *const c_char,
3539 dictSize: c_int,
3540) -> c_int {
3541 if stream.is_null() || dictSize < 0 || (dictSize > 0 && dictionary.is_null()) {
3542 return 0;
3543 }
3544 let ctx = &mut *(stream as *mut DecodeStreamCtx);
3545 ctx.dictionary.clear();
3546 if dictSize > 0 {
3547 let dict = slice::from_raw_parts(dictionary as *const u8, dictSize as usize);
3548 let keep = cmp::min(dict.len(), LZ4_DISTANCE_MAX);
3549 ctx.dictionary.extend_from_slice(&dict[dict.len() - keep..]);
3550 }
3551 1
3552}
3553
3554#[no_mangle]
3579pub unsafe extern "C" fn LZ4_decompress_safe_continue(
3580 stream: *mut LZ4StreamDecode,
3581 src: *const c_char,
3582 dst: *mut c_char,
3583 srcSize: c_int,
3584 dstCapacity: c_int,
3585) -> c_int {
3586 if stream.is_null() || srcSize < 0 || dstCapacity < 0 || src.is_null() || dst.is_null() {
3587 return -1;
3588 }
3589 let ctx = &mut *(stream as *mut DecodeStreamCtx);
3590 let src = slice::from_raw_parts(src as *const u8, srcSize as usize);
3591 let dst = slice::from_raw_parts_mut(dst as *mut u8, dstCapacity as usize);
3592 match decompress_block_with_dict(src, dst, &ctx.dictionary) {
3593 Some(n) => {
3594 append_hc_dictionary(&mut ctx.dictionary, &dst[..n]);
3595 n as c_int
3596 }
3597 None => -1,
3598 }
3599}
3600
3601fn normalize_acceleration(acceleration: c_int) -> usize {
3603 acceleration.clamp(1, 65_537) as usize
3604}
3605
3606trait FastHashTable {
3607 fn get(&self, h: usize) -> usize;
3608 fn set(&mut self, h: usize, pos: usize);
3609}
3610
3611impl FastHashTable for [u32] {
3612 #[inline]
3613 fn get(&self, h: usize) -> usize {
3614 self[h] as usize
3615 }
3616
3617 #[inline]
3618 fn set(&mut self, h: usize, pos: usize) {
3619 self[h] = pos as u32;
3620 }
3621}
3622
3623impl FastHashTable for [u16] {
3624 #[inline]
3625 fn get(&self, h: usize) -> usize {
3626 self[h] as usize
3627 }
3628
3629 #[inline]
3630 fn set(&mut self, h: usize, pos: usize) {
3631 self[h] = pos as u16;
3632 }
3633}
3634
3635fn compress_block(src: &[u8], dst: &mut [u8], acceleration: usize) -> Option<usize> {
3642 if src.len() < LZ4_64K_LIMIT {
3643 let mut table = vec![0u16; 1 << LZ4_HASH_BITS_U16];
3644 compress_block_with_table::<true, _>(src, dst, acceleration, &mut table[..])
3645 } else {
3646 let mut table = vec![0u32; 1 << LZ4_HASH_BITS];
3647 compress_block_with_table::<false, _>(src, dst, acceleration, &mut table[..])
3648 }
3649}
3650
3651fn compress_block_ext_state(
3652 state: *mut c_void,
3653 src: &[u8],
3654 dst: &mut [u8],
3655 acceleration: usize,
3656) -> Option<usize> {
3657 if src.len() < LZ4_64K_LIMIT {
3658 let table = unsafe { slice::from_raw_parts_mut(state as *mut u16, 1 << LZ4_HASH_BITS_U16) };
3659 table.fill(0);
3660 compress_block_with_table::<true, _>(src, dst, acceleration, table)
3661 } else {
3662 let table = unsafe { slice::from_raw_parts_mut(state as *mut u32, 1 << LZ4_HASH_BITS) };
3663 table.fill(0);
3664 compress_block_with_table::<false, _>(src, dst, acceleration, table)
3665 }
3666}
3667
3668fn compress_block_with_table<const BY_U16: bool, T: FastHashTable + ?Sized>(
3669 src: &[u8],
3670 dst: &mut [u8],
3671 acceleration: usize,
3672 table: &mut T,
3673) -> Option<usize> {
3674 if src.is_empty() {
3675 return emit_last_literals(src, dst, 0, 0);
3676 }
3677 if src.len() < MFLIMIT + 1 {
3678 return emit_last_literals(src, dst, 0, 0);
3679 }
3680 let mut ip = 0usize;
3681 let mut anchor = 0usize;
3682 let mut op = 0usize;
3683 let mflimit_plus_one = src.len() - MFLIMIT + 1;
3684 let match_limit = src.len() - LAST_LITERALS;
3685
3686 table.set(hash_fast_const::<BY_U16>(src, ip), ip);
3687 ip += 1;
3688 let mut forward_h = hash_fast_const::<BY_U16>(src, ip);
3689
3690 let src_ptr = src.as_ptr();
3691
3692 loop {
3693 let mut forward_ip = ip;
3694 let mut step = 1usize;
3695 let mut search_match_nb = acceleration << 6;
3696 let mut ref_pos;
3697
3698 loop {
3699 let h = forward_h;
3700 ip = forward_ip;
3701 forward_ip += step;
3702 step = search_match_nb >> 6;
3703 search_match_nb += 1;
3704
3705 if forward_ip > mflimit_plus_one {
3706 return emit_last_literals(src, dst, anchor, op);
3707 }
3708
3709 ref_pos = table.get(h);
3710 forward_h = hash_fast_const::<BY_U16>(src, forward_ip);
3711 table.set(h, ip);
3712
3713 if ip > ref_pos
3716 && ip - ref_pos <= LZ4_DISTANCE_MAX
3717 && unsafe { read_u32_ptr(src_ptr.add(ref_pos)) == read_u32_ptr(src_ptr.add(ip)) }
3718 {
3719 break;
3720 }
3721 }
3722
3723 while ip > anchor && ref_pos > 0 && src[ip - 1] == src[ref_pos - 1] {
3724 ip -= 1;
3725 ref_pos -= 1;
3726 }
3727
3728 loop {
3729 let match_len =
3730 MINMATCH + count_match(src, ip + MINMATCH, ref_pos + MINMATCH, match_limit);
3731 op = encode_sequence(src, dst, anchor, ip, match_len, ip - ref_pos, op)?;
3732 ip += match_len;
3733 anchor = ip;
3734
3735 if ip >= mflimit_plus_one {
3736 return emit_last_literals(src, dst, anchor, op);
3737 }
3738
3739 table.set(hash_fast_const::<BY_U16>(src, ip - 2), ip - 2);
3740 let h = hash_fast_const::<BY_U16>(src, ip);
3741 ref_pos = table.get(h);
3742 table.set(h, ip);
3743 if ip > ref_pos
3744 && ip - ref_pos <= LZ4_DISTANCE_MAX
3745 && unsafe { read_u32_ptr(src_ptr.add(ref_pos)) == read_u32_ptr(src_ptr.add(ip)) }
3746 {
3747 continue;
3748 }
3749
3750 ip += 1;
3751 forward_h = hash_fast_const::<BY_U16>(src, ip);
3752 break;
3753 }
3754 }
3755}
3756
3757fn compress_dest_size(src: &[u8], dst: &mut [u8], acceleration: usize) -> Option<(usize, usize)> {
3764 if dst.is_empty() {
3769 return None;
3770 }
3771 let olimit = dst.len();
3772
3773 if src.len() < MFLIMIT + 1 {
3774 return Some(emit_truncated_last_literals(src, dst, 0, 0, olimit));
3775 }
3776
3777 if src.len() < LZ4_64K_LIMIT {
3778 compress_dest_size_inner::<true>(src, dst, acceleration, olimit)
3779 } else {
3780 compress_dest_size_inner::<false>(src, dst, acceleration, olimit)
3781 }
3782}
3783
3784fn compress_dest_size_inner<const BY_U16: bool>(
3785 src: &[u8],
3786 dst: &mut [u8],
3787 acceleration: usize,
3788 olimit: usize,
3789) -> Option<(usize, usize)> {
3790 let tail = MFLIMIT - MINMATCH; let last_lit = LAST_LITERALS; let hash_bits = if BY_U16 {
3796 LZ4_HASH_BITS_U16
3797 } else {
3798 LZ4_HASH_BITS
3799 };
3800 let mut table = vec![0u32; 1 << hash_bits];
3802 let mut ip = 0usize;
3803 let mut anchor = 0usize;
3804 let mut op = 0usize;
3805 let mflimit_plus_one = src.len() - MFLIMIT + 1;
3806 let match_limit = src.len() - LAST_LITERALS;
3807
3808 table[hash_fast_const::<BY_U16>(src, ip)] = ip as u32;
3809 ip += 1;
3810 let mut forward_h = hash_fast_const::<BY_U16>(src, ip);
3811
3812 'outer: loop {
3813 let mut forward_ip = ip;
3814 let mut step = 1usize;
3815 let mut search_match_nb = acceleration << 6;
3816 let mut ref_pos;
3817
3818 loop {
3819 let h = forward_h;
3820 ip = forward_ip;
3821 forward_ip += step;
3822 step = search_match_nb >> 6;
3823 search_match_nb += 1;
3824
3825 if forward_ip > mflimit_plus_one {
3826 break 'outer;
3827 }
3828
3829 ref_pos = table[h] as usize;
3830 forward_h = hash_fast_const::<BY_U16>(src, forward_ip);
3831 table[h] = ip as u32;
3832
3833 if ip > ref_pos
3834 && ip - ref_pos <= LZ4_DISTANCE_MAX
3835 && unsafe {
3836 read_u32_ptr(src.as_ptr().add(ref_pos)) == read_u32_ptr(src.as_ptr().add(ip))
3837 }
3838 {
3839 break;
3840 }
3841 }
3842
3843 while ip > anchor && ref_pos > 0 && src[ip - 1] == src[ref_pos - 1] {
3844 ip -= 1;
3845 ref_pos -= 1;
3846 }
3847
3848 loop {
3849 let lit_len = ip - anchor;
3850
3851 if op + (lit_len + 240) / 255 + lit_len + 2 + 1 + tail > olimit {
3854 break 'outer;
3855 }
3856
3857 let token_pos = op;
3858 op += 1;
3859 if lit_len >= 15 {
3860 let mut extra = lit_len - 15;
3861 while extra >= 255 {
3862 dst[op] = 255;
3863 op += 1;
3864 extra -= 255;
3865 }
3866 dst[op] = extra as u8;
3867 op += 1;
3868 }
3869 dst[op..op + lit_len].copy_from_slice(&src[anchor..ip]);
3870 op += lit_len;
3871
3872 if op + 2 + 1 + tail > olimit {
3875 op = token_pos;
3876 break 'outer;
3877 }
3878
3879 let offset = ip - ref_pos;
3880 dst[op..op + 2].copy_from_slice(&(offset as u16).to_le_bytes());
3881 op += 2;
3882
3883 let mut match_code = count_match(src, ip + MINMATCH, ref_pos + MINMATCH, match_limit);
3884 ip += match_code + MINMATCH;
3885
3886 if op + (1 + last_lit) + (match_code + 240) / 255 > olimit {
3891 let avail = olimit.saturating_sub(op + 1 + last_lit);
3892 let new_match_code = 14 + avail.saturating_mul(255);
3893 if new_match_code < match_code {
3894 ip -= match_code - new_match_code;
3895 match_code = new_match_code;
3896 } else {
3897 }
3900 }
3901
3902 let token_lit_bits = (cmp::min(lit_len, 15) as u8) << 4;
3903 if match_code >= 15 {
3904 dst[token_pos] = token_lit_bits | 0x0F;
3905 let mut ml = match_code - 15;
3906 while ml >= 255 {
3907 dst[op] = 255;
3908 op += 1;
3909 ml -= 255;
3910 }
3911 dst[op] = ml as u8;
3912 op += 1;
3913 } else {
3914 dst[token_pos] = token_lit_bits | (match_code as u8);
3915 }
3916
3917 anchor = ip;
3918
3919 if ip >= mflimit_plus_one {
3920 break 'outer;
3921 }
3922
3923 table[hash_fast_const::<BY_U16>(src, ip - 2)] = (ip - 2) as u32;
3924 let h = hash_fast_const::<BY_U16>(src, ip);
3925 let next_ref = table[h] as usize;
3926 table[h] = ip as u32;
3927 if ip > next_ref
3928 && ip - next_ref <= LZ4_DISTANCE_MAX
3929 && unsafe {
3930 read_u32_ptr(src.as_ptr().add(next_ref)) == read_u32_ptr(src.as_ptr().add(ip))
3931 }
3932 {
3933 ref_pos = next_ref;
3934 continue;
3935 }
3936
3937 ip += 1;
3938 forward_h = hash_fast_const::<BY_U16>(src, ip);
3939 break;
3940 }
3941 }
3942
3943 Some(emit_truncated_last_literals(src, dst, anchor, op, olimit))
3944}
3945
3946fn emit_truncated_last_literals(
3951 src: &[u8],
3952 dst: &mut [u8],
3953 anchor: usize,
3954 mut op: usize,
3955 olimit: usize,
3956) -> (usize, usize) {
3957 let mut last_run = src.len() - anchor;
3960 let needed = op + last_run + 1 + (last_run + 255 - 15) / 255;
3961 if needed > olimit {
3962 let avail = olimit.saturating_sub(op + 1);
3966 let extension_bytes = (avail + 256 - 15) / 256;
3967 last_run = avail.saturating_sub(extension_bytes);
3968 }
3969 if op >= olimit {
3970 return (anchor, op);
3972 }
3973 if last_run >= 15 {
3974 dst[op] = 0xF0; op += 1;
3976 let mut accum = last_run - 15;
3977 while accum >= 255 {
3978 dst[op] = 255;
3979 op += 1;
3980 accum -= 255;
3981 }
3982 dst[op] = accum as u8;
3983 op += 1;
3984 } else {
3985 dst[op] = (last_run as u8) << 4;
3986 op += 1;
3987 }
3988 dst[op..op + last_run].copy_from_slice(&src[anchor..anchor + last_run]);
3989 op += last_run;
3990 (anchor + last_run, op)
3991}
3992
3993fn compress_block_with_dict(
4000 src: &[u8],
4001 dst: &mut [u8],
4002 dict: &[u8],
4003 acceleration: usize,
4004) -> Option<usize> {
4005 if src.is_empty() {
4006 return emit_last_literals(src, dst, 0, 0);
4007 }
4008 if src.len() < MFLIMIT + 1 {
4009 return emit_last_literals(src, dst, 0, 0);
4010 }
4011 let dict_keep = cmp::min(dict.len(), LZ4_DISTANCE_MAX);
4012 let dict = &dict[dict.len() - dict_keep..];
4013 if dict.is_empty() {
4014 return compress_block(src, dst, acceleration);
4015 }
4016
4017 let mut full = Vec::with_capacity(dict.len() + src.len());
4018 full.extend_from_slice(dict);
4019 full.extend_from_slice(src);
4020 let base = dict.len();
4021 let mut table = vec![0u32; 1 << LZ4_HASH_BITS];
4023 let seed_end = full.len().saturating_sub(MINMATCH - 1);
4024 for pos in 0..cmp::min(base, seed_end) {
4025 table[hash_fast(&full, pos, false)] = pos as u32;
4026 }
4027
4028 let mut ip = base;
4029 let mut anchor = base;
4030 let mut op = 0usize;
4031 let mflimit_plus_one = base + src.len() - MFLIMIT + 1;
4032 let match_limit = base + src.len() - LAST_LITERALS;
4033
4034 table[hash_fast(&full, ip, false)] = ip as u32;
4035 ip += 1;
4036 let mut forward_h = hash_fast(&full, ip, false);
4037
4038 let full_ptr = full.as_ptr();
4039
4040 loop {
4041 let mut forward_ip = ip;
4042 let mut step = 1usize;
4043 let mut search_match_nb = acceleration << 6;
4044 let mut ref_pos;
4045
4046 loop {
4047 let h = forward_h;
4048 ip = forward_ip;
4049 forward_ip += step;
4050 step = search_match_nb >> 6;
4051 search_match_nb += 1;
4052
4053 if forward_ip > mflimit_plus_one {
4054 return emit_last_literals_with_base(&full, dst, base, anchor, op);
4055 }
4056
4057 ref_pos = table[h] as usize;
4058 forward_h = hash_fast(&full, forward_ip, false);
4059 table[h] = ip as u32;
4060
4061 if ip > ref_pos
4062 && ip - ref_pos <= LZ4_DISTANCE_MAX
4063 && unsafe { read_u32_ptr(full_ptr.add(ref_pos)) == read_u32_ptr(full_ptr.add(ip)) }
4064 {
4065 break;
4066 }
4067 }
4068
4069 while ip > anchor && ref_pos > 0 && full[ip - 1] == full[ref_pos - 1] {
4070 ip -= 1;
4071 ref_pos -= 1;
4072 }
4073
4074 loop {
4075 let match_len =
4076 MINMATCH + count_match(&full, ip + MINMATCH, ref_pos + MINMATCH, match_limit);
4077 op = encode_sequence(&full, dst, anchor, ip, match_len, ip - ref_pos, op)?;
4078 ip += match_len;
4079 anchor = ip;
4080
4081 if ip >= mflimit_plus_one {
4082 return emit_last_literals_with_base(&full, dst, base, anchor, op);
4083 }
4084
4085 table[hash_fast(&full, ip - 2, false)] = (ip - 2) as u32;
4086 let h = hash_fast(&full, ip, false);
4087 ref_pos = table[h] as usize;
4088 table[h] = ip as u32;
4089 if ip > ref_pos
4090 && ip - ref_pos <= LZ4_DISTANCE_MAX
4091 && unsafe { read_u32_ptr(full_ptr.add(ref_pos)) == read_u32_ptr(full_ptr.add(ip)) }
4092 {
4093 continue;
4094 }
4095
4096 ip += 1;
4097 forward_h = hash_fast(&full, ip, false);
4098 break;
4099 }
4100 }
4101}
4102
4103#[derive(Clone, Copy, Debug)]
4104struct HcMatch {
4105 start: usize,
4106 len: usize,
4107 off: usize,
4108}
4109
4110#[derive(Clone, Copy, Debug)]
4111#[repr(C)]
4112struct HcOptimal {
4113 price: u32,
4126 off: u32,
4127 mlen: u32,
4128 litlen: u32,
4129}
4130
4131const HC_OFFSET: u32 = (LZ4_DISTANCE_MAX + 1) as u32;
4139
4140const HC_CHAIN_SIZE: usize = LZ4_DISTANCE_MAX + 1;
4143
4144#[derive(Debug)]
4145struct HcTables {
4146 hash: Box<[u32; LZ4HC_HASH_SIZE]>,
4155 chain: Box<[u16; HC_CHAIN_SIZE]>,
4160 next_to_update: usize,
4161 base: usize,
4169}
4170
4171impl HcTables {
4172 fn with_base(_src_len: usize, base: usize) -> Self {
4173 Self {
4174 hash: vec![0u32; LZ4HC_HASH_SIZE]
4175 .into_boxed_slice()
4176 .try_into()
4177 .unwrap(),
4178 chain: vec![LZ4_DISTANCE_MAX as u16; HC_CHAIN_SIZE]
4179 .into_boxed_slice()
4180 .try_into()
4181 .unwrap(),
4182 next_to_update: 0,
4183 base,
4184 }
4185 }
4186
4187 #[inline(always)]
4190 fn to_log(pos: usize) -> u32 {
4191 (pos as u32).wrapping_add(HC_OFFSET)
4192 }
4193
4194 #[inline(always)]
4198 fn to_phys(log: u32) -> usize {
4199 log.wrapping_sub(HC_OFFSET) as usize
4200 }
4201
4202 #[inline]
4203 fn insert_until(&mut self, src: &[u8], target: usize) {
4204 let end = cmp::min(target, src.len().saturating_sub(MINMATCH - 1));
4205 while self.next_to_update < end {
4206 let pos = self.next_to_update;
4207 let h = hash4_hc(src, pos);
4208 let log_pos = Self::to_log(pos);
4209 let prev_log = self.hash[h];
4210 let delta = cmp::min(log_pos - prev_log, HC_OFFSET - 1) as u16;
4215 self.chain[pos & LZ4_DISTANCE_MAX] = delta;
4216 self.hash[h] = log_pos;
4217 self.next_to_update += 1;
4218 }
4219 }
4220
4221 #[inline]
4225 fn previous(&self, pos: usize) -> usize {
4226 let log_pos = Self::to_log(pos);
4227 let delta = self.chain[pos & LZ4_DISTANCE_MAX] as u32;
4228 let prev_log = log_pos.wrapping_sub(delta);
4229 if prev_log < HC_OFFSET {
4230 usize::MAX
4231 } else {
4232 Self::to_phys(prev_log)
4233 }
4234 }
4235}
4236
4237struct MidTables {
4238 hash4: Vec<usize>,
4239 hash8: Vec<usize>,
4240}
4241
4242impl MidTables {
4243 fn new() -> Self {
4244 Self {
4245 hash4: vec![usize::MAX; LZ4MID_HASH_SIZE],
4246 hash8: vec![usize::MAX; LZ4MID_HASH_SIZE],
4247 }
4248 }
4249
4250 fn add4(&mut self, src: &[u8], pos: usize) {
4251 self.add4_index(src, pos, pos);
4252 }
4253
4254 fn add4_index(&mut self, src: &[u8], pos: usize, index: usize) {
4255 if pos + MINMATCH <= src.len() {
4256 self.hash4[hash4_mid(src, pos)] = index;
4257 }
4258 }
4259
4260 fn add8(&mut self, src: &[u8], pos: usize) {
4261 self.add8_index(src, pos, pos);
4262 }
4263
4264 fn add8_index(&mut self, src: &[u8], pos: usize, index: usize) {
4265 if pos + LZ4MID_HASHSIZE <= src.len() {
4266 self.hash8[hash8_mid(src, pos)] = index;
4267 }
4268 }
4269}
4270
4271fn fill_lz4mid_dict_table(table: &mut MidTables, full: &[u8], base: usize) {
4278 if base <= LZ4MID_HASHSIZE {
4279 return;
4280 }
4281 let target = base - LZ4MID_HASHSIZE;
4282 let mut idx = 0usize;
4283 while idx < target {
4284 table.add4_index(full, idx, idx);
4285 table.add8_index(full, idx + 1, idx + 1);
4286 idx += 3;
4287 }
4288
4289 idx = if base > 32 * 1024 + LZ4MID_HASHSIZE {
4290 target - 32 * 1024
4291 } else {
4292 0
4293 };
4294 while idx < target {
4295 table.add8_index(full, idx, idx);
4296 idx += 1;
4297 }
4298}
4299
4300fn compress_block_lz4mid(src: &[u8], dst: &mut [u8]) -> Option<usize> {
4303 compress_block_lz4mid_with_base(src, dst, 0)
4304}
4305
4306fn compress_block_lz4mid_with_base(full: &[u8], dst: &mut [u8], base: usize) -> Option<usize> {
4312 let src_len = full.len().checked_sub(base)?;
4313 if src_len == 0 {
4314 return emit_last_literals_with_base(full, dst, base, base, 0);
4315 }
4316 if src_len < MFLIMIT + 1 {
4317 return emit_last_literals_with_base(full, dst, base, base, 0);
4318 }
4319
4320 let mut table = MidTables::new();
4321 if base > 0 {
4322 fill_lz4mid_dict_table(&mut table, full, base);
4323 }
4324 let mut anchor = base;
4325 let mut ip = base;
4326 let mut op = 0usize;
4327 let mflimit = full.len() - MFLIMIT;
4328 let match_limit = full.len() - LAST_LITERALS;
4329
4330 while ip <= mflimit {
4331 let search_ip = ip;
4332 let mut match_len = 0usize;
4333 let mut match_distance = 0usize;
4334
4335 let h8 = hash8_mid(full, ip);
4336 let pos8 = table.hash8[h8];
4337 table.hash8[h8] = ip;
4338 if pos8 != usize::MAX
4339 && ip - pos8 <= LZ4_DISTANCE_MAX
4340 && unsafe {
4341 read_u32_ptr(full.as_ptr().add(pos8)) == read_u32_ptr(full.as_ptr().add(ip))
4342 }
4343 {
4344 match_len = count_match(full, ip, pos8, match_limit);
4345 if match_len >= MINMATCH {
4346 match_distance = ip - pos8;
4347 }
4348 }
4349
4350 if match_len < MINMATCH {
4351 let h4 = hash4_mid(full, ip);
4352 let pos4 = table.hash4[h4];
4353 table.hash4[h4] = ip;
4354 if pos4 != usize::MAX
4355 && ip - pos4 <= LZ4_DISTANCE_MAX
4356 && unsafe {
4357 read_u32_ptr(full.as_ptr().add(pos4)) == read_u32_ptr(full.as_ptr().add(ip))
4358 }
4359 {
4360 match_len = count_match(full, ip, pos4, match_limit);
4361 if match_len >= MINMATCH {
4362 match_distance = ip - pos4;
4363
4364 let ip1 = ip + 1;
4365 if ip < mflimit {
4366 let h8_next = hash8_mid(full, ip1);
4367 let pos8_next = table.hash8[h8_next];
4368 let dist2 = ip1.saturating_sub(pos8_next);
4369 if pos8_next != usize::MAX
4370 && dist2 <= LZ4_DISTANCE_MAX
4371 && unsafe {
4372 read_u32_ptr(full.as_ptr().add(pos8_next))
4373 == read_u32_ptr(full.as_ptr().add(ip1))
4374 }
4375 {
4376 let len2 = count_match(full, ip1, pos8_next, match_limit);
4377 if len2 > match_len {
4378 table.hash8[h8_next] = ip1;
4379 ip = ip1;
4380 match_len = len2;
4381 match_distance = dist2;
4382 }
4383 }
4384 }
4385 }
4386 }
4387 }
4388
4389 if match_len < MINMATCH {
4390 ip += 1 + ((ip - anchor) >> 9);
4391 continue;
4392 }
4393
4394 while ip > anchor && ip > match_distance && full[ip - 1] == full[ip - match_distance - 1] {
4395 ip -= 1;
4396 match_len += 1;
4397 }
4398
4399 table.add8_index(full, ip + 1, search_ip + 1);
4400 table.add8_index(full, ip + 2, search_ip + 2);
4401 table.add4_index(full, ip + 1, search_ip + 1);
4402
4403 op = encode_sequence(full, dst, anchor, ip, match_len, match_distance, op)?;
4404 ip += match_len;
4405 anchor = ip;
4406
4407 if ip >= 2 && ip - 2 < full.len().saturating_sub(LZ4MID_HASHSIZE) {
4408 if ip >= 5 {
4409 table.add8(full, ip - 5);
4410 }
4411 if ip >= 3 {
4412 table.add8(full, ip - 3);
4413 }
4414 table.add8(full, ip - 2);
4415 table.add4(full, ip - 2);
4416 table.add4(full, ip - 1);
4417 }
4418 }
4419
4420 emit_last_literals_with_base(full, dst, base, anchor, op)
4421}
4422
4423fn compress_block_hc(
4429 src: &[u8],
4430 dst: &mut [u8],
4431 compression_level: c_int,
4432 favor_dec_speed: bool,
4433) -> Option<usize> {
4434 if src.is_empty() || src.len() < MFLIMIT + 1 {
4435 return emit_last_literals(src, dst, 0, 0);
4436 }
4437 let level = normalize_hc_level(compression_level);
4438 if level <= 2 {
4439 return compress_block_lz4mid(src, dst);
4440 }
4441 if level >= 10 {
4442 return compress_block_hc_optimal(src, dst, 0, level, favor_dec_speed);
4443 }
4444 compress_block_hc_hashchain(src, dst, 0, level)
4445}
4446
4447fn compress_block_hc_hashchain(
4452 full: &[u8],
4453 dst: &mut [u8],
4454 base: usize,
4455 level: c_int,
4456) -> Option<usize> {
4457 match level {
4458 3 => compress_block_hc_hashchain_impl::<4, false>(full, dst, base),
4459 4 => compress_block_hc_hashchain_impl::<8, false>(full, dst, base),
4460 5 => compress_block_hc_hashchain_impl::<16, false>(full, dst, base),
4461 6 => compress_block_hc_hashchain_impl::<32, false>(full, dst, base),
4462 7 => compress_block_hc_hashchain_impl::<64, false>(full, dst, base),
4463 8 => compress_block_hc_hashchain_impl::<128, false>(full, dst, base),
4464 _ => compress_block_hc_hashchain_impl::<256, true>(full, dst, base),
4465 }
4466}
4467
4468fn compress_block_hc_hashchain_impl<const ATTEMPTS: usize, const PATTERN_ANALYSIS: bool>(
4469 full: &[u8],
4470 dst: &mut [u8],
4471 base: usize,
4472) -> Option<usize> {
4473 let mut table = HcTables::with_base(full.len(), base);
4474 if base > 0 {
4475 table.insert_until(full, base);
4476 }
4477
4478 let mut anchor = base;
4479 let mut op = 0usize;
4480 let mflimit = full.len() - MFLIMIT;
4481 let match_limit = full.len() - LAST_LITERALS;
4482 let nomatch = HcMatch {
4483 start: 0,
4484 len: 0,
4485 off: 0,
4486 };
4487 let mut ip = base;
4488
4489 while ip <= mflimit {
4490 let mut m1 = find_hc_match::<PATTERN_ANALYSIS>(full, &mut table, ip, match_limit, ATTEMPTS);
4491 if m1.len < MINMATCH {
4492 ip += 1;
4493 continue;
4494 }
4495 let mut start0 = ip;
4496 let mut m0 = m1;
4497
4498 'search2: loop {
4499 let mut start2;
4500 let mut m2;
4501 if ip + m1.len <= mflimit {
4502 start2 = ip + m1.len - 2;
4503 m2 = find_hc_wider_match::<PATTERN_ANALYSIS, false, false>(
4504 full,
4505 &mut table,
4506 start2,
4507 ip,
4508 match_limit,
4509 m1.len,
4510 ATTEMPTS,
4511 );
4512 start2 = m2.start;
4513 } else {
4514 m2 = nomatch;
4515 start2 = 0;
4516 }
4517
4518 if m2.len <= m1.len {
4519 op = encode_sequence(full, dst, anchor, ip, m1.len, m1.off, op)?;
4520 ip += m1.len;
4521 anchor = ip;
4522 break 'search2;
4523 }
4524
4525 if start0 < ip && start2 < ip + m0.len {
4526 ip = start0;
4527 m1 = m0;
4528 }
4529
4530 if start2 - ip < 3 {
4531 ip = start2;
4532 m1 = m2;
4533 continue 'search2;
4534 }
4535
4536 'search3: loop {
4537 if start2 - ip < OPTIMAL_ML {
4538 let mut new_len = cmp::min(m1.len, OPTIMAL_ML);
4539 if ip + new_len > start2 + m2.len - MINMATCH {
4540 new_len = start2 - ip + m2.len - MINMATCH;
4541 }
4542 let correction = new_len.saturating_sub(start2 - ip);
4543 if correction > 0 {
4544 start2 += correction;
4545 m2.start = start2;
4546 m2.len -= correction;
4547 }
4548 }
4549
4550 let mut start3;
4551 let m3;
4552 if start2 + m2.len <= mflimit {
4553 start3 = start2 + m2.len - 3;
4554 m3 = find_hc_wider_match::<PATTERN_ANALYSIS, false, false>(
4555 full,
4556 &mut table,
4557 start3,
4558 start2,
4559 match_limit,
4560 m2.len,
4561 ATTEMPTS,
4562 );
4563 start3 = m3.start;
4564 } else {
4565 m3 = nomatch;
4566 start3 = 0;
4567 }
4568
4569 if m3.len <= m2.len {
4570 if start2 < ip + m1.len {
4571 m1.len = start2 - ip;
4572 }
4573 op = encode_sequence(full, dst, anchor, ip, m1.len, m1.off, op)?;
4574 ip += m1.len;
4575 anchor = ip;
4576
4577 ip = start2;
4578 op = encode_sequence(full, dst, anchor, ip, m2.len, m2.off, op)?;
4579 ip += m2.len;
4580 anchor = ip;
4581 break 'search2;
4582 }
4583
4584 if start3 < ip + m1.len + 3 {
4585 if start3 >= ip + m1.len {
4586 if start2 < ip + m1.len {
4587 let correction = ip + m1.len - start2;
4588 start2 += correction;
4589 m2.start = start2;
4590 m2.len = m2.len.saturating_sub(correction);
4591 if m2.len < MINMATCH {
4592 start2 = start3;
4593 m2 = m3;
4594 }
4595 }
4596 op = encode_sequence(full, dst, anchor, ip, m1.len, m1.off, op)?;
4597 ip += m1.len;
4598 anchor = ip;
4599
4600 ip = start3;
4601 m1 = m3;
4602 start0 = start2;
4603 m0 = m2;
4604 continue 'search2;
4605 }
4606
4607 start2 = start3;
4608 m2 = m3;
4609 continue 'search3;
4610 }
4611
4612 if start2 < ip + m1.len {
4613 if start2 - ip < OPTIMAL_ML {
4614 if m1.len > OPTIMAL_ML {
4615 m1.len = OPTIMAL_ML;
4616 }
4617 if ip + m1.len > start2 + m2.len - MINMATCH {
4618 m1.len = start2 - ip + m2.len - MINMATCH;
4619 }
4620 let correction = m1.len.saturating_sub(start2 - ip);
4621 if correction > 0 {
4622 start2 += correction;
4623 m2.start = start2;
4624 m2.len -= correction;
4625 }
4626 } else {
4627 m1.len = start2 - ip;
4628 }
4629 }
4630
4631 op = encode_sequence(full, dst, anchor, ip, m1.len, m1.off, op)?;
4632 ip += m1.len;
4633 anchor = ip;
4634
4635 ip = start2;
4636 m1 = m2;
4637 start2 = start3;
4638 m2 = m3;
4639 continue 'search3;
4640 }
4641 }
4642 }
4643
4644 emit_last_literals_with_base(full, dst, base, anchor, op)
4645}
4646
4647fn compress_block_hc_with_dict(
4653 src: &[u8],
4654 dst: &mut [u8],
4655 dict: &[u8],
4656 compression_level: c_int,
4657 favor_dec_speed: bool,
4658) -> Option<usize> {
4659 if src.is_empty() {
4660 return emit_last_literals(src, dst, 0, 0);
4661 }
4662 let dict_keep = cmp::min(dict.len(), LZ4_DISTANCE_MAX);
4663 let dict = &dict[dict.len() - dict_keep..];
4664 if dict.is_empty() || src.len() < MFLIMIT + 1 {
4665 return compress_block_hc(src, dst, compression_level, favor_dec_speed);
4666 }
4667
4668 let mut full = Vec::with_capacity(dict.len() + src.len());
4669 full.extend_from_slice(dict);
4670 full.extend_from_slice(src);
4671 let base = dict.len();
4672 let level = normalize_hc_level(compression_level);
4673 if level <= 2 {
4674 return compress_block_lz4mid_with_base(&full, dst, base);
4675 }
4676 if level >= 10 {
4677 return compress_block_hc_optimal(&full, dst, base, level, favor_dec_speed);
4678 }
4679 compress_block_hc_hashchain(&full, dst, base, level)
4680}
4681
4682fn compress_block_hc_optimal(
4691 full: &[u8],
4692 dst: &mut [u8],
4693 base: usize,
4694 compression_level: c_int,
4695 favor_dec_speed: bool,
4696) -> Option<usize> {
4697 match (normalize_hc_level(compression_level), favor_dec_speed) {
4698 (10, false) => compress_block_hc_optimal_impl::<96, 64, false, false>(full, dst, base),
4699 (10, true) => compress_block_hc_optimal_impl::<96, 64, false, true>(full, dst, base),
4700 (11, false) => compress_block_hc_optimal_impl::<512, 128, false, false>(full, dst, base),
4701 (11, true) => compress_block_hc_optimal_impl::<512, 128, false, true>(full, dst, base),
4702 (_, false) => compress_block_hc_optimal_impl::<16_384, { LZ4_OPT_NUM - 1 }, true, false>(
4703 full, dst, base,
4704 ),
4705 (_, true) => compress_block_hc_optimal_impl::<16_384, { LZ4_OPT_NUM - 1 }, true, true>(
4706 full, dst, base,
4707 ),
4708 }
4709}
4710
4711fn compress_block_hc_optimal_impl<
4712 const ATTEMPTS: usize,
4713 const SUFFICIENT_LEN: usize,
4714 const FULL_UPDATE: bool,
4715 const FAVOR_DEC_SPEED: bool,
4716>(
4717 full: &[u8],
4718 dst: &mut [u8],
4719 base: usize,
4720) -> Option<usize> {
4721 let src_len = full.len().checked_sub(base)?;
4722 if src_len == 0 {
4723 return emit_last_literals_with_base(full, dst, base, base, 0);
4724 }
4725 if src_len < MFLIMIT + 1 {
4726 return emit_last_literals_with_base(full, dst, base, base, 0);
4727 }
4728
4729 let mut table = HcTables::with_base(full.len(), base);
4730 if base > 0 {
4731 table.insert_until(full, base);
4732 }
4733
4734 let mut opt: Box<[HcOptimal; LZ4_OPT_NUM + 3]> = vec![
4741 HcOptimal {
4742 price: u32::MAX / 4,
4743 off: 0,
4744 mlen: 1,
4745 litlen: 0,
4746 };
4747 LZ4_OPT_NUM + 3
4748 ]
4749 .into_boxed_slice()
4750 .try_into()
4751 .unwrap();
4752
4753 let mut ip = base;
4754 let mut anchor = base;
4755 let mut op = 0usize;
4756 let iend = full.len();
4757 let mflimit = iend - MFLIMIT;
4758 let match_limit = iend - LAST_LITERALS;
4759 let mut prev_max_touched: usize = LZ4_OPT_NUM + 2;
4764
4765 while ip <= mflimit {
4766 let llen = ip - anchor;
4767 let first_match = find_hc_longer_match::<FAVOR_DEC_SPEED>(
4768 full,
4769 &mut table,
4770 ip,
4771 match_limit,
4772 MINMATCH - 1,
4773 ATTEMPTS,
4774 );
4775 if first_match.len == 0 {
4776 ip += 1;
4777 continue;
4778 }
4779
4780 if first_match.len > SUFFICIENT_LEN {
4781 op = encode_sequence(full, dst, anchor, ip, first_match.len, first_match.off, op)?;
4782 ip += first_match.len;
4783 anchor = ip;
4784 continue;
4785 }
4786
4787 let fill_end = (prev_max_touched + 1).min(LZ4_OPT_NUM + 3);
4796 opt[..fill_end].fill(HcOptimal {
4797 price: u32::MAX / 4,
4798 off: 0,
4799 mlen: 1,
4800 litlen: 0,
4801 });
4802
4803 for rpos in 0..MINMATCH {
4804 opt[rpos] = HcOptimal {
4805 price: hc_literals_price(llen + rpos),
4806 off: 0,
4807 mlen: 1,
4808 litlen: (llen + rpos) as u32,
4809 };
4810 }
4811
4812 let first_len = first_match.len.min(LZ4_OPT_NUM - 4);
4813 for mlen in MINMATCH..=first_len {
4814 opt[mlen] = HcOptimal {
4815 price: hc_sequence_price(llen, mlen),
4816 off: first_match.off as u32,
4817 mlen: mlen as u32,
4818 litlen: llen as u32,
4819 };
4820 }
4821 let mut last_match_pos = first_len;
4822 for add_lit in 1..=3 {
4823 let pos = last_match_pos + add_lit;
4824 opt[pos] = HcOptimal {
4825 price: opt[last_match_pos].price + hc_literals_price(add_lit),
4826 off: 0,
4827 mlen: 1,
4828 litlen: add_lit as u32,
4829 };
4830 }
4831
4832 let mut best_mlen = opt[last_match_pos].mlen as usize;
4833 let mut best_off = opt[last_match_pos].off as usize;
4834 let mut cur = 1usize;
4835 let mut immediate = false;
4836
4837 while cur < last_match_pos {
4838 let cur_ptr = ip + cur;
4839 if cur_ptr > mflimit {
4840 break;
4841 }
4842
4843 if FULL_UPDATE {
4844 if opt[cur + 1].price <= opt[cur].price
4845 && opt[cur + MINMATCH].price < opt[cur].price + 3
4846 {
4847 cur += 1;
4848 continue;
4849 }
4850 } else if opt[cur + 1].price <= opt[cur].price {
4851 cur += 1;
4852 continue;
4853 }
4854
4855 let min_len = if FULL_UPDATE {
4856 MINMATCH - 1
4857 } else {
4858 last_match_pos - cur
4859 };
4860 let new_match = find_hc_longer_match::<FAVOR_DEC_SPEED>(
4861 full,
4862 &mut table,
4863 cur_ptr,
4864 match_limit,
4865 min_len,
4866 ATTEMPTS,
4867 );
4868 if new_match.len == 0 {
4869 cur += 1;
4870 continue;
4871 }
4872
4873 if new_match.len > SUFFICIENT_LEN || new_match.len + cur >= LZ4_OPT_NUM {
4874 best_mlen = new_match.len;
4875 best_off = new_match.off;
4876 last_match_pos = cur + 1;
4877 immediate = true;
4878 break;
4879 }
4880
4881 let base_litlen = opt[cur].litlen as usize;
4882 let lit_anchor_price = opt[cur]
4885 .price
4886 .saturating_sub(hc_literals_price(base_litlen));
4887 for litlen in 1..MINMATCH {
4888 let pos = cur + litlen;
4889 let price = lit_anchor_price + hc_literals_price(base_litlen + litlen);
4890 if price < opt[pos].price {
4891 opt[pos] = HcOptimal {
4892 price,
4893 off: 0,
4894 mlen: 1,
4895 litlen: (base_litlen + litlen) as u32,
4896 };
4897 }
4898 }
4899
4900 let match_len = new_match.len.min(LZ4_OPT_NUM - cur - 1);
4901 let (base_ll, base_price) = if opt[cur].mlen == 1 {
4908 let ll = opt[cur].litlen as usize;
4909 let prefix = if cur > ll { opt[cur - ll].price } else { 0 };
4910 (ll, prefix)
4911 } else {
4912 (0usize, opt[cur].price)
4913 };
4914 let new_off_u32 = new_match.off as u32;
4915 let base_ll_u32 = base_ll as u32;
4916 let dec_speed_adj = u32::from(FAVOR_DEC_SPEED);
4917 let base_seq_price = base_price + 3 + hc_literals_price(base_ll);
4922 let lmp_thresh = last_match_pos + 3;
4929 for ml in MINMATCH..match_len {
4930 let pos = cur + ml;
4931 let mut price = base_seq_price;
4932 if ml >= 15 + MINMATCH {
4933 price += 1 + ((ml - (15 + MINMATCH)) / 255) as u32;
4934 }
4935 let do_write = if pos > lmp_thresh {
4941 true
4942 } else {
4943 let acceptable_price = opt[pos].price.saturating_sub(dec_speed_adj);
4944 price <= acceptable_price
4945 };
4946 if do_write {
4947 opt[pos] = HcOptimal {
4948 price,
4949 off: new_off_u32,
4950 mlen: ml as u32,
4951 litlen: base_ll_u32,
4952 };
4953 }
4954 }
4955 if match_len >= MINMATCH {
4958 let ml = match_len;
4959 let pos = cur + ml;
4960 let mut price = base_seq_price;
4961 if ml >= 15 + MINMATCH {
4962 price += 1 + ((ml - (15 + MINMATCH)) / 255) as u32;
4963 }
4964 let do_write = if pos > last_match_pos + 3 {
4965 true
4966 } else {
4967 let acceptable_price = opt[pos].price.saturating_sub(dec_speed_adj);
4968 price <= acceptable_price
4969 };
4970 if do_write {
4971 if last_match_pos < pos {
4972 last_match_pos = pos;
4973 }
4974 opt[pos] = HcOptimal {
4975 price,
4976 off: new_off_u32,
4977 mlen: ml as u32,
4978 litlen: base_ll_u32,
4979 };
4980 }
4981 }
4982
4983 for add_lit in 1..=3 {
4984 let pos = last_match_pos + add_lit;
4985 opt[pos] = HcOptimal {
4986 price: opt[last_match_pos].price + hc_literals_price(add_lit),
4987 off: 0,
4988 mlen: 1,
4989 litlen: add_lit as u32,
4990 };
4991 }
4992 cur += 1;
4993 }
4994
4995 if !immediate {
4996 best_mlen = opt[last_match_pos].mlen as usize;
4997 best_off = opt[last_match_pos].off as usize;
4998 cur = last_match_pos.saturating_sub(best_mlen);
4999 }
5000
5001 let mut candidate_pos = cur;
5002 let mut selected_match_length = best_mlen as u32;
5003 let mut selected_offset = best_off as u32;
5004 loop {
5005 let next_match_length = opt[candidate_pos].mlen;
5006 let next_offset = opt[candidate_pos].off;
5007 opt[candidate_pos].mlen = selected_match_length;
5008 opt[candidate_pos].off = selected_offset;
5009 selected_match_length = next_match_length;
5010 selected_offset = next_offset;
5011 if next_match_length as usize > candidate_pos {
5012 break;
5013 }
5014 candidate_pos -= next_match_length as usize;
5015 }
5016
5017 let mut rpos = 0usize;
5018 while rpos < last_match_pos {
5019 let ml = opt[rpos].mlen as usize;
5020 let offset = opt[rpos].off as usize;
5021 if ml == 1 {
5022 ip += 1;
5023 rpos += 1;
5024 continue;
5025 }
5026 rpos += ml;
5027 op = encode_sequence(full, dst, anchor, ip, ml, offset, op)?;
5028 ip += ml;
5029 anchor = ip;
5030 }
5031 prev_max_touched = last_match_pos + 3;
5035 }
5036
5037 emit_last_literals_with_base(full, dst, base, anchor, op)
5038}
5039
5040fn compress_frame_block(
5045 src: &[u8],
5046 dst: &mut [u8],
5047 prefs: &FramePrefs,
5048 dict: &[u8],
5049) -> Option<usize> {
5050 if prefs.compression_level >= LZ4HC_CLEVEL_MIN && !dict.is_empty() {
5051 compress_block_hc_with_dict(
5052 src,
5053 dst,
5054 dict,
5055 prefs.compression_level,
5056 prefs.favor_dec_speed,
5057 )
5058 } else if prefs.compression_level >= LZ4HC_CLEVEL_MIN {
5059 compress_block_hc(src, dst, prefs.compression_level, prefs.favor_dec_speed)
5060 } else {
5061 compress_block(src, dst, 1)
5062 }
5063}
5064
5065fn normalize_hc_level(compression_level: c_int) -> c_int {
5069 if compression_level < 1 {
5070 LZ4HC_CLEVEL_DEFAULT
5071 } else {
5072 cmp::min(compression_level, LZ4HC_CLEVEL_MAX)
5073 }
5074}
5075
5076#[inline(always)]
5081fn hc_literals_price(lit_len: usize) -> u32 {
5082 let mut price = lit_len as u32;
5083 if lit_len >= 15 {
5084 price += 1 + ((lit_len - 15) / 255) as u32;
5085 }
5086 price
5087}
5088
5089#[inline(always)]
5094fn hc_sequence_price(lit_len: usize, match_len: usize) -> u32 {
5095 let mut price = 1 + 2 + hc_literals_price(lit_len);
5096 if match_len >= 15 + MINMATCH {
5097 price += 1 + ((match_len - (15 + MINMATCH)) / 255) as u32;
5098 }
5099 price
5100}
5101
5102fn compress_hc_dest_size(
5109 src: &[u8],
5110 dst: &mut [u8],
5111 compression_level: c_int,
5112 favor_dec_speed: bool,
5113) -> Option<(usize, usize)> {
5114 if src.is_empty() {
5115 let written = compress_block_hc(src, dst, compression_level, favor_dec_speed)?;
5116 return Some((0, written));
5117 }
5118
5119 let mut low = 0usize;
5120 let mut high = src.len();
5121 let mut best: Option<(usize, usize, Vec<u8>)> = None;
5122 while low <= high {
5123 let mid = low + (high - low) / 2;
5124 let mut candidate = vec![0u8; dst.len()];
5125 match compress_block_hc(
5126 &src[..mid],
5127 &mut candidate,
5128 compression_level,
5129 favor_dec_speed,
5130 ) {
5131 Some(written) if written <= dst.len() => {
5132 best = Some((mid, written, candidate));
5133 low = mid + 1;
5134 }
5135 _ => {
5136 if mid == 0 {
5137 break;
5138 }
5139 high = mid - 1;
5140 }
5141 }
5142 }
5143
5144 let (consumed, written, candidate) = best?;
5145 dst[..written].copy_from_slice(&candidate[..written]);
5146 Some((consumed, written))
5147}
5148
5149fn compress_hc_dest_size_with_dict(
5153 src: &[u8],
5154 dst: &mut [u8],
5155 dict: &[u8],
5156 compression_level: c_int,
5157 favor_dec_speed: bool,
5158) -> Option<(usize, usize)> {
5159 if src.is_empty() {
5160 let written =
5161 compress_block_hc_with_dict(src, dst, dict, compression_level, favor_dec_speed)?;
5162 return Some((0, written));
5163 }
5164
5165 let mut low = 0usize;
5166 let mut high = src.len();
5167 let mut best: Option<(usize, usize, Vec<u8>)> = None;
5168 while low <= high {
5169 let mid = low + (high - low) / 2;
5170 let mut candidate = vec![0u8; dst.len()];
5171 match compress_block_hc_with_dict(
5172 &src[..mid],
5173 &mut candidate,
5174 dict,
5175 compression_level,
5176 favor_dec_speed,
5177 ) {
5178 Some(written) if written <= dst.len() => {
5179 best = Some((mid, written, candidate));
5180 low = mid + 1;
5181 }
5182 _ => {
5183 if mid == 0 {
5184 break;
5185 }
5186 high = mid - 1;
5187 }
5188 }
5189 }
5190
5191 let (consumed, written, candidate) = best?;
5192 dst[..written].copy_from_slice(&candidate[..written]);
5193 Some((consumed, written))
5194}
5195
5196fn append_hc_dictionary(dictionary: &mut Vec<u8>, src: &[u8]) {
5201 dictionary.extend_from_slice(src);
5202 if dictionary.len() > LZ4_DISTANCE_MAX {
5203 let drop_len = dictionary.len() - LZ4_DISTANCE_MAX;
5204 dictionary.drain(..drop_len);
5205 }
5206}
5207
5208fn find_hc_match<const PATTERN_ANALYSIS: bool>(
5214 src: &[u8],
5215 table: &mut HcTables,
5216 ip: usize,
5217 match_limit: usize,
5218 max_attempts: usize,
5219) -> HcMatch {
5220 find_hc_wider_match::<PATTERN_ANALYSIS, false, false>(
5221 src,
5222 table,
5223 ip,
5224 ip,
5225 match_limit,
5226 MINMATCH - 1,
5227 max_attempts,
5228 )
5229}
5230
5231#[inline(always)]
5237fn find_hc_longer_match<const FAVOR_DEC_SPEED: bool>(
5238 src: &[u8],
5239 table: &mut HcTables,
5240 ip: usize,
5241 match_limit: usize,
5242 min_len: usize,
5243 max_attempts: usize,
5244) -> HcMatch {
5245 let m = find_hc_wider_match::<true, true, FAVOR_DEC_SPEED>(
5246 src,
5247 table,
5248 ip,
5249 ip,
5250 match_limit,
5251 min_len,
5252 max_attempts,
5253 );
5254 if m.len <= min_len {
5255 HcMatch {
5256 start: ip,
5257 len: 0,
5258 off: 0,
5259 }
5260 } else {
5261 let len = if FAVOR_DEC_SPEED && m.len > 18 && m.len <= 36 {
5262 18
5263 } else {
5264 m.len
5265 };
5266 HcMatch { len, ..m }
5267 }
5268}
5269
5270#[inline(always)]
5277fn find_hc_wider_match<
5278 const PATTERN_ANALYSIS: bool,
5279 const CHAIN_SWAP: bool,
5280 const FAVOR_DEC_SPEED: bool,
5281>(
5282 src: &[u8],
5283 table: &mut HcTables,
5284 ip: usize,
5285 low_limit: usize,
5286 match_limit: usize,
5287 longest: usize,
5288 max_attempts: usize,
5289) -> HcMatch {
5290 table.insert_until(src, ip);
5291 if ip + MINMATCH > match_limit {
5292 return HcMatch {
5293 start: ip,
5294 len: 0,
5295 off: 0,
5296 };
5297 }
5298
5299 let ip_log = HcTables::to_log(ip);
5300 let lowest_log = cmp::max(HC_OFFSET, ip_log.wrapping_sub(LZ4_DISTANCE_MAX as u32));
5304 let mut candidate_log = table.hash[hash4_hc(src, ip)];
5305 let mut attempts = max_attempts;
5306 let mut best = HcMatch {
5307 start: ip,
5308 len: longest,
5309 off: 0,
5310 };
5311 let pattern = read_u32(&src[ip..]);
5312 let repeated_pattern = is_repeated_pattern(pattern);
5313 let mut src_pattern_len = 0usize;
5314 let mut match_chain_pos = 0usize;
5315 let src_ptr = src.as_ptr();
5316 let look_back = ip - low_limit;
5317 let prefix_base = table.base;
5318 let chain_ptr = table.chain.as_ptr();
5321 while candidate_log >= lowest_log && attempts > 0 {
5326 attempts -= 1;
5327 let candidate = HcTables::to_phys(candidate_log);
5328 let mut match_len = 0usize;
5329 let early_skip = FAVOR_DEC_SPEED && ip - candidate < 8;
5340 if !early_skip {
5341 let mut passes_filter = true;
5342 if best.len >= 1 && candidate >= look_back {
5343 let ref_end = low_limit + best.len;
5344 let cand_end = candidate - look_back + best.len;
5345 let a = read_u16_ptr(unsafe { src_ptr.add(ref_end - 1) });
5346 let b = read_u16_ptr(unsafe { src_ptr.add(cand_end - 1) });
5347 passes_filter = a == b;
5348 }
5349 if passes_filter && read_u32_ptr(unsafe { src_ptr.add(candidate) }) == pattern {
5350 let forward =
5351 MINMATCH + count_match(src, ip + MINMATCH, candidate + MINMATCH, match_limit);
5352 let back = count_back(src, ip, candidate, low_limit);
5353 let len = forward + back;
5354 match_len = len;
5355 if len > best.len {
5356 best = HcMatch {
5357 start: ip - back,
5358 len,
5359 off: ip - candidate,
5360 };
5361 }
5362 }
5363 }
5364
5365 if CHAIN_SWAP && match_len == best.len && candidate + best.len <= ip {
5366 let mut distance_to_next: u32 = 1;
5367 let end = best.len.saturating_sub(MINMATCH - 1);
5368 let mut accel = 1usize << 4;
5369 let mut pos = 0usize;
5370 while pos < end {
5371 let probe = candidate + pos;
5372 let candidate_dist = unsafe { *chain_ptr.add(probe & LZ4_DISTANCE_MAX) } as u32;
5377 let step = accel >> 4;
5378 accel += 1;
5379 if candidate_dist > distance_to_next {
5380 distance_to_next = candidate_dist;
5381 match_chain_pos = pos;
5382 accel = 1usize << 4;
5383 }
5384 pos += step;
5385 }
5386 if distance_to_next > 1 {
5387 candidate_log = candidate_log.wrapping_sub(distance_to_next);
5388 continue;
5389 }
5390 }
5391
5392 let chain_probe = candidate + match_chain_pos;
5393 let dist_next_match = unsafe { *chain_ptr.add(chain_probe & LZ4_DISTANCE_MAX) } as u32;
5394 if PATTERN_ANALYSIS
5395 && dist_next_match == 1
5396 && repeated_pattern
5397 && candidate_log > lowest_log
5398 && match_chain_pos == 0
5399 {
5400 if src_pattern_len == 0 {
5401 src_pattern_len =
5402 MINMATCH + count_pattern(src, ip + MINMATCH, match_limit, pattern);
5403 }
5404 let match_candidate = candidate - 1;
5405 let protect_dict_end = prefix_base == 0
5414 || match_candidate >= prefix_base
5415 || match_candidate + MINMATCH <= prefix_base;
5416 let lowest = HcTables::to_phys(lowest_log);
5417 if protect_dict_end
5418 && match_candidate >= lowest
5419 && match_candidate + MINMATCH <= src.len()
5420 && read_u32_ptr(unsafe { src.as_ptr().add(match_candidate) }) == pattern
5421 {
5422 let forward =
5423 MINMATCH + count_pattern(src, match_candidate + MINMATCH, match_limit, pattern);
5424 let back_raw = reverse_count_pattern(src, match_candidate, 0, pattern);
5433 let back = cmp::min(back_raw, match_candidate.saturating_sub(lowest));
5434 let current_segment_len = back + forward;
5435 let adjusted_to_segment_end =
5436 current_segment_len >= src_pattern_len && forward <= src_pattern_len;
5437 let mut next_candidate = if adjusted_to_segment_end {
5438 match_candidate + forward - src_pattern_len
5439 } else {
5440 match_candidate.saturating_sub(back)
5441 };
5442 if next_candidate < lowest {
5443 next_candidate = lowest;
5444 }
5445 if adjusted_to_segment_end {
5446 if next_candidate < candidate {
5447 candidate_log = HcTables::to_log(next_candidate);
5448 continue;
5449 }
5450 } else if low_limit == ip
5451 && next_candidate < ip
5452 && ip - next_candidate <= LZ4_DISTANCE_MAX
5453 {
5454 let max_len = cmp::min(current_segment_len, src_pattern_len);
5455 if max_len > best.len && !(FAVOR_DEC_SPEED && ip - next_candidate < 8) {
5456 best = HcMatch {
5457 start: ip,
5458 len: max_len,
5459 off: ip - next_candidate,
5460 };
5461 }
5462 if next_candidate < candidate {
5463 let after_pattern = table.previous(next_candidate);
5464 candidate_log =
5465 if after_pattern != usize::MAX && after_pattern < next_candidate {
5466 HcTables::to_log(after_pattern)
5467 } else {
5468 HcTables::to_log(next_candidate)
5469 };
5470 continue;
5471 }
5472 }
5473
5474 if next_candidate < candidate {
5475 candidate_log = HcTables::to_log(next_candidate);
5476 continue;
5477 }
5478 }
5479 }
5480
5481 candidate_log = candidate_log.wrapping_sub(dist_next_match);
5486 }
5487
5488 best
5489}
5490
5491#[inline(always)]
5496fn is_repeated_pattern(pattern: u32) -> bool {
5497 (pattern & 0xffff) == (pattern >> 16) && (pattern & 0xff) == (pattern >> 24)
5498}
5499
5500#[inline]
5505fn count_pattern(src: &[u8], mut pos: usize, limit: usize, pattern: u32) -> usize {
5506 let byte = pattern as u8;
5507 let start = pos;
5508 while pos < limit && src[pos] == byte {
5509 pos += 1;
5510 }
5511 pos - start
5512}
5513
5514fn reverse_count_pattern(src: &[u8], mut pos: usize, low_limit: usize, pattern: u32) -> usize {
5521 let start = pos;
5522 let pattern_bytes = pattern.to_ne_bytes();
5526 while pos >= low_limit + 4 {
5527 let prev = read_u32_ptr(unsafe { src.as_ptr().add(pos - 4) });
5528 if prev != pattern {
5529 break;
5530 }
5531 pos -= 4;
5532 }
5533 let mut tail_idx = 3usize;
5534 while pos > low_limit {
5535 if src[pos - 1] != pattern_bytes[tail_idx] {
5536 break;
5537 }
5538 pos -= 1;
5539 if tail_idx == 0 {
5540 break;
5541 }
5542 tail_idx -= 1;
5543 }
5544 start - pos
5545}
5546
5547#[inline(always)]
5553fn count_back(src: &[u8], ip: usize, candidate: usize, low_limit: usize) -> usize {
5554 let mut back = 0usize;
5555 let max_back = cmp::min(ip - low_limit, candidate);
5556 let src_ptr = src.as_ptr();
5557 unsafe {
5558 while back + 8 <= max_back {
5559 let a = read_u64_ptr(src_ptr.add(ip - back - 8));
5560 let b = read_u64_ptr(src_ptr.add(candidate - back - 8));
5561 let diff = a ^ b;
5562 if diff == 0 {
5563 back += 8;
5564 } else {
5565 back += diff.leading_zeros() as usize / 8;
5566 return back;
5567 }
5568 }
5569 while back < max_back && *src_ptr.add(ip - back - 1) == *src_ptr.add(candidate - back - 1) {
5570 back += 1;
5571 }
5572 }
5573 back
5574}
5575
5576#[inline(always)]
5581fn count_match(src: &[u8], mut ip: usize, mut match_pos: usize, limit: usize) -> usize {
5582 let start = ip;
5583 let src_ptr = src.as_ptr();
5584 unsafe {
5585 while ip + 8 <= limit {
5586 let diff = read_u64_ptr(src_ptr.add(ip)) ^ read_u64_ptr(src_ptr.add(match_pos));
5587 if diff != 0 {
5588 return ip - start + (diff.trailing_zeros() as usize / 8);
5589 }
5590 ip += 8;
5591 match_pos += 8;
5592 }
5593 while ip < limit && *src_ptr.add(ip) == *src_ptr.add(match_pos) {
5594 ip += 1;
5595 match_pos += 1;
5596 }
5597 }
5598 ip - start
5599}
5600
5601#[inline(always)]
5607fn encode_sequence(
5608 src: &[u8],
5609 dst: &mut [u8],
5610 anchor: usize,
5611 ip: usize,
5612 match_len: usize,
5613 offset: usize,
5614 mut op: usize,
5615) -> Option<usize> {
5616 if offset == 0 || offset > LZ4_DISTANCE_MAX {
5617 return None;
5618 }
5619 let lit_len = ip - anchor;
5620 let token_pos = op;
5621 if op >= dst.len() {
5622 return None;
5623 }
5624 op += 1;
5625 op = emit_len(dst, op, lit_len, 15)?;
5626 if op + lit_len + 2 > dst.len() {
5627 return None;
5628 }
5629 dst[op..op + lit_len].copy_from_slice(&src[anchor..ip]);
5630 op += lit_len;
5631 dst[op..op + 2].copy_from_slice(&(offset as u16).to_le_bytes());
5632 op += 2;
5633
5634 let ml_code = match_len - MINMATCH;
5635 dst[token_pos] = ((cmp::min(lit_len, 15) as u8) << 4) | cmp::min(ml_code, 15) as u8;
5636 emit_len(dst, op, ml_code, 15)
5637}
5638
5639fn emit_last_literals(src: &[u8], dst: &mut [u8], anchor: usize, mut op: usize) -> Option<usize> {
5644 let lit_len = src.len() - anchor;
5645 if op >= dst.len() {
5646 return None;
5647 }
5648 let token_pos = op;
5649 op += 1;
5650 dst[token_pos] = (cmp::min(lit_len, 15) as u8) << 4;
5651 op = emit_len(dst, op, lit_len, 15)?;
5652 if op + lit_len > dst.len() {
5653 return None;
5654 }
5655 dst[op..op + lit_len].copy_from_slice(&src[anchor..]);
5656 Some(op + lit_len)
5657}
5658
5659fn emit_last_literals_with_base(
5665 full: &[u8],
5666 dst: &mut [u8],
5667 base: usize,
5668 anchor: usize,
5669 mut op: usize,
5670) -> Option<usize> {
5671 if anchor < base || anchor > full.len() {
5672 return None;
5673 }
5674 let lit_len = full.len() - anchor;
5675 if op >= dst.len() {
5676 return None;
5677 }
5678 let token_pos = op;
5679 op += 1;
5680 dst[token_pos] = (cmp::min(lit_len, 15) as u8) << 4;
5681 op = emit_len(dst, op, lit_len, 15)?;
5682 if op + lit_len > dst.len() {
5683 return None;
5684 }
5685 let literal_start = anchor - base;
5686 dst[op..op + lit_len]
5687 .copy_from_slice(&full[base + literal_start..base + literal_start + lit_len]);
5688 Some(op + lit_len)
5689}
5690
5691fn emit_len(dst: &mut [u8], mut op: usize, len: usize, base: usize) -> Option<usize> {
5697 if len >= base {
5698 let mut extra = len - base;
5699 while extra >= 255 {
5700 if op >= dst.len() {
5701 return None;
5702 }
5703 dst[op] = 255;
5704 op += 1;
5705 extra -= 255;
5706 }
5707 if op >= dst.len() {
5708 return None;
5709 }
5710 dst[op] = extra as u8;
5711 op += 1;
5712 }
5713 Some(op)
5714}
5715
5716const FASTLOOP_SAFE_DISTANCE: usize = 64;
5720
5721#[inline(always)]
5728unsafe fn wild_copy_32(mut dst_ptr: *mut u8, mut src_ptr: *const u8, dst_end: *mut u8) {
5729 loop {
5730 ptr::copy_nonoverlapping(src_ptr, dst_ptr, 16);
5731 ptr::copy_nonoverlapping(src_ptr.add(16), dst_ptr.add(16), 16);
5732 dst_ptr = dst_ptr.add(32);
5733 src_ptr = src_ptr.add(32);
5734 if dst_ptr >= dst_end {
5735 break;
5736 }
5737 }
5738}
5739
5740#[inline(always)]
5744fn read_len_fast(src: &[u8], ip: &mut usize) -> Option<usize> {
5745 let mut total: usize = 15;
5746 loop {
5747 if *ip >= src.len() {
5748 return None;
5749 }
5750 let b = src[*ip] as usize;
5751 *ip += 1;
5752 total = total.checked_add(b)?;
5753 if b != 255 {
5754 return Some(total);
5755 }
5756 }
5757}
5758
5759#[inline]
5767fn decompress_block(src: &[u8], dst: &mut [u8]) -> Option<usize> {
5768 let oend = dst.len();
5769 let iend = src.len();
5770
5771 if oend < FASTLOOP_SAFE_DISTANCE {
5773 return decompress_block_safe(src, dst, 0, 0);
5774 }
5775
5776 let dst_ptr = dst.as_mut_ptr();
5777 let src_ptr = src.as_ptr();
5778 let mut ip = 0usize;
5779 let mut op = 0usize;
5780
5781 while op + FASTLOOP_SAFE_DISTANCE <= oend && ip + 17 <= iend {
5785 let token = unsafe { *src_ptr.add(ip) } as usize;
5786 ip += 1;
5787 let lit_len_token = token >> 4;
5788
5789 let lit_len = if lit_len_token < 15 {
5791 unsafe {
5796 ptr::copy_nonoverlapping(src_ptr.add(ip), dst_ptr.add(op), 16);
5797 }
5798 lit_len_token
5799 } else {
5800 let lit_len = read_len_fast(src, &mut ip)?;
5804 if ip + lit_len + 32 <= iend && op + lit_len + 32 <= oend {
5805 unsafe {
5806 wild_copy_32(dst_ptr.add(op), src_ptr.add(ip), dst_ptr.add(op + lit_len));
5807 }
5808 } else {
5809 if ip + lit_len > iend || op + lit_len > oend {
5810 return None;
5811 }
5812 unsafe {
5813 ptr::copy_nonoverlapping(src_ptr.add(ip), dst_ptr.add(op), lit_len);
5814 }
5815 }
5816 lit_len
5817 };
5818 ip += lit_len;
5819 op += lit_len;
5820
5821 if ip == iend {
5823 return Some(op);
5824 }
5825
5826 if ip + 2 > iend {
5828 return None;
5829 }
5830 let offset =
5831 unsafe { (*src_ptr.add(ip) as usize) | ((*src_ptr.add(ip + 1) as usize) << 8) };
5832 ip += 2;
5833 if offset == 0 || offset > op {
5834 return None;
5835 }
5836
5837 let match_len = if (token & 0x0F) == 15 {
5839 read_len_fast(src, &mut ip)? + MINMATCH
5840 } else {
5841 (token & 0x0F) + MINMATCH
5842 };
5843 if ip == iend {
5844 return None;
5845 }
5846
5847 if op + match_len + 32 > oend {
5849 copy_match_no_dict(dst, &mut op, offset, match_len)?;
5853 continue;
5854 }
5855
5856 unsafe {
5857 let match_src = dst_ptr.add(op - offset);
5858 if offset >= 16 {
5859 wild_copy_32(dst_ptr.add(op), match_src, dst_ptr.add(op + match_len));
5861 op += match_len;
5862 } else if offset >= 8 && match_len <= 18 {
5863 ptr::copy_nonoverlapping(match_src, dst_ptr.add(op), 8);
5866 ptr::copy_nonoverlapping(match_src.add(8), dst_ptr.add(op + 8), 8);
5867 ptr::copy_nonoverlapping(match_src.add(16), dst_ptr.add(op + 16), 2);
5868 op += match_len;
5869 } else {
5870 copy_match_no_dict(dst, &mut op, offset, match_len)?;
5873 }
5874 }
5875 }
5876
5877 decompress_block_safe(src, dst, ip, op)
5878}
5879
5880#[inline]
5887fn decompress_block_safe(
5888 src: &[u8],
5889 dst: &mut [u8],
5890 mut ip: usize,
5891 mut op: usize,
5892) -> Option<usize> {
5893 while ip < src.len() {
5894 let token = src[ip];
5895 ip += 1;
5896
5897 let lit_len = read_len(src, &mut ip, (token >> 4) as usize)?;
5898 if ip + lit_len > src.len() || op + lit_len > dst.len() {
5899 return None;
5900 }
5901 unsafe {
5902 ptr::copy_nonoverlapping(src.as_ptr().add(ip), dst.as_mut_ptr().add(op), lit_len);
5903 }
5904 ip += lit_len;
5905 op += lit_len;
5906 if ip == src.len() {
5907 return Some(op);
5908 }
5909 if ip + 2 > src.len() {
5910 return None;
5911 }
5912 let offset = read_u16(&src[ip..]) as usize;
5913 ip += 2;
5914 if offset == 0 || offset > op {
5915 return None;
5916 }
5917 let match_len = read_len(src, &mut ip, (token & 0x0f) as usize)? + MINMATCH;
5918 if ip == src.len() {
5919 return None;
5920 }
5921 if op + match_len > dst.len() {
5922 return None;
5923 }
5924 copy_match_no_dict(dst, &mut op, offset, match_len)?;
5925 }
5926 Some(op)
5927}
5928
5929#[inline]
5934fn decompress_block_with_dict(src: &[u8], dst: &mut [u8], dict: &[u8]) -> Option<usize> {
5935 let mut ip = 0usize;
5936 let mut op = 0usize;
5937 while ip < src.len() {
5938 let token = src[ip];
5939 ip += 1;
5940
5941 let lit_len = read_len(src, &mut ip, (token >> 4) as usize)?;
5942 if ip + lit_len > src.len() || op + lit_len > dst.len() {
5943 return None;
5944 }
5945 dst[op..op + lit_len].copy_from_slice(&src[ip..ip + lit_len]);
5946 ip += lit_len;
5947 op += lit_len;
5948 if ip == src.len() {
5949 return Some(op);
5950 }
5951 if ip + 2 > src.len() {
5952 return None;
5953 }
5954 let offset = read_u16(&src[ip..]) as usize;
5955 ip += 2;
5956 if offset == 0 || offset > op + dict.len() {
5957 return None;
5958 }
5959 let match_len = read_len(src, &mut ip, (token & 0x0f) as usize)? + MINMATCH;
5960 if ip == src.len() {
5961 return None;
5962 }
5963 if op + match_len > dst.len() {
5964 return None;
5965 }
5966 copy_match(dst, dict, &mut op, offset, match_len)?;
5967 }
5968 Some(op)
5969}
5970
5971fn decompress_block_partial(src: &[u8], dst: &mut [u8], target: usize) -> Option<usize> {
5976 decompress_block_partial_with_dict(src, dst, target, &[])
5977}
5978
5979fn decompress_block_partial_with_dict(
5985 src: &[u8],
5986 dst: &mut [u8],
5987 target: usize,
5988 dict: &[u8],
5989) -> Option<usize> {
5990 if target == 0 {
5991 return Some(0);
5992 }
5993 let target = cmp::min(target, dst.len());
5994
5995 let mut ip = 0usize;
5996 let mut op = 0usize;
5997 while ip < src.len() && op < target {
5998 let token = src[ip];
5999 ip += 1;
6000
6001 let lit_len = read_len(src, &mut ip, (token >> 4) as usize)?;
6002 let lit_copy = cmp::min(lit_len, target - op);
6003 if ip + lit_copy > src.len() {
6004 return None;
6005 }
6006 dst[op..op + lit_copy].copy_from_slice(&src[ip..ip + lit_copy]);
6007 op += lit_copy;
6008 if op == target {
6009 return Some(op);
6010 }
6011 if ip + lit_len > src.len() {
6012 return None;
6013 }
6014 ip += lit_len;
6015 if ip == src.len() {
6016 return Some(op);
6017 }
6018 if ip + 2 > src.len() {
6019 return None;
6020 }
6021 let offset = read_u16(&src[ip..]) as usize;
6022 ip += 2;
6023 if offset == 0 || offset > op + dict.len() {
6024 return None;
6025 }
6026 let match_len = read_len(src, &mut ip, (token & 0x0f) as usize)? + MINMATCH;
6027 let match_copy = cmp::min(match_len, target - op);
6028 copy_match(dst, dict, &mut op, offset, match_copy)?;
6029 }
6030 Some(op)
6031}
6032
6033fn decompress_block_exact_ptr(src: *const u8, dst: &mut [u8], dict: &[u8]) -> Option<usize> {
6041 let mut ip = 0usize;
6042 let mut op = 0usize;
6043 while op < dst.len() {
6044 let token = unsafe { *src.add(ip) };
6045 ip += 1;
6046
6047 let lit_len = read_len_ptr(src, &mut ip, (token >> 4) as usize)?;
6048 if op + lit_len > dst.len() {
6049 return None;
6050 }
6051 unsafe {
6052 ptr::copy_nonoverlapping(src.add(ip), dst.as_mut_ptr().add(op), lit_len);
6053 }
6054 ip += lit_len;
6055 op += lit_len;
6056 if op == dst.len() {
6057 return Some(ip);
6058 }
6059
6060 let offset = read_u16_ptr(unsafe { src.add(ip) }) as usize;
6061 ip += 2;
6062 if offset == 0 || offset > op + dict.len() {
6063 return None;
6064 }
6065 let match_len = read_len_ptr(src, &mut ip, (token & 0x0f) as usize)? + MINMATCH;
6066 if op + match_len > dst.len() {
6067 return None;
6068 }
6069 copy_match(dst, dict, &mut op, offset, match_len)?;
6070 }
6071 Some(ip)
6072}
6073
6074fn read_len_ptr(src: *const u8, ip: &mut usize, mut len: usize) -> Option<usize> {
6080 if len == 15 {
6081 loop {
6082 let b = unsafe { *src.add(*ip) } as usize;
6083 *ip += 1;
6084 len = len.checked_add(b)?;
6085 if b != 255 {
6086 break;
6087 }
6088 }
6089 }
6090 Some(len)
6091}
6092
6093#[inline]
6101fn copy_match(
6102 dst: &mut [u8],
6103 dict: &[u8],
6104 op: &mut usize,
6105 offset: usize,
6106 mut len: usize,
6107) -> Option<()> {
6108 if offset == 0 {
6109 return None;
6110 }
6111 if offset > *op {
6112 let dict_offset = offset - *op;
6113 if dict_offset > dict.len() {
6114 return None;
6115 }
6116 let dict_pos = dict.len() - dict_offset;
6117 let dict_len = cmp::min(len, dict.len() - dict_pos);
6118 if *op + dict_len > dst.len() {
6119 return None;
6120 }
6121 unsafe {
6122 ptr::copy_nonoverlapping(
6123 dict.as_ptr().add(dict_pos),
6124 dst.as_mut_ptr().add(*op),
6125 dict_len,
6126 );
6127 }
6128 *op += dict_len;
6129 len -= dict_len;
6130 }
6131
6132 if len == 0 {
6133 return Some(());
6134 }
6135 if offset > *op {
6136 return None;
6137 }
6138 if *op + len > dst.len() {
6139 return None;
6140 }
6141
6142 let first = cmp::min(offset, len);
6143 let src = *op - offset;
6144 if src + first > *op {
6145 return None;
6146 }
6147 unsafe {
6148 ptr::copy_nonoverlapping(dst.as_ptr().add(src), dst.as_mut_ptr().add(*op), first);
6149 }
6150 *op += first;
6151 len -= first;
6152
6153 let mut copied = first;
6154 while len > 0 {
6155 let chunk = cmp::min(copied, len);
6156 let src = *op - copied;
6157 unsafe {
6158 ptr::copy_nonoverlapping(dst.as_ptr().add(src), dst.as_mut_ptr().add(*op), chunk);
6159 }
6160 *op += chunk;
6161 len -= chunk;
6162 copied += chunk;
6163 }
6164 Some(())
6165}
6166
6167#[inline]
6174fn copy_match_no_dict(dst: &mut [u8], op: &mut usize, offset: usize, mut len: usize) -> Option<()> {
6175 if offset == 0 || offset > *op || *op + len > dst.len() {
6176 return None;
6177 }
6178
6179 let first = cmp::min(offset, len);
6180 let src = *op - offset;
6181 unsafe {
6182 ptr::copy_nonoverlapping(dst.as_ptr().add(src), dst.as_mut_ptr().add(*op), first);
6183 }
6184 *op += first;
6185 len -= first;
6186
6187 let mut copied = first;
6188 while len > 0 {
6189 let chunk = cmp::min(copied, len);
6190 let src = *op - copied;
6191 unsafe {
6192 ptr::copy_nonoverlapping(dst.as_ptr().add(src), dst.as_mut_ptr().add(*op), chunk);
6193 }
6194 *op += chunk;
6195 len -= chunk;
6196 copied += chunk;
6197 }
6198 Some(())
6199}
6200
6201#[inline]
6208fn read_len(src: &[u8], ip: &mut usize, mut len: usize) -> Option<usize> {
6209 if len == 15 {
6210 loop {
6211 if *ip >= src.len() {
6212 return None;
6213 }
6214 let b = src[*ip] as usize;
6215 *ip += 1;
6216 len = len.checked_add(b)?;
6217 if b != 255 {
6218 break;
6219 }
6220 }
6221 }
6222 Some(len)
6223}
6224
6225fn hash_fast(src: &[u8], pos: usize, by_u16: bool) -> usize {
6231 if by_u16 {
6232 hash4_bits(src, pos, LZ4_HASH_BITS_U16)
6233 } else if usize::BITS == 64 {
6234 let v = read_u64(&src[pos..]);
6235 (((v << 24).wrapping_mul(889_523_592_379)) >> (64 - LZ4_HASH_BITS)) as usize
6236 } else {
6237 hash4_bits(src, pos, LZ4_HASH_BITS)
6238 }
6239}
6240
6241#[inline(always)]
6242fn hash_fast_const<const BY_U16: bool>(src: &[u8], pos: usize) -> usize {
6243 if BY_U16 {
6244 hash4_bits(src, pos, LZ4_HASH_BITS_U16)
6245 } else if usize::BITS == 64 {
6246 let v = read_u64(&src[pos..]);
6247 (((v << 24).wrapping_mul(889_523_592_379)) >> (64 - LZ4_HASH_BITS)) as usize
6248 } else {
6249 hash4_bits(src, pos, LZ4_HASH_BITS)
6250 }
6251}
6252
6253fn hash4_bits(src: &[u8], pos: usize, bits: usize) -> usize {
6258 let v = read_u32(&src[pos..]);
6259 ((v.wrapping_mul(2_654_435_761)) >> (32 - bits)) as usize
6260}
6261
6262fn hash4_hc(src: &[u8], pos: usize) -> usize {
6267 let v = read_u32(&src[pos..]);
6268 ((v.wrapping_mul(2_654_435_761)) >> (32 - LZ4HC_HASH_BITS)) as usize
6269}
6270
6271fn hash4_mid(src: &[u8], pos: usize) -> usize {
6273 hash4_bits(src, pos, LZ4MID_HASH_BITS)
6274}
6275
6276fn hash8_mid(src: &[u8], pos: usize) -> usize {
6281 let v = read_u64(&src[pos..]);
6282 (((v << 8).wrapping_mul(58_295_818_150_454_627)) >> (64 - LZ4MID_HASH_BITS)) as usize
6283}
6284
6285fn preferences_from_ptr(ptr: *const LZ4FPreferences) -> FramePrefs {
6289 if ptr.is_null() {
6290 return FramePrefs::default();
6291 }
6292 unsafe {
6293 let prefs = &*ptr;
6294 let id = match prefs.frame_info.block_size_id {
6295 BlockSize::Default | BlockSize::Max64KB => 4,
6296 BlockSize::Max256KB => 5,
6297 BlockSize::Max1MB => 6,
6298 BlockSize::Max4MB => 7,
6299 };
6300 FramePrefs {
6301 block_size_id: id,
6302 block_independent: matches!(prefs.frame_info.block_mode, BlockMode::Independent),
6303 block_checksum: matches!(
6304 prefs.frame_info.block_checksum_flag,
6305 BlockChecksum::BlockChecksumEnabled
6306 ),
6307 content_checksum: matches!(
6308 prefs.frame_info.content_checksum_flag,
6309 ContentChecksum::ChecksumEnabled
6310 ),
6311 content_size: prefs.frame_info.content_size,
6312 dict_id: prefs.frame_info.dict_id,
6313 compression_level: prefs.compression_level as c_int,
6314 auto_flush: prefs.auto_flush != 0,
6315 favor_dec_speed: prefs.favor_dec_speed != 0,
6316 }
6317 }
6318}
6319
6320fn frame_header(prefs: FramePrefs) -> Vec<u8> {
6327 let mut out = Vec::with_capacity(19);
6328 out.extend_from_slice(&LZ4F_MAGIC);
6329 let mut flg = 0x40;
6330 if prefs.block_independent {
6331 flg |= 0x20;
6332 }
6333 if prefs.block_checksum {
6334 flg |= 0x10;
6335 }
6336 if prefs.content_size != 0 {
6337 flg |= 0x08;
6338 }
6339 if prefs.content_checksum {
6340 flg |= 0x04;
6341 }
6342 if prefs.dict_id != 0 {
6343 flg |= 0x01;
6344 }
6345 out.push(flg);
6346 out.push(prefs.block_size_id << 4);
6347 if prefs.content_size != 0 {
6348 out.extend_from_slice(&prefs.content_size.to_le_bytes());
6349 }
6350 if prefs.dict_id != 0 {
6351 out.extend_from_slice(&prefs.dict_id.to_le_bytes());
6352 }
6353 let hc = (xxhash32(&out[4..], 0) >> 8) as u8;
6354 out.push(hc);
6355 out
6356}
6357
6358fn parse_frame_header(src: &[u8]) -> Result<(FramePrefs, usize), usize> {
6365 if src.len() < 7 || src[..4] != LZ4F_MAGIC {
6366 return if src.len() >= 4 && src[..4] != LZ4F_MAGIC {
6367 Err(ERROR_FRAME_TYPE_UNKNOWN)
6368 } else {
6369 Err(ERROR_BAD_HEADER)
6370 };
6371 }
6372 let flg = src[4];
6373 if flg & 0xC0 != 0x40 {
6374 return Err(ERROR_HEADER_VERSION_WRONG);
6375 }
6376 if flg & 0x02 != 0 {
6377 return Err(ERROR_RESERVED_FLAG_SET);
6378 }
6379 let bd = src[5];
6380 let block_size_id = (bd >> 4) & 0x07;
6381 if bd & 0x8F != 0 {
6382 return Err(ERROR_RESERVED_FLAG_SET);
6383 }
6384 if !(4..=7).contains(&block_size_id) {
6385 return Err(ERROR_MAX_BLOCK_SIZE_INVALID);
6386 }
6387 let mut pos = 6;
6388 let mut content_size = 0u64;
6389 let mut dict_id = 0u32;
6390 if flg & 0x08 != 0 {
6391 if src.len() < pos + 8 + 1 {
6392 return Err(ERROR_BAD_HEADER);
6393 }
6394 content_size =
6395 u64::from_le_bytes(src[pos..pos + 8].try_into().map_err(|_| ERROR_BAD_HEADER)?);
6396 pos += 8;
6397 }
6398 if flg & 0x01 != 0 {
6399 if src.len() < pos + 4 + 1 {
6400 return Err(ERROR_BAD_HEADER);
6401 }
6402 dict_id = u32::from_le_bytes(src[pos..pos + 4].try_into().map_err(|_| ERROR_BAD_HEADER)?);
6403 pos += 4;
6404 }
6405 if src.len() < pos + 1 {
6406 return Err(ERROR_BAD_HEADER);
6407 }
6408 let expected_hc = (xxhash32(&src[4..pos], 0) >> 8) as u8;
6409 if src[pos] != expected_hc {
6410 return Err(ERROR_HEADER_CHECKSUM_INVALID);
6411 }
6412 pos += 1;
6413 Ok((
6414 FramePrefs {
6415 block_size_id,
6416 block_independent: flg & 0x20 != 0,
6417 block_checksum: flg & 0x10 != 0,
6418 content_checksum: flg & 0x04 != 0,
6419 content_size,
6420 dict_id,
6421 compression_level: 0,
6422 auto_flush: false,
6423 favor_dec_speed: false,
6424 },
6425 pos,
6426 ))
6427}
6428
6429fn is_skippable_magic_prefix(src: &[u8]) -> bool {
6435 if src.len() < 4 {
6436 return false;
6437 }
6438 let magic = u32::from_le_bytes(src[..4].try_into().unwrap());
6439 (LZ4F_SKIPPABLE_MAGIC_MIN..=LZ4F_SKIPPABLE_MAGIC_MAX).contains(&magic)
6440}
6441
6442fn parse_skippable_frame_len(src: &[u8]) -> Option<usize> {
6446 if src.len() < 8 || !is_skippable_magic_prefix(src) {
6447 return None;
6448 }
6449 let content_len = u32::from_le_bytes(src[4..8].try_into().ok()?) as usize;
6450 content_len.checked_add(8)
6451}
6452
6453fn parse_frame_header_if_available(ctx: &mut DecompressionCtx) -> Result<bool, usize> {
6461 if ctx.done || ctx.parsed_header {
6462 return Ok(true);
6463 }
6464 if ctx.input.len().saturating_sub(ctx.pos) >= 4
6465 && is_skippable_magic_prefix(&ctx.input[ctx.pos..])
6466 {
6467 if ctx.input.len().saturating_sub(ctx.pos) < 8 {
6468 return Ok(false);
6469 }
6470 let Some(skip_len) = parse_skippable_frame_len(&ctx.input[ctx.pos..]) else {
6471 return Err(ERROR_BAD_HEADER);
6472 };
6473 if ctx.input.len().saturating_sub(ctx.pos) < skip_len {
6474 compact_input(ctx);
6475 return Ok(false);
6476 }
6477 ctx.pos += skip_len;
6478 ctx.done = true;
6479 compact_input(ctx);
6480 return Ok(true);
6481 }
6482 if ctx.input.len().saturating_sub(ctx.pos) < 7 {
6483 return Ok(false);
6484 }
6485 if let Some(header_len) = expected_frame_header_len(&ctx.input[ctx.pos..]) {
6486 if ctx.input.len().saturating_sub(ctx.pos) < header_len {
6487 return Ok(false);
6488 }
6489 }
6490 let (prefs, header_len) = parse_frame_header(&ctx.input[ctx.pos..])?;
6491 ctx.pos += header_len;
6492 apply_frame_prefs(ctx, prefs);
6493 Ok(true)
6494}
6495
6496fn apply_frame_prefs(ctx: &mut DecompressionCtx, prefs: FramePrefs) {
6500 ctx.parsed_header = true;
6501 ctx.block_checksum = prefs.block_checksum;
6502 ctx.content_checksum = prefs.content_checksum;
6503 ctx.content_size = prefs.content_size;
6504 ctx.content_read = 0;
6505 ctx.dict_id = prefs.dict_id;
6506 ctx.block_independent = prefs.block_independent;
6507 ctx.block_max = block_max_size(prefs.block_size_id);
6508 if !ctx.external_dictionary {
6509 ctx.dictionary.clear();
6510 }
6511 ctx.external_dictionary = false;
6512 ctx.raw_block_remaining = 0;
6513 ctx.raw_block_checksum = XxHash32::new(0);
6514}
6515
6516fn start_raw_block(ctx: &mut DecompressionCtx, block_len: usize) {
6517 ctx.raw_block_remaining = block_len;
6518 ctx.raw_block_checksum = XxHash32::new(0);
6519}
6520
6521macro_rules! dispatch_frame_block_flags {
6522 ($ctx:expr, $skip_checksums:expr, $func:ident($($arg:expr),* $(,)?)) => {
6523 match (
6524 $ctx.block_checksum,
6525 $ctx.content_checksum,
6526 $ctx.block_independent,
6527 $skip_checksums,
6528 ) {
6529 (false, false, false, false) => $func::<false, false, false, false>($($arg),*),
6530 (false, false, false, true) => $func::<false, false, false, true>($($arg),*),
6531 (false, false, true, false) => $func::<false, false, true, false>($($arg),*),
6532 (false, false, true, true) => $func::<false, false, true, true>($($arg),*),
6533 (false, true, false, false) => $func::<false, true, false, false>($($arg),*),
6534 (false, true, false, true) => $func::<false, true, false, true>($($arg),*),
6535 (false, true, true, false) => $func::<false, true, true, false>($($arg),*),
6536 (false, true, true, true) => $func::<false, true, true, true>($($arg),*),
6537 (true, false, false, false) => $func::<true, false, false, false>($($arg),*),
6538 (true, false, false, true) => $func::<true, false, false, true>($($arg),*),
6539 (true, false, true, false) => $func::<true, false, true, false>($($arg),*),
6540 (true, false, true, true) => $func::<true, false, true, true>($($arg),*),
6541 (true, true, false, false) => $func::<true, true, false, false>($($arg),*),
6542 (true, true, false, true) => $func::<true, true, false, true>($($arg),*),
6543 (true, true, true, false) => $func::<true, true, true, false>($($arg),*),
6544 (true, true, true, true) => $func::<true, true, true, true>($($arg),*),
6545 }
6546 };
6547}
6548
6549fn update_raw_block_output<
6550 const BLOCK_CHECKSUM: bool,
6551 const CONTENT_CHECKSUM: bool,
6552 const BLOCK_INDEPENDENT: bool,
6553 const SKIP_CHECKSUMS: bool,
6554>(
6555 ctx: &mut DecompressionCtx,
6556 bytes: &[u8],
6557) {
6558 if BLOCK_CHECKSUM && !SKIP_CHECKSUMS {
6559 ctx.raw_block_checksum.update(bytes);
6560 }
6561 if CONTENT_CHECKSUM {
6562 ctx.content_hasher.update(bytes);
6563 }
6564 ctx.content_read += bytes.len() as u64;
6565 if BLOCK_INDEPENDENT {
6566 ctx.dictionary.clear();
6567 } else {
6568 append_hc_dictionary(&mut ctx.dictionary, bytes);
6569 }
6570}
6571
6572fn validate_content_size_after_block(ctx: &DecompressionCtx) -> Result<(), usize> {
6573 if ctx.content_size != 0 && ctx.content_read > ctx.content_size {
6574 Err(ERROR_FRAME_SIZE_WRONG)
6575 } else {
6576 Ok(())
6577 }
6578}
6579
6580fn consume_frame_end_from_slice<
6581 const BLOCK_CHECKSUM: bool,
6582 const CONTENT_CHECKSUM: bool,
6583 const BLOCK_INDEPENDENT: bool,
6584 const SKIP_CHECKSUMS: bool,
6585>(
6586 ctx: &mut DecompressionCtx,
6587 src: &[u8],
6588 consumed: &mut usize,
6589) -> Option<Result<(), usize>> {
6590 if src.len().saturating_sub(*consumed) < 4 {
6591 return None;
6592 }
6593 let end_mark = u32::from_le_bytes(src[*consumed..*consumed + 4].try_into().unwrap());
6594 if end_mark != 0 {
6595 return Some(Ok(()));
6596 }
6597 let trailer = if CONTENT_CHECKSUM { 4 } else { 0 };
6598 if src.len().saturating_sub(*consumed) < 4 + trailer {
6599 return None;
6600 }
6601 *consumed += 4;
6602 if CONTENT_CHECKSUM {
6603 let stored = u32::from_le_bytes(src[*consumed..*consumed + 4].try_into().unwrap());
6604 *consumed += 4;
6605 if !SKIP_CHECKSUMS && stored != ctx.content_hasher.digest() {
6606 return Some(Err(ERROR_CHECKSUM_INVALID));
6607 }
6608 }
6609 if ctx.content_size != 0 && ctx.content_read != ctx.content_size {
6610 return Some(Err(ERROR_FRAME_SIZE_WRONG));
6611 }
6612 ctx.done = true;
6613 Some(Ok(()))
6614}
6615
6616fn consume_frame_end_from_input<
6617 const BLOCK_CHECKSUM: bool,
6618 const CONTENT_CHECKSUM: bool,
6619 const BLOCK_INDEPENDENT: bool,
6620 const SKIP_CHECKSUMS: bool,
6621>(
6622 ctx: &mut DecompressionCtx,
6623) -> Option<Result<(), usize>> {
6624 if ctx.input.len().saturating_sub(ctx.pos) < 4 {
6625 return None;
6626 }
6627 let end_mark = u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
6628 if end_mark != 0 {
6629 return Some(Ok(()));
6630 }
6631 let trailer = if CONTENT_CHECKSUM { 4 } else { 0 };
6632 if ctx.input.len().saturating_sub(ctx.pos) < 4 + trailer {
6633 return None;
6634 }
6635 ctx.pos += 4;
6636 if CONTENT_CHECKSUM {
6637 let stored = u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
6638 ctx.pos += 4;
6639 if !SKIP_CHECKSUMS && stored != ctx.content_hasher.digest() {
6640 return Some(Err(ERROR_CHECKSUM_INVALID));
6641 }
6642 }
6643 if ctx.content_size != 0 && ctx.content_read != ctx.content_size {
6644 return Some(Err(ERROR_FRAME_SIZE_WRONG));
6645 }
6646 ctx.done = true;
6647 Some(Ok(()))
6648}
6649
6650fn finish_raw_block_checksum_from_slice<
6651 const BLOCK_CHECKSUM: bool,
6652 const CONTENT_CHECKSUM: bool,
6653 const BLOCK_INDEPENDENT: bool,
6654 const SKIP_CHECKSUMS: bool,
6655>(
6656 ctx: &mut DecompressionCtx,
6657 src: &[u8],
6658 consumed: &mut usize,
6659) -> Option<Result<(), usize>> {
6660 if ctx.raw_block_remaining != 0 || !BLOCK_CHECKSUM {
6661 return Some(Ok(()));
6662 }
6663 if src.len().saturating_sub(*consumed) < 4 {
6664 return None;
6665 }
6666 let stored = u32::from_le_bytes(src[*consumed..*consumed + 4].try_into().unwrap());
6667 *consumed += 4;
6668 if !SKIP_CHECKSUMS && stored != ctx.raw_block_checksum.digest() {
6669 return Some(Err(ERROR_BLOCK_CHECKSUM_INVALID));
6670 }
6671 Some(Ok(()))
6672}
6673
6674fn finish_raw_block_checksum_from_input<
6675 const BLOCK_CHECKSUM: bool,
6676 const CONTENT_CHECKSUM: bool,
6677 const BLOCK_INDEPENDENT: bool,
6678 const SKIP_CHECKSUMS: bool,
6679>(
6680 ctx: &mut DecompressionCtx,
6681) -> Option<Result<(), usize>> {
6682 if ctx.raw_block_remaining != 0 || !BLOCK_CHECKSUM {
6683 return Some(Ok(()));
6684 }
6685 if ctx.input.len().saturating_sub(ctx.pos) < 4 {
6686 compact_input(ctx);
6687 return None;
6688 }
6689 let stored = u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
6690 ctx.pos += 4;
6691 if !SKIP_CHECKSUMS && stored != ctx.raw_block_checksum.digest() {
6692 return Some(Err(ERROR_BLOCK_CHECKSUM_INVALID));
6693 }
6694 Some(Ok(()))
6695}
6696
6697fn try_copy_raw_block_slice_to_dst(
6698 ctx: &mut DecompressionCtx,
6699 src: &[u8],
6700 dst: &mut [u8],
6701 skip_checksums: bool,
6702) -> Option<Result<(usize, usize), usize>> {
6703 dispatch_frame_block_flags!(
6704 ctx,
6705 skip_checksums,
6706 try_copy_raw_block_slice_to_dst_spec(ctx, src, dst)
6707 )
6708}
6709
6710fn try_copy_raw_block_slice_to_dst_spec<
6711 const BLOCK_CHECKSUM: bool,
6712 const CONTENT_CHECKSUM: bool,
6713 const BLOCK_INDEPENDENT: bool,
6714 const SKIP_CHECKSUMS: bool,
6715>(
6716 ctx: &mut DecompressionCtx,
6717 src: &[u8],
6718 dst: &mut [u8],
6719) -> Option<Result<(usize, usize), usize>> {
6720 if ctx.raw_block_remaining == 0 {
6721 return None;
6722 }
6723 let to_copy = cmp::min(ctx.raw_block_remaining, cmp::min(src.len(), dst.len()));
6724 if to_copy > 0 {
6725 unsafe {
6726 ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), to_copy);
6727 }
6728 update_raw_block_output::<
6729 BLOCK_CHECKSUM,
6730 CONTENT_CHECKSUM,
6731 BLOCK_INDEPENDENT,
6732 SKIP_CHECKSUMS,
6733 >(ctx, &dst[..to_copy]);
6734 ctx.raw_block_remaining -= to_copy;
6735 }
6736 let mut consumed = to_copy;
6737 if ctx.raw_block_remaining == 0 {
6738 match finish_raw_block_checksum_from_slice::<
6739 BLOCK_CHECKSUM,
6740 CONTENT_CHECKSUM,
6741 BLOCK_INDEPENDENT,
6742 SKIP_CHECKSUMS,
6743 >(ctx, src, &mut consumed)
6744 {
6745 Some(Ok(())) => {}
6746 Some(Err(code)) => return Some(Err(code)),
6747 None => {}
6748 }
6749 if let Err(code) = validate_content_size_after_block(ctx) {
6750 return Some(Err(code));
6751 }
6752 match consume_frame_end_from_slice::<
6753 BLOCK_CHECKSUM,
6754 CONTENT_CHECKSUM,
6755 BLOCK_INDEPENDENT,
6756 SKIP_CHECKSUMS,
6757 >(ctx, src, &mut consumed)
6758 {
6759 Some(Ok(())) | None => {}
6760 Some(Err(code)) => return Some(Err(code)),
6761 }
6762 }
6763 Some(Ok((consumed, to_copy)))
6764}
6765
6766fn copy_raw_block_from_input_to_dst(
6767 ctx: &mut DecompressionCtx,
6768 dst: &mut [u8],
6769 skip_checksums: bool,
6770) -> Option<Result<usize, usize>> {
6771 dispatch_frame_block_flags!(
6772 ctx,
6773 skip_checksums,
6774 copy_raw_block_from_input_to_dst_spec(ctx, dst)
6775 )
6776}
6777
6778fn copy_raw_block_from_input_to_dst_spec<
6779 const BLOCK_CHECKSUM: bool,
6780 const CONTENT_CHECKSUM: bool,
6781 const BLOCK_INDEPENDENT: bool,
6782 const SKIP_CHECKSUMS: bool,
6783>(
6784 ctx: &mut DecompressionCtx,
6785 dst: &mut [u8],
6786) -> Option<Result<usize, usize>> {
6787 if ctx.raw_block_remaining == 0 {
6788 return None;
6789 }
6790 let available = ctx.input.len().saturating_sub(ctx.pos);
6791 let to_copy = cmp::min(ctx.raw_block_remaining, cmp::min(available, dst.len()));
6792 if to_copy > 0 {
6793 unsafe {
6794 ptr::copy_nonoverlapping(ctx.input.as_ptr().add(ctx.pos), dst.as_mut_ptr(), to_copy);
6795 }
6796 ctx.pos += to_copy;
6797 update_raw_block_output::<
6798 BLOCK_CHECKSUM,
6799 CONTENT_CHECKSUM,
6800 BLOCK_INDEPENDENT,
6801 SKIP_CHECKSUMS,
6802 >(ctx, &dst[..to_copy]);
6803 ctx.raw_block_remaining -= to_copy;
6804 }
6805 if ctx.raw_block_remaining == 0 {
6806 match finish_raw_block_checksum_from_input::<
6807 BLOCK_CHECKSUM,
6808 CONTENT_CHECKSUM,
6809 BLOCK_INDEPENDENT,
6810 SKIP_CHECKSUMS,
6811 >(ctx)
6812 {
6813 Some(Ok(())) => {}
6814 Some(Err(code)) => return Some(Err(code)),
6815 None => {}
6816 }
6817 if let Err(code) = validate_content_size_after_block(ctx) {
6818 return Some(Err(code));
6819 }
6820 match consume_frame_end_from_input::<
6821 BLOCK_CHECKSUM,
6822 CONTENT_CHECKSUM,
6823 BLOCK_INDEPENDENT,
6824 SKIP_CHECKSUMS,
6825 >(ctx)
6826 {
6827 Some(Ok(())) | None => {}
6828 Some(Err(code)) => return Some(Err(code)),
6829 }
6830 }
6831 compact_input(ctx);
6832 Some(Ok(to_copy))
6833}
6834
6835fn frame_info_from_decompression_ctx(ctx: &DecompressionCtx) -> LZ4FFrameInfo {
6840 LZ4FFrameInfo {
6841 block_size_id: match ctx.block_max {
6842 n if n == 256 * 1024 => BlockSize::Max256KB,
6843 n if n == 1024 * 1024 => BlockSize::Max1MB,
6844 n if n == 4 * 1024 * 1024 => BlockSize::Max4MB,
6845 _ => BlockSize::Max64KB,
6846 },
6847 block_mode: if ctx.block_independent {
6848 BlockMode::Independent
6849 } else {
6850 BlockMode::Linked
6851 },
6852 content_checksum_flag: if ctx.content_checksum {
6853 ContentChecksum::ChecksumEnabled
6854 } else {
6855 ContentChecksum::NoChecksum
6856 },
6857 frame_type: FrameType::Frame,
6858 content_size: ctx.content_size,
6859 dict_id: ctx.dict_id,
6860 block_checksum_flag: if ctx.block_checksum {
6861 BlockChecksum::BlockChecksumEnabled
6862 } else {
6863 BlockChecksum::NoBlockChecksum
6864 },
6865 }
6866}
6867
6868fn try_decompress_frame_block_to_dst(
6878 ctx: &mut DecompressionCtx,
6879 dst: &mut [u8],
6880 skip_checksums: bool,
6881) -> Option<Result<usize, usize>> {
6882 dispatch_frame_block_flags!(
6883 ctx,
6884 skip_checksums,
6885 try_decompress_frame_block_to_dst_spec(ctx, dst)
6886 )
6887}
6888
6889fn try_decompress_frame_block_to_dst_spec<
6890 const BLOCK_CHECKSUM: bool,
6891 const CONTENT_CHECKSUM: bool,
6892 const BLOCK_INDEPENDENT: bool,
6893 const SKIP_CHECKSUMS: bool,
6894>(
6895 ctx: &mut DecompressionCtx,
6896 dst: &mut [u8],
6897) -> Option<Result<usize, usize>> {
6898 if ctx.done || !ctx.parsed_header || !pending_is_empty(ctx) {
6899 return None;
6900 }
6901 if ctx.input.len().saturating_sub(ctx.pos) < 4 {
6902 compact_input(ctx);
6903 return None;
6904 }
6905 let block_header = u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
6906 let raw = block_header & 0x8000_0000 != 0;
6907 let block_len = (block_header & 0x7FFF_FFFF) as usize;
6908 if block_len == 0 {
6909 return None;
6910 }
6911 if block_len > ctx.block_max {
6912 return Some(Err(ERROR_MAX_BLOCK_SIZE_INVALID));
6913 }
6914 let checksum_len = if BLOCK_CHECKSUM { 4 } else { 0 };
6915 if raw {
6916 ctx.pos += 4;
6917 start_raw_block(ctx, block_len);
6918 return copy_raw_block_from_input_to_dst_spec::<
6919 BLOCK_CHECKSUM,
6920 CONTENT_CHECKSUM,
6921 BLOCK_INDEPENDENT,
6922 SKIP_CHECKSUMS,
6923 >(ctx, dst);
6924 }
6925 if ctx.input.len().saturating_sub(ctx.pos) < 4 + block_len + checksum_len {
6926 compact_input(ctx);
6927 return None;
6928 }
6929 if dst.len() < ctx.block_max {
6930 return None;
6931 }
6932
6933 ctx.pos += 4;
6934 let block_start = ctx.pos;
6935 let block_end = block_start + block_len;
6936 if BLOCK_CHECKSUM && !SKIP_CHECKSUMS {
6937 let stored = u32::from_le_bytes(
6938 ctx.input[block_end..block_end + checksum_len]
6939 .try_into()
6940 .unwrap(),
6941 );
6942 if stored != xxhash32(&ctx.input[block_start..block_end], 0) {
6943 return Some(Err(ERROR_BLOCK_CHECKSUM_INVALID));
6944 }
6945 }
6946
6947 let n = if BLOCK_INDEPENDENT && ctx.dictionary.is_empty() {
6948 decompress_block(&ctx.input[block_start..block_end], dst)
6949 } else {
6950 decompress_block_with_dict(&ctx.input[block_start..block_end], dst, &ctx.dictionary)
6951 };
6952 let written = match n {
6953 Some(n) => n,
6954 None => return Some(Err(ERROR_DECOMPRESSION_FAILED)),
6955 };
6956
6957 if CONTENT_CHECKSUM {
6958 ctx.content_hasher.update(&dst[..written]);
6959 }
6960 ctx.content_read += written as u64;
6961 if BLOCK_INDEPENDENT {
6962 ctx.dictionary.clear();
6963 } else {
6964 append_hc_dictionary(&mut ctx.dictionary, &dst[..written]);
6965 }
6966 ctx.pos = block_end + checksum_len;
6967 if ctx.content_size != 0 && ctx.content_read > ctx.content_size {
6968 return Some(Err(ERROR_FRAME_SIZE_WRONG));
6969 }
6970 if ctx.content_size != 0 {
6971 let trailer = if CONTENT_CHECKSUM { 4 } else { 0 };
6972 if ctx.input.len().saturating_sub(ctx.pos) >= 4 {
6973 let end_mark = u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
6974 if end_mark != 0 {
6975 if ctx.content_read >= ctx.content_size {
6976 return Some(Err(ERROR_FRAME_SIZE_WRONG));
6977 }
6978 } else if ctx.content_read != ctx.content_size {
6979 return Some(Err(ERROR_FRAME_SIZE_WRONG));
6980 } else if ctx.input.len().saturating_sub(ctx.pos) >= 4 + trailer {
6981 ctx.pos += 4;
6982 if CONTENT_CHECKSUM {
6983 let stored =
6984 u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
6985 if !SKIP_CHECKSUMS && stored != ctx.content_hasher.digest() {
6986 return Some(Err(ERROR_CHECKSUM_INVALID));
6987 }
6988 ctx.pos += 4;
6989 }
6990 ctx.done = true;
6991 }
6992 }
6993 }
6994 compact_input(ctx);
6995 Some(Ok(written))
6996}
6997
6998fn try_decompress_frame_block_slice_to_dst(
7007 ctx: &mut DecompressionCtx,
7008 src: &[u8],
7009 dst: &mut [u8],
7010 skip_checksums: bool,
7011) -> Option<Result<(usize, usize), usize>> {
7012 dispatch_frame_block_flags!(
7013 ctx,
7014 skip_checksums,
7015 try_decompress_frame_block_slice_to_dst_spec(ctx, src, dst)
7016 )
7017}
7018
7019fn try_decompress_frame_block_slice_to_dst_spec<
7020 const BLOCK_CHECKSUM: bool,
7021 const CONTENT_CHECKSUM: bool,
7022 const BLOCK_INDEPENDENT: bool,
7023 const SKIP_CHECKSUMS: bool,
7024>(
7025 ctx: &mut DecompressionCtx,
7026 src: &[u8],
7027 dst: &mut [u8],
7028) -> Option<Result<(usize, usize), usize>> {
7029 if src.len() < 4 {
7030 return None;
7031 }
7032 let block_header = u32::from_le_bytes(src[..4].try_into().unwrap());
7033 let raw = block_header & 0x8000_0000 != 0;
7034 let block_len = (block_header & 0x7FFF_FFFF) as usize;
7035 if block_len == 0 {
7036 let trailer = if CONTENT_CHECKSUM { 4 } else { 0 };
7037 if src.len() < 4 + trailer {
7038 return None;
7039 }
7040 if CONTENT_CHECKSUM && !SKIP_CHECKSUMS {
7041 let stored = u32::from_le_bytes(src[4..8].try_into().unwrap());
7042 if stored != ctx.content_hasher.digest() {
7043 return Some(Err(ERROR_CHECKSUM_INVALID));
7044 }
7045 }
7046 if ctx.content_size != 0 && ctx.content_read != ctx.content_size {
7047 return Some(Err(ERROR_FRAME_SIZE_WRONG));
7048 }
7049 ctx.done = true;
7050 return Some(Ok((4 + trailer, 0)));
7051 }
7052 if block_len > ctx.block_max {
7053 return Some(Err(ERROR_MAX_BLOCK_SIZE_INVALID));
7054 }
7055
7056 let checksum_len = if BLOCK_CHECKSUM { 4 } else { 0 };
7057 if raw {
7058 start_raw_block(ctx, block_len);
7059 return try_copy_raw_block_slice_to_dst_spec::<
7060 BLOCK_CHECKSUM,
7061 CONTENT_CHECKSUM,
7062 BLOCK_INDEPENDENT,
7063 SKIP_CHECKSUMS,
7064 >(ctx, &src[4..], dst)
7065 .map(|result| result.map(|(consumed, written)| (consumed + 4, written)));
7066 }
7067 if src.len() < 4 + block_len + checksum_len {
7068 return None;
7069 }
7070 if dst.len() < ctx.block_max {
7071 return None;
7072 }
7073
7074 let block_start = 4;
7075 let block_end = block_start + block_len;
7076 if BLOCK_CHECKSUM && !SKIP_CHECKSUMS {
7077 let stored =
7078 u32::from_le_bytes(src[block_end..block_end + checksum_len].try_into().unwrap());
7079 if stored != xxhash32(&src[block_start..block_end], 0) {
7080 return Some(Err(ERROR_BLOCK_CHECKSUM_INVALID));
7081 }
7082 }
7083
7084 let n = if BLOCK_INDEPENDENT && ctx.dictionary.is_empty() {
7085 decompress_block(&src[block_start..block_end], dst)
7086 } else {
7087 decompress_block_with_dict(&src[block_start..block_end], dst, &ctx.dictionary)
7088 };
7089 let written = match n {
7090 Some(n) => n,
7091 None => return Some(Err(ERROR_DECOMPRESSION_FAILED)),
7092 };
7093
7094 if CONTENT_CHECKSUM {
7095 ctx.content_hasher.update(&dst[..written]);
7096 }
7097 ctx.content_read += written as u64;
7098 if BLOCK_INDEPENDENT {
7099 ctx.dictionary.clear();
7100 } else {
7101 append_hc_dictionary(&mut ctx.dictionary, &dst[..written]);
7102 }
7103 if ctx.content_size != 0 && ctx.content_read > ctx.content_size {
7104 return Some(Err(ERROR_FRAME_SIZE_WRONG));
7105 }
7106 Some(Ok((4 + block_len + checksum_len, written)))
7107}
7108
7109fn parse_available_frame(ctx: &mut DecompressionCtx, skip_checksums: bool) -> Result<(), usize> {
7117 dispatch_frame_block_flags!(ctx, skip_checksums, parse_available_frame_spec(ctx))
7118}
7119
7120fn parse_available_frame_spec<
7121 const BLOCK_CHECKSUM: bool,
7122 const CONTENT_CHECKSUM: bool,
7123 const BLOCK_INDEPENDENT: bool,
7124 const SKIP_CHECKSUMS: bool,
7125>(
7126 ctx: &mut DecompressionCtx,
7127) -> Result<(), usize> {
7128 if ctx.done {
7129 return Ok(());
7130 }
7131 if !parse_frame_header_if_available(ctx)? || ctx.done {
7132 return Ok(());
7133 }
7134
7135 loop {
7136 if ctx.input.len().saturating_sub(ctx.pos) < 4 {
7137 compact_input(ctx);
7138 return Ok(());
7139 }
7140 let block_header = u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
7141 let raw = block_header & 0x8000_0000 != 0;
7142 let block_len = (block_header & 0x7FFF_FFFF) as usize;
7143 if block_len == 0 {
7144 let trailer = if CONTENT_CHECKSUM { 4 } else { 0 };
7145 if ctx.input.len().saturating_sub(ctx.pos) < 4 + trailer {
7146 compact_input(ctx);
7147 return Ok(());
7148 }
7149 ctx.pos += 4;
7150 if CONTENT_CHECKSUM {
7151 let stored =
7152 u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
7153 if !SKIP_CHECKSUMS && stored != ctx.content_hasher.digest() {
7154 return Err(ERROR_CHECKSUM_INVALID);
7155 }
7156 ctx.pos += 4;
7157 }
7158 if ctx.content_size != 0 && ctx.content_read != ctx.content_size {
7159 return Err(ERROR_FRAME_SIZE_WRONG);
7160 }
7161 ctx.done = true;
7162 compact_input(ctx);
7163 return Ok(());
7164 }
7165 if block_len > ctx.block_max {
7166 return Err(ERROR_MAX_BLOCK_SIZE_INVALID);
7167 }
7168 let checksum_len = if BLOCK_CHECKSUM { 4 } else { 0 };
7169 ctx.pos += 4;
7170 let block_start = ctx.pos;
7171 if raw {
7172 start_raw_block(ctx, block_len);
7173 compact_input(ctx);
7174 return Ok(());
7175 }
7176 if ctx.input.len().saturating_sub(ctx.pos - 4) < 4 + block_len + checksum_len {
7177 ctx.pos -= 4;
7178 compact_input(ctx);
7179 return Ok(());
7180 }
7181 let block_end = block_start + block_len;
7182 if BLOCK_CHECKSUM && !SKIP_CHECKSUMS {
7183 let stored = u32::from_le_bytes(
7184 ctx.input[block_end..block_end + checksum_len]
7185 .try_into()
7186 .unwrap(),
7187 );
7188 if stored != xxhash32(&ctx.input[block_start..block_end], 0) {
7189 return Err(ERROR_BLOCK_CHECKSUM_INVALID);
7190 }
7191 }
7192 let mut out = vec![0u8; ctx.block_max];
7193 let n = if BLOCK_INDEPENDENT && ctx.dictionary.is_empty() {
7194 decompress_block(&ctx.input[block_start..block_end], &mut out)
7195 } else {
7196 decompress_block_with_dict(
7197 &ctx.input[block_start..block_end],
7198 &mut out,
7199 &ctx.dictionary,
7200 )
7201 }
7202 .ok_or(ERROR_DECOMPRESSION_FAILED)?;
7203 if CONTENT_CHECKSUM {
7204 ctx.content_hasher.update(&out[..n]);
7205 }
7206 ctx.content_read += n as u64;
7207 ctx.pending.extend_from_slice(&out[..n]);
7208 if BLOCK_INDEPENDENT {
7209 ctx.dictionary.clear();
7210 } else {
7211 append_hc_dictionary(&mut ctx.dictionary, &out[..n]);
7212 }
7213 ctx.pos = block_end + checksum_len;
7214 if ctx.content_size != 0 && ctx.content_read > ctx.content_size {
7215 return Err(ERROR_FRAME_SIZE_WRONG);
7216 }
7217 if !pending_is_empty(ctx) {
7218 compact_input(ctx);
7219 return Ok(());
7220 }
7221 }
7222}
7223
7224fn pending_len(ctx: &DecompressionCtx) -> usize {
7227 ctx.pending.len().saturating_sub(ctx.pending_pos)
7228}
7229
7230fn pending_is_empty(ctx: &DecompressionCtx) -> bool {
7232 pending_len(ctx) == 0
7233}
7234
7235fn compact_input(ctx: &mut DecompressionCtx) {
7239 if ctx.pos > 0 {
7240 if ctx.pos >= ctx.input.len() {
7241 ctx.input.clear();
7242 } else {
7243 ctx.input.drain(..ctx.pos);
7244 }
7245 ctx.pos = 0;
7246 }
7247}
7248
7249fn consumed_from_call(done: bool, src_size: usize, remaining_input_len: usize) -> usize {
7253 if !done {
7254 return src_size;
7255 }
7256 let remaining_from_call = cmp::min(remaining_input_len, src_size);
7257 src_size.saturating_sub(remaining_from_call)
7258}
7259
7260fn expected_frame_header_len(src: &[u8]) -> Option<usize> {
7265 if src.len() < 5 || src[..4] != LZ4F_MAGIC {
7266 return None;
7267 }
7268 let flg = src[4];
7269 let mut len = 7;
7270 if flg & 0x08 != 0 {
7271 len += 8;
7272 }
7273 if flg & 0x01 != 0 {
7274 len += 4;
7275 }
7276 Some(len)
7277}
7278
7279fn frame_hint(ctx: &DecompressionCtx) -> usize {
7286 if !ctx.parsed_header {
7287 let available = ctx.input.len().saturating_sub(ctx.pos);
7288 let expected = if available >= 5 {
7289 expected_frame_header_len(&ctx.input[ctx.pos..]).unwrap_or(7)
7290 } else {
7291 7
7292 };
7293 expected.saturating_sub(available)
7294 } else if ctx.raw_block_remaining > 0 {
7295 ctx.raw_block_remaining + if ctx.block_checksum { 4 } else { 0 } + 4
7296 } else if pending_is_empty(ctx) {
7297 let available = ctx.input.len().saturating_sub(ctx.pos);
7298 if available < 4 {
7299 return 4 - available;
7300 }
7301 let block_header = u32::from_le_bytes(ctx.input[ctx.pos..ctx.pos + 4].try_into().unwrap());
7302 let block_len = (block_header & 0x7FFF_FFFF) as usize;
7303 let needed = if block_len == 0 {
7304 4 + if ctx.content_checksum { 4 } else { 0 }
7305 } else {
7306 4 + block_len + if ctx.block_checksum { 4 } else { 0 }
7307 };
7308 cmp::max(needed.saturating_sub(available), 1)
7309 } else {
7310 1
7311 }
7312}
7313
7314fn decompression_free_status(ctx: &DecompressionCtx) -> usize {
7315 if ctx.done || (!ctx.parsed_header && ctx.input.is_empty() && pending_is_empty(ctx)) {
7316 return 0;
7317 }
7318 if !ctx.parsed_header {
7319 return if ctx.input.is_empty() { 0 } else { 1 };
7320 }
7321 if !pending_is_empty(ctx) {
7322 return 9;
7323 }
7324 if ctx.raw_block_remaining > 0 {
7325 return 5;
7326 }
7327 3
7328}
7329
7330fn block_size_enum(id: u8) -> BlockSize {
7333 match id {
7334 5 => BlockSize::Max256KB,
7335 6 => BlockSize::Max1MB,
7336 7 => BlockSize::Max4MB,
7337 _ => BlockSize::Max64KB,
7338 }
7339}
7340
7341fn block_max_size(id: u8) -> usize {
7344 match id {
7345 5 => 256 * 1024,
7346 6 => 1024 * 1024,
7347 7 => 4 * 1024 * 1024,
7348 _ => 64 * 1024,
7349 }
7350}
7351
7352fn xxhash32(input: &[u8], seed: u32) -> u32 {
7357 let mut h = XxHash32::new(seed);
7358 h.update(input);
7359 h.digest()
7360}
7361
7362#[derive(Clone, Copy, Debug)]
7363struct XxHash32 {
7364 total: usize,
7365 seed: u32,
7366 v1: u32,
7367 v2: u32,
7368 v3: u32,
7369 v4: u32,
7370 mem: [u8; 16],
7371 mem_len: usize,
7372}
7373
7374impl XxHash32 {
7375 fn new(seed: u32) -> Self {
7376 Self {
7377 total: 0,
7378 seed,
7379 v1: seed.wrapping_add(0x9E37_79B1).wrapping_add(0x85EB_CA77),
7380 v2: seed.wrapping_add(0x85EB_CA77),
7381 v3: seed,
7382 v4: seed.wrapping_sub(0x9E37_79B1),
7383 mem: [0; 16],
7384 mem_len: 0,
7385 }
7386 }
7387
7388 fn update(&mut self, mut input: &[u8]) {
7389 self.total += input.len();
7390 if self.mem_len + input.len() < 16 {
7391 self.mem[self.mem_len..self.mem_len + input.len()].copy_from_slice(input);
7392 self.mem_len += input.len();
7393 return;
7394 }
7395 if self.mem_len > 0 {
7396 let fill = 16 - self.mem_len;
7397 self.mem[self.mem_len..16].copy_from_slice(&input[..fill]);
7398 let block = self.mem;
7399 self.process(&block);
7400 input = &input[fill..];
7401 self.mem_len = 0;
7402 }
7403 let mut v1 = self.v1;
7404 let mut v2 = self.v2;
7405 let mut v3 = self.v3;
7406 let mut v4 = self.v4;
7407 let mut p = input.as_ptr();
7408 let end = unsafe { p.add(input.len() & !15) };
7409 while p < end {
7410 v1 = round(v1, read_u32_ptr(p));
7411 v2 = round(v2, read_u32_ptr(unsafe { p.add(4) }));
7412 v3 = round(v3, read_u32_ptr(unsafe { p.add(8) }));
7413 v4 = round(v4, read_u32_ptr(unsafe { p.add(12) }));
7414 p = unsafe { p.add(16) };
7415 }
7416 self.v1 = v1;
7417 self.v2 = v2;
7418 self.v3 = v3;
7419 self.v4 = v4;
7420 let consumed = input.len() & !15;
7421 input = &input[consumed..];
7422 self.mem[..input.len()].copy_from_slice(input);
7423 self.mem_len = input.len();
7424 }
7425
7426 fn digest(&self) -> u32 {
7427 let mut h = if self.total >= 16 {
7428 self.v1
7429 .rotate_left(1)
7430 .wrapping_add(self.v2.rotate_left(7))
7431 .wrapping_add(self.v3.rotate_left(12))
7432 .wrapping_add(self.v4.rotate_left(18))
7433 } else {
7434 self.seed.wrapping_add(0x1656_67B1)
7435 };
7436 h = h.wrapping_add(self.total as u32);
7437 let mut p = &self.mem[..self.mem_len];
7438 while p.len() >= 4 {
7439 h = h
7440 .wrapping_add(read_u32(p).wrapping_mul(0xC2B2_AE3D))
7441 .rotate_left(17)
7442 .wrapping_mul(0x27D4_EB2F);
7443 p = &p[4..];
7444 }
7445 for &b in p {
7446 h = h
7447 .wrapping_add((b as u32).wrapping_mul(0x1656_67B1))
7448 .rotate_left(11)
7449 .wrapping_mul(0x9E37_79B1);
7450 }
7451 h ^= h >> 15;
7452 h = h.wrapping_mul(0x85EB_CA77);
7453 h ^= h >> 13;
7454 h = h.wrapping_mul(0xC2B2_AE3D);
7455 h ^ (h >> 16)
7456 }
7457
7458 fn process(&mut self, block: &[u8]) {
7459 self.v1 = round(self.v1, read_u32(&block[0..]));
7460 self.v2 = round(self.v2, read_u32(&block[4..]));
7461 self.v3 = round(self.v3, read_u32(&block[8..]));
7462 self.v4 = round(self.v4, read_u32(&block[12..]));
7463 }
7464}
7465
7466fn round(acc: u32, input: u32) -> u32 {
7470 acc.wrapping_add(input.wrapping_mul(0x85EB_CA77))
7471 .rotate_left(13)
7472 .wrapping_mul(0x9E37_79B1)
7473}
7474
7475#[inline]
7479fn read_u32(input: &[u8]) -> u32 {
7480 unsafe { ptr::read_unaligned(input.as_ptr() as *const u32).to_le() }
7481}
7482
7483#[inline]
7487fn read_u16(input: &[u8]) -> u16 {
7488 unsafe { ptr::read_unaligned(input.as_ptr() as *const u16).to_le() }
7489}
7490
7491#[inline]
7494fn read_u16_ptr(input: *const u8) -> u16 {
7495 unsafe { ptr::read_unaligned(input as *const u16).to_le() }
7496}
7497
7498#[inline]
7501fn read_u32_ptr(input: *const u8) -> u32 {
7502 unsafe { ptr::read_unaligned(input as *const u32).to_le() }
7503}
7504
7505#[inline]
7509fn read_u64(input: &[u8]) -> u64 {
7510 unsafe { ptr::read_unaligned(input.as_ptr() as *const u64).to_le() }
7511}
7512
7513#[inline]
7516fn read_u64_ptr(input: *const u8) -> u64 {
7517 unsafe { ptr::read_unaligned(input as *const u64).to_le() }
7518}
7519
7520#[cfg(test)]
7521mod tests {
7522 use super::*;
7523
7524 #[test]
7525 fn frame_error_code_mapping_matches_upstream_numbers() {
7526 unsafe {
7527 assert_eq!(LZ4F_isError(0), 0);
7528 assert_eq!(LZ4F_getErrorCode(0), 0);
7529 assert_eq!(
7530 std::ffi::CStr::from_ptr(LZ4F_getErrorName(0)).to_bytes(),
7531 b"Unspecified error code"
7532 );
7533
7534 let cases = [
7535 (ERROR_GENERIC, 1, b"ERROR_GENERIC".as_slice()),
7536 (
7537 ERROR_MAX_BLOCK_SIZE_INVALID,
7538 2,
7539 b"ERROR_maxBlockSize_invalid".as_slice(),
7540 ),
7541 (
7542 ERROR_BLOCK_MODE_INVALID,
7543 3,
7544 b"ERROR_blockMode_invalid".as_slice(),
7545 ),
7546 (
7547 ERROR_PARAMETER_INVALID,
7548 4,
7549 b"ERROR_parameter_invalid".as_slice(),
7550 ),
7551 (
7552 ERROR_COMPRESSION_LEVEL_INVALID,
7553 5,
7554 b"ERROR_compressionLevel_invalid".as_slice(),
7555 ),
7556 (
7557 ERROR_HEADER_VERSION_WRONG,
7558 6,
7559 b"ERROR_headerVersion_wrong".as_slice(),
7560 ),
7561 (
7562 ERROR_BLOCK_CHECKSUM_INVALID,
7563 7,
7564 b"ERROR_blockChecksum_invalid".as_slice(),
7565 ),
7566 (
7567 ERROR_RESERVED_FLAG_SET,
7568 8,
7569 b"ERROR_reservedFlag_set".as_slice(),
7570 ),
7571 (
7572 ERROR_ALLOCATION_FAILED,
7573 9,
7574 b"ERROR_allocation_failed".as_slice(),
7575 ),
7576 (
7577 ERROR_SRC_SIZE_TOO_LARGE,
7578 10,
7579 b"ERROR_srcSize_tooLarge".as_slice(),
7580 ),
7581 (
7582 ERROR_DST_TOO_SMALL,
7583 11,
7584 b"ERROR_dstMaxSize_tooSmall".as_slice(),
7585 ),
7586 (
7587 ERROR_BAD_HEADER,
7588 12,
7589 b"ERROR_frameHeader_incomplete".as_slice(),
7590 ),
7591 (
7592 ERROR_FRAME_SIZE_WRONG,
7593 14,
7594 b"ERROR_frameSize_wrong".as_slice(),
7595 ),
7596 (
7597 ERROR_FRAME_TYPE_UNKNOWN,
7598 13,
7599 b"ERROR_frameType_unknown".as_slice(),
7600 ),
7601 (ERROR_SRC_PTR_WRONG, 15, b"ERROR_srcPtr_wrong".as_slice()),
7602 (
7603 ERROR_DECOMPRESSION_FAILED,
7604 16,
7605 b"ERROR_decompressionFailed".as_slice(),
7606 ),
7607 (
7608 ERROR_HEADER_CHECKSUM_INVALID,
7609 17,
7610 b"ERROR_headerChecksum_invalid".as_slice(),
7611 ),
7612 (
7613 ERROR_CHECKSUM_INVALID,
7614 18,
7615 b"ERROR_contentChecksum_invalid".as_slice(),
7616 ),
7617 (
7618 ERROR_FRAME_DECODING_ALREADY_STARTED,
7619 19,
7620 b"ERROR_frameDecoding_alreadyStarted".as_slice(),
7621 ),
7622 (
7623 ERROR_COMPRESSION_STATE_UNINITIALIZED,
7624 20,
7625 b"ERROR_compressionState_uninitialized".as_slice(),
7626 ),
7627 (ERROR_PARAMETER_NULL, 21, b"ERROR_parameter_null".as_slice()),
7628 (ERROR_IO_WRITE, 22, b"ERROR_io_write".as_slice()),
7629 (ERROR_IO_READ, 23, b"ERROR_io_read".as_slice()),
7630 ];
7631 for (value, code, name) in cases {
7632 assert_eq!(LZ4F_isError(value), 1);
7633 assert_eq!(LZ4F_getErrorCode(value), code);
7634 assert_eq!(
7635 std::ffi::CStr::from_ptr(LZ4F_getErrorName(value)).to_bytes(),
7636 name
7637 );
7638 }
7639 }
7640 }
7641
7642 #[test]
7643 fn frame_default_block_size_id_maps_to_64kb() {
7644 assert_eq!(LZ4F_getBlockSize(0), 64 * 1024);
7645 assert_eq!(LZ4F_getBlockSize(4), 64 * 1024);
7646 assert_eq!(LZ4F_getBlockSize(3), ERROR_MAX_BLOCK_SIZE_INVALID);
7647 }
7648
7649 #[test]
7650 fn frame_compression_state_errors_match_upstream() {
7651 unsafe {
7652 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
7653 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
7654 let input = b"not started";
7655 let mut output = vec![0u8; 128];
7656
7657 assert_eq!(
7658 LZ4F_compressUpdate(
7659 cctx,
7660 output.as_mut_ptr(),
7661 output.len(),
7662 input.as_ptr(),
7663 input.len(),
7664 ptr::null(),
7665 ),
7666 ERROR_COMPRESSION_STATE_UNINITIALIZED
7667 );
7668 assert_eq!(
7669 LZ4F_uncompressedUpdate(
7670 cctx,
7671 output.as_mut_ptr() as *mut c_void,
7672 output.len(),
7673 input.as_ptr() as *const c_void,
7674 input.len(),
7675 ptr::null(),
7676 ),
7677 ERROR_COMPRESSION_STATE_UNINITIALIZED
7678 );
7679 assert_eq!(
7680 LZ4F_flush(cctx, output.as_mut_ptr(), output.len(), ptr::null()),
7681 ERROR_COMPRESSION_STATE_UNINITIALIZED
7682 );
7683 assert_eq!(
7684 LZ4F_compressEnd(cctx, output.as_mut_ptr(), output.len(), ptr::null()),
7685 ERROR_COMPRESSION_STATE_UNINITIALIZED
7686 );
7687
7688 let header_len =
7689 LZ4F_compressBegin(cctx, output.as_mut_ptr(), output.len(), ptr::null());
7690 assert_eq!(LZ4F_isError(header_len), 0);
7691 assert_eq!(
7692 LZ4F_compressEnd(
7693 cctx,
7694 output.as_mut_ptr().add(header_len),
7695 output.len() - header_len,
7696 ptr::null(),
7697 ),
7698 4
7699 );
7700 assert_eq!(
7701 LZ4F_compressEnd(cctx, output.as_mut_ptr(), output.len(), ptr::null()),
7702 ERROR_COMPRESSION_STATE_UNINITIALIZED
7703 );
7704 LZ4F_freeCompressionContext(cctx);
7705 }
7706 }
7707
7708 #[test]
7709 fn block_round_trip_repeated() {
7710 let input = b"Some data Some data Some data Some data";
7711 let mut compressed = vec![0u8; unsafe { LZ4_compressBound(input.len() as c_int) } as usize];
7712 let clen = unsafe {
7713 LZ4_compress_default(
7714 input.as_ptr() as *const c_char,
7715 compressed.as_mut_ptr() as *mut c_char,
7716 input.len() as c_int,
7717 compressed.len() as c_int,
7718 )
7719 };
7720 assert!(clen > 0);
7721 let mut output = vec![0u8; input.len()];
7722 let olen = unsafe {
7723 LZ4_decompress_safe(
7724 compressed.as_ptr() as *const c_char,
7725 output.as_mut_ptr() as *mut c_char,
7726 clen,
7727 output.len() as c_int,
7728 )
7729 };
7730 assert_eq!(olen as usize, input.len());
7731 assert_eq!(&output, input);
7732 }
7733
7734 #[test]
7735 fn block_fast_accepts_null_empty_source_and_rejects_oversize() {
7736 unsafe {
7737 let mut dst = [0u8; 16];
7738 let empty_len = LZ4_compress_fast(
7739 ptr::null(),
7740 dst.as_mut_ptr() as *mut c_char,
7741 0,
7742 dst.len() as c_int,
7743 1,
7744 );
7745 assert_eq!(empty_len, 1);
7746 assert_eq!(dst[0], 0);
7747
7748 assert_eq!(
7749 LZ4_compress_fast(
7750 ptr::null(),
7751 dst.as_mut_ptr() as *mut c_char,
7752 1,
7753 dst.len() as c_int,
7754 1,
7755 ),
7756 0
7757 );
7758
7759 let src = [0u8; 1];
7760 assert_eq!(
7761 LZ4_compress_fast(
7762 src.as_ptr() as *const c_char,
7763 dst.as_mut_ptr() as *mut c_char,
7764 LZ4_MAX_INPUT_SIZE + 1,
7765 dst.len() as c_int,
7766 1,
7767 ),
7768 0
7769 );
7770 }
7771 }
7772
7773 #[test]
7774 fn acceleration_is_clamped_to_upstream_range() {
7775 assert_eq!(normalize_acceleration(c_int::MIN), 1);
7776 assert_eq!(normalize_acceleration(0), 1);
7777 assert_eq!(normalize_acceleration(4), 4);
7778 assert_eq!(normalize_acceleration(65_537), 65_537);
7779 assert_eq!(normalize_acceleration(65_538), 65_537);
7780 assert_eq!(normalize_acceleration(c_int::MAX), 65_537);
7781 }
7782
7783 #[test]
7784 fn block_dest_size_and_ext_state_round_trip() {
7785 let input = b"dest-size-data-".repeat(2048);
7786 let bound = unsafe { LZ4_compressBound(input.len() as c_int) } as usize;
7787 let mut state = vec![0u8; LZ4_sizeofState() as usize];
7788 let mut compressed = vec![0u8; bound];
7789 let compressed_len = unsafe {
7790 LZ4_compress_fast_extState(
7791 state.as_mut_ptr() as *mut c_void,
7792 input.as_ptr() as *const c_char,
7793 compressed.as_mut_ptr() as *mut c_char,
7794 input.len() as c_int,
7795 compressed.len() as c_int,
7796 1,
7797 )
7798 };
7799 assert!(compressed_len > 0);
7800 assert!(state.iter().any(|&b| b != 0));
7801
7802 let mut output = vec![0u8; input.len()];
7803 let output_len = unsafe {
7804 LZ4_decompress_safe(
7805 compressed.as_ptr() as *const c_char,
7806 output.as_mut_ptr() as *mut c_char,
7807 compressed_len,
7808 output.len() as c_int,
7809 )
7810 };
7811 assert_eq!(output_len as usize, input.len());
7812 assert_eq!(output, input);
7813
7814 let mut src_size = input.len() as c_int;
7815 let mut tiny = vec![0u8; compressed_len as usize / 2];
7816 let tiny_len = unsafe {
7817 LZ4_compress_destSize(
7818 input.as_ptr() as *const c_char,
7819 tiny.as_mut_ptr() as *mut c_char,
7820 &mut src_size,
7821 tiny.len() as c_int,
7822 )
7823 };
7824 assert!(tiny_len > 0);
7825 assert!(src_size > 0);
7826 assert!(src_size < input.len() as c_int);
7827
7828 let mut dest_state = vec![0u8; LZ4_sizeofState() as usize];
7829 src_size = input.len() as c_int;
7830 let null_state_len = unsafe {
7831 LZ4_compress_destSize_extState(
7832 std::ptr::null_mut(),
7833 input.as_ptr() as *const c_char,
7834 tiny.as_mut_ptr() as *mut c_char,
7835 &mut src_size,
7836 tiny.len() as c_int,
7837 1,
7838 )
7839 };
7840 assert_eq!(null_state_len, 0);
7841
7842 let mut fast_accel4 = vec![0u8; bound];
7843 let fast_accel4_len = unsafe {
7844 LZ4_compress_fast(
7845 input.as_ptr() as *const c_char,
7846 fast_accel4.as_mut_ptr() as *mut c_char,
7847 input.len() as c_int,
7848 fast_accel4.len() as c_int,
7849 4,
7850 )
7851 };
7852 assert!(fast_accel4_len > 0);
7853
7854 let mut dest_accel4 = vec![0u8; bound];
7855 src_size = input.len() as c_int;
7856 let dest_accel4_len = unsafe {
7857 LZ4_compress_destSize_extState(
7858 dest_state.as_mut_ptr() as *mut c_void,
7859 input.as_ptr() as *const c_char,
7860 dest_accel4.as_mut_ptr() as *mut c_char,
7861 &mut src_size,
7862 dest_accel4.len() as c_int,
7863 4,
7864 )
7865 };
7866 assert_eq!(src_size as usize, input.len());
7867 assert_eq!(dest_accel4_len, fast_accel4_len);
7868 assert!(dest_state.iter().any(|&b| b != 0));
7869 assert_eq!(
7870 &dest_accel4[..dest_accel4_len as usize],
7871 &fast_accel4[..fast_accel4_len as usize]
7872 );
7873
7874 let mut fast_reset = vec![0u8; bound];
7875 let fast_reset_len = unsafe {
7876 LZ4_compress_fast_extState_fastReset(
7877 state.as_mut_ptr() as *mut c_void,
7878 input.as_ptr() as *const c_char,
7879 fast_reset.as_mut_ptr() as *mut c_char,
7880 input.len() as c_int,
7881 fast_reset.len() as c_int,
7882 4,
7883 )
7884 };
7885 assert_eq!(fast_reset_len, fast_accel4_len);
7886 assert_eq!(
7887 &fast_reset[..fast_reset_len as usize],
7888 &fast_accel4[..fast_accel4_len as usize]
7889 );
7890
7891 let mut hc_state = vec![0u8; LZ4_sizeofStateHC() as usize];
7892 let hc_stream =
7893 unsafe { LZ4_initStreamHC(hc_state.as_mut_ptr() as *mut c_void, hc_state.len()) };
7894 assert!(!hc_stream.is_null());
7895 let mut hc_fast_reset = vec![0u8; bound];
7896 let hc_fast_reset_len = unsafe {
7897 LZ4_compress_HC_extStateHC_fastReset(
7898 hc_state.as_mut_ptr() as *mut c_void,
7899 input.as_ptr() as *const c_char,
7900 hc_fast_reset.as_mut_ptr() as *mut c_char,
7901 input.len() as c_int,
7902 hc_fast_reset.len() as c_int,
7903 9,
7904 )
7905 };
7906 assert!(hc_fast_reset_len > 0);
7907 output.fill(0);
7908 let hc_output_len = unsafe {
7909 LZ4_decompress_safe(
7910 hc_fast_reset.as_ptr() as *const c_char,
7911 output.as_mut_ptr() as *mut c_char,
7912 hc_fast_reset_len,
7913 output.len() as c_int,
7914 )
7915 };
7916 assert_eq!(hc_output_len as usize, input.len());
7917 assert_eq!(output, input);
7918 }
7919
7920 #[test]
7921 fn fast_stream_compression_references_loaded_dictionary() {
7922 unsafe {
7923 let dict = b"abcdefghijklmnop";
7924 let input = b"abcdefghijklmnop";
7925 let stream = LZ4_createStream();
7926 assert!(!stream.is_null());
7927 assert_eq!(
7928 LZ4_loadDict(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
7929 dict.len() as c_int
7930 );
7931 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
7932 let compressed_len = LZ4_compress_continue(
7933 stream,
7934 input.as_ptr() as *const c_char,
7935 compressed.as_mut_ptr() as *mut c_char,
7936 input.len() as c_int,
7937 compressed.len() as c_int,
7938 );
7939 assert!(compressed_len > 0);
7940 assert!((compressed_len as usize) < input.len() + 1);
7941
7942 let mut saved = vec![0u8; dict.len() + input.len()];
7943 let saved_len = LZ4_saveDict(
7944 stream,
7945 saved.as_mut_ptr() as *mut c_char,
7946 saved.len() as c_int,
7947 );
7948 assert!(saved_len >= input.len() as c_int);
7949 LZ4_freeStream(stream);
7950
7951 let mut output = vec![0u8; input.len()];
7952 let output_len = LZ4_decompress_safe_usingDict(
7953 compressed.as_ptr() as *const c_char,
7954 output.as_mut_ptr() as *mut c_char,
7955 compressed_len,
7956 output.len() as c_int,
7957 dict.as_ptr() as *const c_char,
7958 dict.len() as c_int,
7959 );
7960 assert_eq!(output_len as usize, input.len());
7961 assert_eq!(output, input);
7962 }
7963 }
7964
7965 #[test]
7966 fn fast_stream_init_and_tiny_load_dict_match_upstream() {
7967 unsafe {
7968 let mut stream_storage = vec![0u8; LZ4_sizeofStreamState() as usize];
7969 assert!(LZ4_initStream(ptr::null_mut(), stream_storage.len()).is_null());
7970 assert!(LZ4_initStream(stream_storage.as_mut_ptr() as *mut c_void, 1).is_null());
7971 let stream = LZ4_initStream(
7972 stream_storage.as_mut_ptr() as *mut c_void,
7973 stream_storage.len(),
7974 );
7975 assert!(!stream.is_null());
7976
7977 let dict = b"abcdefghijklmnop";
7978 for len in 0..HASH_UNIT {
7979 assert_eq!(
7980 LZ4_loadDict(stream, dict.as_ptr() as *const c_char, len as c_int),
7981 0,
7982 "{len}"
7983 );
7984 assert_eq!(
7985 LZ4_loadDictSlow(stream, dict.as_ptr() as *const c_char, len as c_int),
7986 0,
7987 "{len}"
7988 );
7989 }
7990 assert_eq!(
7991 LZ4_loadDict(stream, dict.as_ptr() as *const c_char, HASH_UNIT as c_int),
7992 HASH_UNIT as c_int
7993 );
7994
7995 let mut hc_storage = vec![0u8; LZ4_sizeofStreamStateHC() as usize];
7996 assert!(LZ4_initStreamHC(ptr::null_mut(), hc_storage.len()).is_null());
7997 assert!(LZ4_initStreamHC(hc_storage.as_mut_ptr() as *mut c_void, 1).is_null());
7998 let hc_stream =
7999 LZ4_initStreamHC(hc_storage.as_mut_ptr() as *mut c_void, hc_storage.len());
8000 assert!(!hc_stream.is_null());
8001 for len in 0..HASH_UNIT {
8002 assert_eq!(
8003 LZ4_loadDictHC(hc_stream, dict.as_ptr() as *const c_char, len as c_int),
8004 len as c_int,
8005 "{len}"
8006 );
8007 }
8008 }
8009 }
8010
8011 #[test]
8012 fn hc_save_dict_treats_tiny_requested_dictionary_as_empty() {
8013 unsafe {
8014 let dict = b"abcdefghijklmnop";
8015 let stream = LZ4_createStreamHC();
8016 assert!(!stream.is_null());
8017 assert_eq!(
8018 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
8019 dict.len() as c_int
8020 );
8021
8022 let mut saved = [0u8; 16];
8023 assert_eq!(
8024 LZ4_saveDictHC(stream, saved.as_mut_ptr() as *mut c_char, 3),
8025 0
8026 );
8027 assert_eq!(
8028 LZ4_saveDictHC(stream, saved.as_mut_ptr() as *mut c_char, 4),
8029 0
8030 );
8031
8032 assert_eq!(
8033 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
8034 dict.len() as c_int
8035 );
8036 assert_eq!(
8037 LZ4_saveDictHC(stream, saved.as_mut_ptr() as *mut c_char, 4),
8038 4
8039 );
8040 assert_eq!(&saved[..4], &dict[dict.len() - 4..]);
8041 LZ4_freeStreamHC(stream);
8042 }
8043 }
8044
8045 #[test]
8046 fn decoder_ring_buffer_size_matches_upstream_boundaries() {
8047 let overhead = (LZ4_DISTANCE_MAX + 1 + 14) as c_int;
8048 assert_eq!(LZ4_decoderRingBufferSize(-1), 0);
8049 assert_eq!(LZ4_decoderRingBufferSize(0), overhead + 16);
8050 assert_eq!(LZ4_decoderRingBufferSize(15), overhead + 16);
8051 assert_eq!(LZ4_decoderRingBufferSize(16), overhead + 16);
8052 assert_eq!(LZ4_decoderRingBufferSize(64 * 1024), overhead + 64 * 1024);
8053 assert_eq!(
8054 LZ4_decoderRingBufferSize(LZ4_MAX_INPUT_SIZE),
8055 overhead + LZ4_MAX_INPUT_SIZE
8056 );
8057 assert_eq!(LZ4_decoderRingBufferSize(LZ4_MAX_INPUT_SIZE + 1), 0);
8058 }
8059
8060 #[test]
8061 fn fast_attach_dictionary_is_cleared_after_first_compression() {
8062 unsafe {
8063 let dict = b"abcdefghijklmnop0123456789";
8064 let first = b"zzzzzzzzzzzzzzzz";
8065 let second = b"abcdefghijklmnop0123456789";
8066
8067 let dict_stream = LZ4_createStream();
8068 let work_stream = LZ4_createStream();
8069 assert!(!dict_stream.is_null());
8070 assert!(!work_stream.is_null());
8071 assert_eq!(
8072 LZ4_loadDict(
8073 dict_stream,
8074 dict.as_ptr() as *const c_char,
8075 dict.len() as c_int
8076 ),
8077 dict.len() as c_int
8078 );
8079 LZ4_attach_dictionary(work_stream, dict_stream);
8080
8081 let mut first_compressed = vec![0u8; LZ4_compressBound(first.len() as c_int) as usize];
8082 let first_len = LZ4_compress_fast_continue(
8083 work_stream,
8084 first.as_ptr() as *const c_char,
8085 first_compressed.as_mut_ptr() as *mut c_char,
8086 first.len() as c_int,
8087 first_compressed.len() as c_int,
8088 1,
8089 );
8090 assert!(first_len > 0);
8091
8092 let mut second_compressed =
8093 vec![0u8; LZ4_compressBound(second.len() as c_int) as usize];
8094 let second_len = LZ4_compress_fast_continue(
8095 work_stream,
8096 second.as_ptr() as *const c_char,
8097 second_compressed.as_mut_ptr() as *mut c_char,
8098 second.len() as c_int,
8099 second_compressed.len() as c_int,
8100 1,
8101 );
8102 assert!(second_len > 0);
8103
8104 let mut output = vec![0u8; second.len()];
8105 let output_len = LZ4_decompress_safe(
8106 second_compressed.as_ptr() as *const c_char,
8107 output.as_mut_ptr() as *mut c_char,
8108 second_len,
8109 output.len() as c_int,
8110 );
8111 assert_eq!(output_len, second.len() as c_int);
8112 assert_eq!(output, second);
8113
8114 LZ4_freeStream(dict_stream);
8115 LZ4_freeStream(work_stream);
8116 }
8117 }
8118
8119 #[test]
8120 fn hc_attach_dictionary_is_cleared_after_first_compression() {
8121 unsafe {
8122 let dict = b"abcdefghijklmnop0123456789";
8123 let first = b"zzzzzzzzzzzzzzzz";
8124 let second = b"abcdefghijklmnop0123456789";
8125
8126 let dict_stream = LZ4_createStreamHC();
8127 let work_stream = LZ4_createStreamHC();
8128 assert!(!dict_stream.is_null());
8129 assert!(!work_stream.is_null());
8130 assert_eq!(
8131 LZ4_loadDictHC(
8132 dict_stream,
8133 dict.as_ptr() as *const c_char,
8134 dict.len() as c_int
8135 ),
8136 dict.len() as c_int
8137 );
8138 LZ4_attach_HC_dictionary(work_stream, dict_stream);
8139
8140 let mut first_compressed = vec![0u8; LZ4_compressBound(first.len() as c_int) as usize];
8141 let first_len = LZ4_compress_HC_continue(
8142 work_stream,
8143 first.as_ptr() as *const c_char,
8144 first_compressed.as_mut_ptr() as *mut c_char,
8145 first.len() as c_int,
8146 first_compressed.len() as c_int,
8147 );
8148 assert!(first_len > 0);
8149
8150 let mut second_compressed =
8151 vec![0u8; LZ4_compressBound(second.len() as c_int) as usize];
8152 let second_len = LZ4_compress_HC_continue(
8153 work_stream,
8154 second.as_ptr() as *const c_char,
8155 second_compressed.as_mut_ptr() as *mut c_char,
8156 second.len() as c_int,
8157 second_compressed.len() as c_int,
8158 );
8159 assert!(second_len > 0);
8160
8161 let mut output = vec![0u8; second.len()];
8162 let output_len = LZ4_decompress_safe_usingDict(
8163 second_compressed.as_ptr() as *const c_char,
8164 output.as_mut_ptr() as *mut c_char,
8165 second_len,
8166 output.len() as c_int,
8167 first.as_ptr() as *const c_char,
8168 first.len() as c_int,
8169 );
8170 assert_eq!(output_len, second.len() as c_int);
8171 assert_eq!(output, second);
8172
8173 LZ4_freeStreamHC(dict_stream);
8174 LZ4_freeStreamHC(work_stream);
8175 }
8176 }
8177
8178 #[test]
8179 fn hc_deprecated_continue_uses_stream_dictionary_and_updates_history() {
8180 unsafe {
8181 let dict = b"abcdefghijklmnop0123456789";
8182 let input = b"abcdefghijklmnop0123456789";
8183 let stream = LZ4_createStreamHC();
8184 assert!(!stream.is_null());
8185 assert_eq!(
8186 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
8187 dict.len() as c_int
8188 );
8189
8190 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
8191 let compressed_len = LZ4_compressHC2_continue(
8192 stream as *mut c_void,
8193 input.as_ptr() as *const c_char,
8194 compressed.as_mut_ptr() as *mut c_char,
8195 input.len() as c_int,
8196 9,
8197 );
8198 assert!(compressed_len > 0);
8199 assert!((compressed_len as usize) < input.len());
8200
8201 let mut output = vec![0u8; input.len()];
8202 let output_len = LZ4_decompress_safe_usingDict(
8203 compressed.as_ptr() as *const c_char,
8204 output.as_mut_ptr() as *mut c_char,
8205 compressed_len,
8206 output.len() as c_int,
8207 dict.as_ptr() as *const c_char,
8208 dict.len() as c_int,
8209 );
8210 assert_eq!(output_len, input.len() as c_int);
8211 assert_eq!(output, input);
8212
8213 let mut saved = [0u8; 64];
8214 let saved_len = LZ4_saveDictHC(
8215 stream,
8216 saved.as_mut_ptr() as *mut c_char,
8217 saved.len() as c_int,
8218 );
8219 assert!(saved_len >= input.len() as c_int);
8220 let saved_len = saved_len as usize;
8221 assert_eq!(&saved[saved_len - input.len()..saved_len], input);
8222 LZ4_freeStreamHC(stream);
8223 }
8224 }
8225
8226 #[test]
8227 fn hc_favor_decompression_speed_skips_tiny_offsets() {
8228 unsafe {
8229 let input = vec![b'a'; 4096];
8230 let bound = LZ4_compressBound(input.len() as c_int);
8231 let stream = LZ4_createStreamHC();
8232 assert!(!stream.is_null());
8233 LZ4_setCompressionLevel(stream, 10);
8234
8235 let mut normal = vec![0u8; bound as usize];
8236 let normal_len = LZ4_compress_HC_continue(
8237 stream,
8238 input.as_ptr() as *const c_char,
8239 normal.as_mut_ptr() as *mut c_char,
8240 input.len() as c_int,
8241 normal.len() as c_int,
8242 );
8243 assert!(normal_len > 0);
8244 assert!(block_has_offset_below(&normal[..normal_len as usize], 8));
8245
8246 LZ4_resetStreamHC_fast(stream, 10);
8247 LZ4_favorDecompressionSpeed(stream, 1);
8248 let mut favored = vec![0u8; bound as usize];
8249 let favored_len = LZ4_compress_HC_continue(
8250 stream,
8251 input.as_ptr() as *const c_char,
8252 favored.as_mut_ptr() as *mut c_char,
8253 input.len() as c_int,
8254 favored.len() as c_int,
8255 );
8256 assert!(favored_len > 0);
8257 assert!(favored_len >= normal_len);
8258 assert!(!block_has_offset_below(&favored[..favored_len as usize], 8));
8259
8260 LZ4_resetStreamHC(stream, 10);
8261 let mut reset = vec![0u8; bound as usize];
8262 let reset_len = LZ4_compress_HC_continue(
8263 stream,
8264 input.as_ptr() as *const c_char,
8265 reset.as_mut_ptr() as *mut c_char,
8266 input.len() as c_int,
8267 reset.len() as c_int,
8268 );
8269 LZ4_freeStreamHC(stream);
8270
8271 assert_eq!(reset_len, normal_len);
8272 assert_eq!(&reset[..reset_len as usize], &normal[..normal_len as usize]);
8273 assert!(block_has_offset_below(&reset[..reset_len as usize], 8));
8274
8275 let mut output = vec![0u8; input.len()];
8276 let output_len = LZ4_decompress_safe(
8277 favored.as_ptr() as *const c_char,
8278 output.as_mut_ptr() as *mut c_char,
8279 favored_len,
8280 output.len() as c_int,
8281 );
8282 assert_eq!(output_len as usize, input.len());
8283 assert_eq!(output, input);
8284 }
8285 }
8286
8287 #[test]
8288 fn fast_obsolete_slide_input_buffer_reports_saved_dictionary() {
8289 unsafe {
8290 let input = b"slide-input-buffer-dictionary".repeat(4);
8291 let stream = LZ4_createStream();
8292 assert!(!stream.is_null());
8293 assert!(LZ4_slideInputBuffer(stream as *mut c_void).is_null());
8294
8295 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
8296 let compressed_len = LZ4_compress_fast_continue(
8297 stream,
8298 input.as_ptr() as *const c_char,
8299 compressed.as_mut_ptr() as *mut c_char,
8300 input.len() as c_int,
8301 compressed.len() as c_int,
8302 1,
8303 );
8304 assert!(compressed_len > 0);
8305
8306 let ptr = LZ4_slideInputBuffer(stream as *mut c_void);
8307 assert!(!ptr.is_null());
8308 let saved = slice::from_raw_parts(ptr as *const u8, input.len());
8309 assert_eq!(saved, input);
8310 LZ4_freeStream(stream);
8311 }
8312 }
8313
8314 #[test]
8315 fn fast_deprecated_state_wrappers_round_trip_and_reset() {
8316 unsafe {
8317 let input = b"deprecated-fast-state-wrapper-".repeat(2048);
8318 let bound = LZ4_compressBound(input.len() as c_int) as usize;
8319 let state_size = LZ4_sizeofStreamState();
8320 assert!(state_size > 0);
8321 let mut state = vec![0u8; state_size as usize];
8322
8323 assert_eq!(
8324 LZ4_resetStreamState(
8325 state.as_mut_ptr() as *mut c_void,
8326 input.as_ptr() as *mut c_char,
8327 ),
8328 0
8329 );
8330 assert!(LZ4_slideInputBuffer(state.as_mut_ptr() as *mut c_void).is_null());
8331
8332 let mut compressed = vec![0u8; bound];
8333 let compressed_len = LZ4_compress_withState(
8334 state.as_mut_ptr() as *mut c_void,
8335 input.as_ptr() as *const c_char,
8336 compressed.as_mut_ptr() as *mut c_char,
8337 input.len() as c_int,
8338 );
8339 assert!(compressed_len > 0);
8340
8341 let mut output = vec![0u8; input.len()];
8342 let output_len = LZ4_decompress_safe(
8343 compressed.as_ptr() as *const c_char,
8344 output.as_mut_ptr() as *mut c_char,
8345 compressed_len,
8346 output.len() as c_int,
8347 );
8348 assert_eq!(output_len as usize, input.len());
8349 assert_eq!(output, input);
8350
8351 assert_eq!(
8352 LZ4_resetStreamState(state.as_mut_ptr() as *mut c_void, ptr::null_mut(),),
8353 0
8354 );
8355 let mut limited = vec![0u8; compressed_len as usize];
8356 let limited_len = LZ4_compress_limitedOutput_withState(
8357 state.as_mut_ptr() as *mut c_void,
8358 input.as_ptr() as *const c_char,
8359 limited.as_mut_ptr() as *mut c_char,
8360 input.len() as c_int,
8361 limited.len() as c_int,
8362 );
8363 assert!(limited_len > 0);
8364
8365 output.fill(0);
8366 let output_len = LZ4_decompress_safe(
8367 limited.as_ptr() as *const c_char,
8368 output.as_mut_ptr() as *mut c_char,
8369 limited_len,
8370 output.len() as c_int,
8371 );
8372 assert_eq!(output_len as usize, input.len());
8373 assert_eq!(output, input);
8374
8375 let obsolete = LZ4_create(ptr::null_mut());
8376 assert!(!obsolete.is_null());
8377 assert_eq!(LZ4_freeStream(obsolete as *mut LZ4StreamEncode), 0);
8378 }
8379 }
8380
8381 #[test]
8382 fn hc_obsolete_slide_input_buffer_clears_history() {
8383 unsafe {
8384 let dict = b"abcdefghijklmnop0123456789";
8385 let stream = LZ4_createStreamHC();
8386 assert!(!stream.is_null());
8387 assert!(LZ4_slideInputBufferHC(stream as *mut c_void).is_null());
8388 assert_eq!(
8389 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
8390 dict.len() as c_int
8391 );
8392
8393 let ptr = LZ4_slideInputBufferHC(stream as *mut c_void);
8394 assert!(!ptr.is_null());
8395
8396 let mut compressed = vec![0u8; LZ4_compressBound(dict.len() as c_int) as usize];
8397 let compressed_len = LZ4_compress_HC_continue(
8398 stream,
8399 dict.as_ptr() as *const c_char,
8400 compressed.as_mut_ptr() as *mut c_char,
8401 dict.len() as c_int,
8402 compressed.len() as c_int,
8403 );
8404 assert!(compressed_len > 0);
8405
8406 let mut output = vec![0u8; dict.len()];
8407 let output_len = LZ4_decompress_safe(
8408 compressed.as_ptr() as *const c_char,
8409 output.as_mut_ptr() as *mut c_char,
8410 compressed_len,
8411 output.len() as c_int,
8412 );
8413 assert_eq!(output_len, dict.len() as c_int);
8414 assert_eq!(output, dict);
8415 LZ4_freeStreamHC(stream);
8416 }
8417 }
8418
8419 #[test]
8420 fn decompress_safe_partial_returns_prefix() {
8421 let input = b"partial output data ".repeat(1024);
8422 let bound = unsafe { LZ4_compressBound(input.len() as c_int) } as usize;
8423 let mut compressed = vec![0u8; bound];
8424 let compressed_len = unsafe {
8425 LZ4_compress_default(
8426 input.as_ptr() as *const c_char,
8427 compressed.as_mut_ptr() as *mut c_char,
8428 input.len() as c_int,
8429 compressed.len() as c_int,
8430 )
8431 };
8432 assert!(compressed_len > 0);
8433 let target = 1234usize;
8434 let mut output = vec![0u8; input.len()];
8435 let output_len = unsafe {
8436 LZ4_decompress_safe_partial(
8437 compressed.as_ptr() as *const c_char,
8438 output.as_mut_ptr() as *mut c_char,
8439 compressed_len,
8440 target as c_int,
8441 output.len() as c_int,
8442 )
8443 };
8444 assert_eq!(output_len as usize, target);
8445 assert_eq!(&output[..target], &input[..target]);
8446 }
8447
8448 #[test]
8449 fn decompress_safe_rejects_block_ending_in_match() {
8450 let compressed = [0x15, b'a', 0x01, 0x00];
8451 let mut output = vec![0u8; 10];
8452
8453 let output_len = unsafe {
8454 LZ4_decompress_safe(
8455 compressed.as_ptr() as *const c_char,
8456 output.as_mut_ptr() as *mut c_char,
8457 compressed.len() as c_int,
8458 output.len() as c_int,
8459 )
8460 };
8461 assert_eq!(output_len, -1);
8462
8463 let dict_compressed = [0x02u8, 0x03, 0x00];
8464 let dict = b"abc";
8465 let mut dict_output = vec![0u8; 6];
8466 let dict_output_len = unsafe {
8467 LZ4_decompress_safe_usingDict(
8468 dict_compressed.as_ptr() as *const c_char,
8469 dict_output.as_mut_ptr() as *mut c_char,
8470 dict_compressed.len() as c_int,
8471 dict_output.len() as c_int,
8472 dict.as_ptr() as *const c_char,
8473 dict.len() as c_int,
8474 )
8475 };
8476 assert_eq!(dict_output_len, -1);
8477 }
8478
8479 #[test]
8480 fn decompress_safe_partial_clamps_target_to_capacity() {
8481 let compressed = [
8482 0xa0, b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j',
8483 ];
8484 let mut output = vec![0u8; 5];
8485
8486 let output_len = unsafe {
8487 LZ4_decompress_safe_partial(
8488 compressed.as_ptr() as *const c_char,
8489 output.as_mut_ptr() as *mut c_char,
8490 compressed.len() as c_int,
8491 10,
8492 output.len() as c_int,
8493 )
8494 };
8495 assert_eq!(output_len, output.len() as c_int);
8496 assert_eq!(&output, b"abcde");
8497
8498 let dict_compressed = [0x02u8, 0x03, 0x00];
8499 let dict = b"abc";
8500 let mut dict_output = vec![0u8; 4];
8501 let dict_output_len = unsafe {
8502 LZ4_decompress_safe_partial_usingDict(
8503 dict_compressed.as_ptr() as *const c_char,
8504 dict_output.as_mut_ptr() as *mut c_char,
8505 dict_compressed.len() as c_int,
8506 9,
8507 dict_output.len() as c_int,
8508 dict.as_ptr() as *const c_char,
8509 dict.len() as c_int,
8510 )
8511 };
8512 assert_eq!(dict_output_len, dict_output.len() as c_int);
8513 assert_eq!(&dict_output, b"abca");
8514 }
8515
8516 #[test]
8517 fn decompress_fast_returns_consumed_bytes() {
8518 let input = b"fast decode consumed bytes ".repeat(256);
8519 let bound = unsafe { LZ4_compressBound(input.len() as c_int) } as usize;
8520 let mut compressed = vec![0u8; bound];
8521 let compressed_len = unsafe {
8522 LZ4_compress_default(
8523 input.as_ptr() as *const c_char,
8524 compressed.as_mut_ptr() as *mut c_char,
8525 input.len() as c_int,
8526 compressed.len() as c_int,
8527 )
8528 };
8529 assert!(compressed_len > 0);
8530 let mut output = vec![0u8; input.len()];
8531 let consumed = unsafe {
8532 LZ4_decompress_fast(
8533 compressed.as_ptr() as *const c_char,
8534 output.as_mut_ptr() as *mut c_char,
8535 output.len() as c_int,
8536 )
8537 };
8538 assert!(consumed > 0);
8539 assert!(consumed <= compressed_len);
8540 assert_eq!(output, input);
8541
8542 let dict = b"abc";
8543 let dict_compressed = [0x30, b'x', b'y', b'z'];
8544 let mut dict_output = vec![0u8; 3];
8545 let consumed = unsafe {
8546 LZ4_decompress_fast_usingDict(
8547 dict_compressed.as_ptr() as *const c_char,
8548 dict_output.as_mut_ptr() as *mut c_char,
8549 dict_output.len() as c_int,
8550 dict.as_ptr() as *const c_char,
8551 dict.len() as c_int,
8552 )
8553 };
8554 assert_eq!(consumed, dict_compressed.len() as c_int);
8555 assert_eq!(dict_output, b"xyz");
8556 }
8557
8558 #[test]
8559 fn decodes_known_empty_block() {
8560 let compressed = [0u8];
8561 let mut output = [];
8562 let len = unsafe {
8563 LZ4_decompress_safe(
8564 compressed.as_ptr() as *const c_char,
8565 output.as_mut_ptr() as *mut c_char,
8566 compressed.len() as c_int,
8567 2,
8568 )
8569 };
8570 assert_eq!(len, 0);
8571 }
8572
8573 #[test]
8574 fn streaming_decode_uses_dictionary() {
8575 unsafe {
8576 let compressed = [0x08, 0x03, 0x00, 0x50, b'a', b'b', b'c', b'd', b'e'];
8577 let expected = b"abcabcabcabcabcde";
8578 let mut direct = vec![0u8; expected.len()];
8579 assert_eq!(
8580 decompress_block_with_dict(&compressed, &mut direct, b"abc"),
8581 Some(expected.len())
8582 );
8583 let dict = b"abc";
8584 let mut using_dict = vec![0u8; expected.len()];
8585 let using_dict_len = LZ4_decompress_safe_usingDict(
8586 compressed.as_ptr() as *const c_char,
8587 using_dict.as_mut_ptr() as *mut c_char,
8588 compressed.len() as c_int,
8589 using_dict.len() as c_int,
8590 dict.as_ptr() as *const c_char,
8591 dict.len() as c_int,
8592 );
8593 assert_eq!(using_dict_len, using_dict.len() as c_int);
8594 assert_eq!(using_dict, expected);
8595
8596 let stream = LZ4_createStreamDecode();
8597 assert!(!stream.is_null());
8598 assert_eq!(
8599 LZ4_setStreamDecode(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
8600 1
8601 );
8602 let mut output = vec![0u8; expected.len()];
8603 let len = LZ4_decompress_safe_continue(
8604 stream,
8605 compressed.as_ptr() as *const c_char,
8606 output.as_mut_ptr() as *mut c_char,
8607 compressed.len() as c_int,
8608 output.len() as c_int,
8609 );
8610 assert_eq!(len, output.len() as c_int);
8611 assert_eq!(output, expected);
8612 LZ4_freeStreamDecode(stream);
8613 }
8614 }
8615
8616 #[test]
8617 fn decompress_match_copy_handles_small_overlap() {
8618 let compressed = [0x15, b'a', 0x01, 0x00, 0x50, b'b', b'c', b'd', b'e', b'f'];
8619 let mut output = vec![0u8; 15];
8620
8621 assert_eq!(decompress_block(&compressed, &mut output), Some(15));
8622 assert_eq!(&output[..10], &[b'a'; 10]);
8623 assert_eq!(&output[10..], b"bcdef");
8624
8625 let mut partial = vec![0u8; 10];
8626 assert_eq!(
8627 decompress_block_partial(&compressed, &mut partial, 7),
8628 Some(7)
8629 );
8630 assert_eq!(&partial[..7], b"aaaaaaa");
8631 }
8632
8633 #[test]
8634 fn decompress_match_copy_spans_dictionary_and_prefix() {
8635 let compressed = [
8636 0x24, b'd', b'e', 0x04, 0x00, 0x50, b'f', b'g', b'h', b'i', b'j',
8637 ];
8638 let mut output = vec![0u8; 15];
8639
8640 assert_eq!(
8641 decompress_block_with_dict(&compressed, &mut output, b"abc"),
8642 Some(15)
8643 );
8644 assert_eq!(output, b"debcdebcdefghij");
8645
8646 let mut fast_output = vec![0u8; 15];
8647 let consumed = unsafe {
8648 LZ4_decompress_fast_usingDict(
8649 compressed.as_ptr() as *const c_char,
8650 fast_output.as_mut_ptr() as *mut c_char,
8651 fast_output.len() as c_int,
8652 b"abc".as_ptr() as *const c_char,
8653 3,
8654 )
8655 };
8656 assert_eq!(consumed, compressed.len() as c_int);
8657 assert_eq!(fast_output, b"debcdebcdefghij");
8658 }
8659
8660 #[test]
8661 fn frame_round_trip_raw_blocks() {
8662 unsafe {
8663 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
8664 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
8665 let prefs = LZ4FPreferences {
8666 frame_info: LZ4FFrameInfo {
8667 block_size_id: BlockSize::Max64KB,
8668 block_mode: BlockMode::Independent,
8669 content_checksum_flag: ContentChecksum::ChecksumEnabled,
8670 frame_type: FrameType::Frame,
8671 content_size: 0,
8672 dict_id: 0,
8673 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
8674 },
8675 compression_level: 0,
8676 auto_flush: 0,
8677 favor_dec_speed: 0,
8678 reserved: [0; 3],
8679 };
8680 let input = b"frame data";
8681 let mut encoded = vec![0u8; 128];
8682 let mut pos = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
8683 pos += LZ4F_compressUpdate(
8684 cctx,
8685 encoded.as_mut_ptr().add(pos),
8686 encoded.len() - pos,
8687 input.as_ptr(),
8688 input.len(),
8689 ptr::null(),
8690 );
8691 pos += LZ4F_compressEnd(
8692 cctx,
8693 encoded.as_mut_ptr().add(pos),
8694 encoded.len() - pos,
8695 ptr::null(),
8696 );
8697 encoded.truncate(pos);
8698 LZ4F_freeCompressionContext(cctx);
8699
8700 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
8701 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
8702 let mut out = vec![0u8; input.len()];
8703 let mut dst_size = out.len();
8704 let mut src_size = encoded.len();
8705 let code = LZ4F_decompress(
8706 dctx,
8707 out.as_mut_ptr(),
8708 &mut dst_size,
8709 encoded.as_ptr(),
8710 &mut src_size,
8711 ptr::null(),
8712 );
8713 assert!(!LZ4F_isError(code).eq(&1));
8714 assert_eq!(dst_size, input.len());
8715 assert_eq!(&out, input);
8716 LZ4F_freeDecompressionContext(dctx);
8717 }
8718 }
8719
8720 #[test]
8721 fn frame_rejects_block_larger_than_declared_max_size() {
8722 unsafe {
8723 let prefs = LZ4FPreferences {
8724 frame_info: LZ4FFrameInfo {
8725 block_size_id: BlockSize::Max64KB,
8726 block_mode: BlockMode::Independent,
8727 content_checksum_flag: ContentChecksum::NoChecksum,
8728 frame_type: FrameType::Frame,
8729 content_size: 0,
8730 dict_id: 0,
8731 block_checksum_flag: BlockChecksum::NoBlockChecksum,
8732 },
8733 compression_level: 0,
8734 auto_flush: 0,
8735 favor_dec_speed: 0,
8736 reserved: [0; 3],
8737 };
8738 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
8739 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
8740 let mut header = vec![0u8; 32];
8741 let header_len = LZ4F_compressBegin(cctx, header.as_mut_ptr(), header.len(), &prefs);
8742 assert_eq!(LZ4F_isError(header_len), 0);
8743 header.truncate(header_len);
8744 LZ4F_freeCompressionContext(cctx);
8745
8746 let too_large_block_len = (64 * 1024 + 1u32).to_le_bytes();
8747 let mut encoded = header.clone();
8748 encoded.extend_from_slice(&too_large_block_len);
8749
8750 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
8751 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
8752 let mut out = [0u8; 1];
8753 let mut dst_size = out.len();
8754 let mut src_size = encoded.len();
8755 let code = LZ4F_decompress(
8756 dctx,
8757 out.as_mut_ptr(),
8758 &mut dst_size,
8759 encoded.as_ptr(),
8760 &mut src_size,
8761 ptr::null(),
8762 );
8763 assert_eq!(code, ERROR_MAX_BLOCK_SIZE_INVALID);
8764 LZ4F_freeDecompressionContext(dctx);
8765
8766 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
8767 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
8768 let mut empty_dst_size = 0usize;
8769 let mut header_src_size = header.len();
8770 let hint = LZ4F_decompress(
8771 dctx,
8772 ptr::null_mut(),
8773 &mut empty_dst_size,
8774 header.as_ptr(),
8775 &mut header_src_size,
8776 ptr::null(),
8777 );
8778 assert_eq!(LZ4F_isError(hint), 0);
8779
8780 let mut dst_size = out.len();
8781 let mut src_size = too_large_block_len.len();
8782 let code = LZ4F_decompress(
8783 dctx,
8784 out.as_mut_ptr(),
8785 &mut dst_size,
8786 too_large_block_len.as_ptr(),
8787 &mut src_size,
8788 ptr::null(),
8789 );
8790 assert_eq!(code, ERROR_MAX_BLOCK_SIZE_INVALID);
8791 LZ4F_freeDecompressionContext(dctx);
8792 }
8793 }
8794
8795 #[test]
8796 fn frame_uncompressed_update_splits_large_raw_blocks() {
8797 unsafe {
8798 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
8799 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
8800 let prefs = LZ4FPreferences {
8801 frame_info: LZ4FFrameInfo {
8802 block_size_id: BlockSize::Max64KB,
8803 block_mode: BlockMode::Independent,
8804 content_checksum_flag: ContentChecksum::ChecksumEnabled,
8805 frame_type: FrameType::Frame,
8806 content_size: 0,
8807 dict_id: 0,
8808 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
8809 },
8810 compression_level: 0,
8811 auto_flush: 0,
8812 favor_dec_speed: 0,
8813 reserved: [0; 3],
8814 };
8815 let input = patterned_hc_input(150_000);
8816 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
8817 let mut pos = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
8818 let update = LZ4F_uncompressedUpdate(
8819 cctx,
8820 encoded.as_mut_ptr().add(pos) as *mut c_void,
8821 encoded.len() - pos,
8822 input.as_ptr() as *const c_void,
8823 input.len(),
8824 ptr::null(),
8825 );
8826 assert_eq!(LZ4F_isError(update), 0);
8827 let first_header = u32::from_le_bytes(encoded[pos..pos + 4].try_into().unwrap());
8828 assert_eq!(first_header, 0x8000_0000 | (64 * 1024));
8829 let second_pos = pos + 4 + 64 * 1024 + 4;
8830 let second_header =
8831 u32::from_le_bytes(encoded[second_pos..second_pos + 4].try_into().unwrap());
8832 assert_eq!(second_header, 0x8000_0000 | (64 * 1024));
8833 pos += update;
8834 pos += LZ4F_compressEnd(
8835 cctx,
8836 encoded.as_mut_ptr().add(pos),
8837 encoded.len() - pos,
8838 ptr::null(),
8839 );
8840 encoded.truncate(pos);
8841 LZ4F_freeCompressionContext(cctx);
8842
8843 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
8844 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
8845 let mut output = vec![0u8; input.len()];
8846 let mut src_offset = 0usize;
8847 let mut dst_offset = 0usize;
8848 loop {
8849 let mut src_size = encoded.len() - src_offset;
8850 let mut dst_size = output.len() - dst_offset;
8851 let code = LZ4F_decompress(
8852 dctx,
8853 output[dst_offset..].as_mut_ptr(),
8854 &mut dst_size,
8855 encoded.as_ptr().add(src_offset),
8856 &mut src_size,
8857 ptr::null(),
8858 );
8859 assert_eq!(LZ4F_isError(code), 0);
8860 src_offset += src_size;
8861 dst_offset += dst_size;
8862 if code == 0 {
8863 break;
8864 }
8865 }
8866 assert_eq!(output, input);
8867 LZ4F_freeDecompressionContext(dctx);
8868 }
8869 }
8870
8871 #[test]
8872 fn frame_single_call_compress_round_trip() {
8873 unsafe {
8874 assert_eq!(LZ4F_getVersion(), LZ4F_VERSION);
8875 assert_eq!(LZ4F_compressionLevel_max(), LZ4HC_CLEVEL_MAX);
8876 let prefs = LZ4FPreferences {
8877 frame_info: LZ4FFrameInfo {
8878 block_size_id: BlockSize::Max64KB,
8879 block_mode: BlockMode::Linked,
8880 content_checksum_flag: ContentChecksum::ChecksumEnabled,
8881 frame_type: FrameType::Frame,
8882 content_size: 0,
8883 dict_id: 0,
8884 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
8885 },
8886 compression_level: 9,
8887 auto_flush: 0,
8888 favor_dec_speed: 0,
8889 reserved: [0; 3],
8890 };
8891 let input = b"single call frame compression ".repeat(512);
8892 let bound = LZ4F_compressFrameBound(input.len(), &prefs);
8893 let mut encoded = vec![0u8; bound];
8894 let encoded_len = LZ4F_compressFrame(
8895 encoded.as_mut_ptr() as *mut c_void,
8896 encoded.len(),
8897 input.as_ptr() as *const c_void,
8898 input.len(),
8899 &prefs,
8900 );
8901 assert_eq!(LZ4F_isError(encoded_len), 0);
8902 encoded.truncate(encoded_len);
8903
8904 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
8905 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
8906 let mut output = vec![0u8; input.len()];
8907 let mut src_offset = 0usize;
8908 let mut dst_offset = 0usize;
8909 loop {
8910 let mut src_size = encoded.len() - src_offset;
8911 let mut dst_size = output.len() - dst_offset;
8912 let code = LZ4F_decompress(
8913 dctx,
8914 output[dst_offset..].as_mut_ptr(),
8915 &mut dst_size,
8916 encoded.as_ptr().add(src_offset),
8917 &mut src_size,
8918 ptr::null(),
8919 );
8920 assert_eq!(LZ4F_isError(code), 0);
8921 src_offset += src_size;
8922 dst_offset += dst_size;
8923 if code == 0 {
8924 break;
8925 }
8926 }
8927 assert_eq!(dst_offset, output.len());
8928 assert_eq!(output, input);
8929 LZ4F_freeDecompressionContext(dctx);
8930 }
8931 }
8932
8933 #[test]
8934 fn frame_header_size_reports_expected_lengths() {
8935 unsafe {
8936 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
8937 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
8938 let prefs = LZ4FPreferences {
8939 frame_info: LZ4FFrameInfo {
8940 block_size_id: BlockSize::Max64KB,
8941 block_mode: BlockMode::Independent,
8942 content_checksum_flag: ContentChecksum::ChecksumEnabled,
8943 frame_type: FrameType::Frame,
8944 content_size: 123,
8945 dict_id: 0,
8946 block_checksum_flag: BlockChecksum::NoBlockChecksum,
8947 },
8948 compression_level: 0,
8949 auto_flush: 0,
8950 favor_dec_speed: 0,
8951 reserved: [0; 3],
8952 };
8953 let mut encoded = vec![0u8; 32];
8954 let header_len = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
8955 assert_eq!(
8956 LZ4F_headerSize(encoded.as_ptr() as *const c_void, 5),
8957 header_len
8958 );
8959 assert_eq!(
8960 LZ4F_headerSize(encoded.as_ptr() as *const c_void, 4),
8961 ERROR_BAD_HEADER
8962 );
8963 assert_eq!(LZ4F_headerSize(ptr::null(), 5), ERROR_SRC_PTR_WRONG);
8964 let bad_magic = [0u8; 5];
8965 assert_eq!(
8966 LZ4F_headerSize(bad_magic.as_ptr() as *const c_void, bad_magic.len()),
8967 ERROR_FRAME_TYPE_UNKNOWN
8968 );
8969 LZ4F_freeCompressionContext(cctx);
8970
8971 let dict_prefs = LZ4FPreferences {
8972 frame_info: LZ4FFrameInfo {
8973 block_size_id: BlockSize::Max64KB,
8974 block_mode: BlockMode::Independent,
8975 content_checksum_flag: ContentChecksum::NoChecksum,
8976 frame_type: FrameType::Frame,
8977 content_size: 0,
8978 dict_id: 0x11,
8979 block_checksum_flag: BlockChecksum::NoBlockChecksum,
8980 },
8981 compression_level: 0,
8982 auto_flush: 0,
8983 favor_dec_speed: 0,
8984 reserved: [0; 3],
8985 };
8986 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
8987 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
8988 let header_len =
8989 LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &dict_prefs);
8990 assert_eq!(header_len, 11);
8991 assert_eq!(
8992 LZ4F_headerSize(encoded.as_ptr() as *const c_void, 5),
8993 header_len
8994 );
8995 LZ4F_freeCompressionContext(cctx);
8996
8997 let full_prefs = LZ4FPreferences {
8998 frame_info: LZ4FFrameInfo {
8999 block_size_id: BlockSize::Max4MB,
9000 block_mode: BlockMode::Linked,
9001 content_checksum_flag: ContentChecksum::ChecksumEnabled,
9002 frame_type: FrameType::Frame,
9003 content_size: 123,
9004 dict_id: 0x22,
9005 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
9006 },
9007 compression_level: 0,
9008 auto_flush: 0,
9009 favor_dec_speed: 0,
9010 reserved: [0; 3],
9011 };
9012 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
9013 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
9014 let header_len =
9015 LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &full_prefs);
9016 assert_eq!(header_len, 19);
9017 assert_eq!(
9018 LZ4F_headerSize(encoded.as_ptr() as *const c_void, 5),
9019 header_len
9020 );
9021 LZ4F_freeCompressionContext(cctx);
9022
9023 let mut skippable = Vec::new();
9024 skippable.extend_from_slice(&LZ4F_SKIPPABLE_MAGIC_MIN.to_le_bytes());
9025 skippable.extend_from_slice(&0u32.to_le_bytes());
9026 assert_eq!(
9027 LZ4F_headerSize(skippable.as_ptr() as *const c_void, skippable.len()),
9028 8
9029 );
9030 }
9031 }
9032
9033 #[test]
9034 fn frame_header_preserves_dict_id_and_rejects_reserved_bits() {
9035 unsafe {
9036 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
9037 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
9038 let prefs = LZ4FPreferences {
9039 frame_info: LZ4FFrameInfo {
9040 block_size_id: BlockSize::Max256KB,
9041 block_mode: BlockMode::Independent,
9042 content_checksum_flag: ContentChecksum::NoChecksum,
9043 frame_type: FrameType::Frame,
9044 content_size: 0,
9045 dict_id: 0x1234_abcd,
9046 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9047 },
9048 compression_level: 0,
9049 auto_flush: 0,
9050 favor_dec_speed: 0,
9051 reserved: [0; 3],
9052 };
9053 let mut encoded = vec![0u8; 32];
9054 let header_len = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
9055 assert_eq!(header_len, 11);
9056 assert_eq!(encoded[4] & 0x01, 0x01);
9057
9058 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9059 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9060 let mut info = LZ4FFrameInfo {
9061 block_size_id: BlockSize::Default,
9062 block_mode: BlockMode::Linked,
9063 content_checksum_flag: ContentChecksum::ChecksumEnabled,
9064 frame_type: FrameType::SkippableFrame,
9065 content_size: 1,
9066 dict_id: 0,
9067 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
9068 };
9069 let mut src_size = header_len;
9070 assert_eq!(
9071 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut src_size),
9072 0
9073 );
9074 assert_eq!(src_size, header_len);
9075 assert_eq!(info.dict_id, 0x1234_abcd);
9076 assert!(matches!(info.block_size_id, BlockSize::Max256KB));
9077 LZ4F_freeDecompressionContext(dctx);
9078
9079 let mut bad_flg = encoded[..header_len].to_vec();
9080 bad_flg[4] |= 0x02;
9081 let hc = (xxhash32(&bad_flg[4..header_len - 1], 0) >> 8) as u8;
9082 bad_flg[header_len - 1] = hc;
9083 let mut bad_size = bad_flg.len();
9084 assert_eq!(
9085 LZ4F_getFrameInfo(
9086 LZ4FDecompressionContext(ptr::null_mut()),
9087 ptr::null_mut(),
9088 ptr::null(),
9089 ptr::null_mut(),
9090 ),
9091 ERROR_PARAMETER_NULL
9092 );
9093 let mut dctx_bad = LZ4FDecompressionContext(ptr::null_mut());
9094 assert_eq!(
9095 LZ4F_createDecompressionContext(&mut dctx_bad, LZ4F_VERSION),
9096 0
9097 );
9098 assert_eq!(
9099 LZ4F_getFrameInfo(dctx_bad, &mut info, bad_flg.as_ptr(), &mut bad_size),
9100 ERROR_RESERVED_FLAG_SET
9101 );
9102
9103 let mut bad_bd = encoded[..header_len].to_vec();
9104 bad_bd[5] |= 0x01;
9105 let hc = (xxhash32(&bad_bd[4..header_len - 1], 0) >> 8) as u8;
9106 bad_bd[header_len - 1] = hc;
9107 bad_size = bad_bd.len();
9108 assert_eq!(
9109 LZ4F_getFrameInfo(dctx_bad, &mut info, bad_bd.as_ptr(), &mut bad_size),
9110 ERROR_RESERVED_FLAG_SET
9111 );
9112
9113 let mut bad_version = encoded[..header_len].to_vec();
9114 bad_version[4] = (bad_version[4] & !0xC0) | 0x80;
9115 let hc = (xxhash32(&bad_version[4..header_len - 1], 0) >> 8) as u8;
9116 bad_version[header_len - 1] = hc;
9117 bad_size = bad_version.len();
9118 assert_eq!(
9119 LZ4F_getFrameInfo(dctx_bad, &mut info, bad_version.as_ptr(), &mut bad_size),
9120 ERROR_HEADER_VERSION_WRONG
9121 );
9122 let mut dst = [0u8; 1];
9123 let mut dst_size = dst.len();
9124 let mut src_size = bad_version.len();
9125 assert_eq!(
9126 LZ4F_decompress(
9127 dctx_bad,
9128 dst.as_mut_ptr(),
9129 &mut dst_size,
9130 bad_version.as_ptr(),
9131 &mut src_size,
9132 ptr::null(),
9133 ),
9134 ERROR_HEADER_VERSION_WRONG
9135 );
9136
9137 LZ4F_resetDecompressionContext(dctx_bad);
9138 let mut bad_block_size = encoded[..header_len].to_vec();
9139 bad_block_size[5] = (bad_block_size[5] & 0x0f) | (3 << 4);
9140 let hc = (xxhash32(&bad_block_size[4..header_len - 1], 0) >> 8) as u8;
9141 bad_block_size[header_len - 1] = hc;
9142 bad_size = bad_block_size.len();
9143 assert_eq!(
9144 LZ4F_getFrameInfo(dctx_bad, &mut info, bad_block_size.as_ptr(), &mut bad_size),
9145 ERROR_MAX_BLOCK_SIZE_INVALID
9146 );
9147 let mut dst_size = dst.len();
9148 let mut src_size = bad_block_size.len();
9149 assert_eq!(
9150 LZ4F_decompress(
9151 dctx_bad,
9152 dst.as_mut_ptr(),
9153 &mut dst_size,
9154 bad_block_size.as_ptr(),
9155 &mut src_size,
9156 ptr::null(),
9157 ),
9158 ERROR_MAX_BLOCK_SIZE_INVALID
9159 );
9160
9161 LZ4F_resetDecompressionContext(dctx_bad);
9162 let mut bad_magic = encoded[..header_len].to_vec();
9163 bad_magic[..4].copy_from_slice(b"bad!");
9164 bad_size = bad_magic.len();
9165 assert_eq!(
9166 LZ4F_getFrameInfo(dctx_bad, &mut info, bad_magic.as_ptr(), &mut bad_size),
9167 ERROR_FRAME_TYPE_UNKNOWN
9168 );
9169 assert_eq!(bad_size, 0);
9170 let mut dst_size = dst.len();
9171 let mut src_size = bad_magic.len();
9172 assert_eq!(
9173 LZ4F_decompress(
9174 dctx_bad,
9175 dst.as_mut_ptr(),
9176 &mut dst_size,
9177 bad_magic.as_ptr(),
9178 &mut src_size,
9179 ptr::null(),
9180 ),
9181 ERROR_FRAME_TYPE_UNKNOWN
9182 );
9183 LZ4F_freeDecompressionContext(dctx_bad);
9184 LZ4F_freeCompressionContext(cctx);
9185 }
9186 }
9187
9188 #[test]
9189 fn frame_info_errors_zero_consumed_and_preserve_context() {
9190 unsafe {
9191 let input = b"context survives bad frame info";
9192 let prefs = LZ4FPreferences {
9193 frame_info: LZ4FFrameInfo {
9194 block_size_id: BlockSize::Max64KB,
9195 block_mode: BlockMode::Independent,
9196 content_checksum_flag: ContentChecksum::NoChecksum,
9197 frame_type: FrameType::Frame,
9198 content_size: input.len() as u64,
9199 dict_id: 0,
9200 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9201 },
9202 compression_level: 0,
9203 auto_flush: 0,
9204 favor_dec_speed: 0,
9205 reserved: [0; 3],
9206 };
9207 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
9208 let encoded_len = LZ4F_compressFrame(
9209 encoded.as_mut_ptr() as *mut c_void,
9210 encoded.len(),
9211 input.as_ptr() as *const c_void,
9212 input.len(),
9213 &prefs,
9214 );
9215 assert_eq!(LZ4F_isError(encoded_len), 0);
9216 encoded.truncate(encoded_len);
9217
9218 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9219 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9220 let mut info = LZ4FFrameInfo {
9221 block_size_id: BlockSize::Default,
9222 block_mode: BlockMode::Linked,
9223 content_checksum_flag: ContentChecksum::ChecksumEnabled,
9224 frame_type: FrameType::SkippableFrame,
9225 content_size: 99,
9226 dict_id: 99,
9227 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
9228 };
9229 let mut short_size = 6usize;
9230 assert_eq!(
9231 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut short_size),
9232 ERROR_BAD_HEADER
9233 );
9234 assert_eq!(short_size, 0);
9235 assert!(matches!(info.frame_type, FrameType::SkippableFrame));
9236
9237 let mut consumed = encoded.len();
9238 assert_eq!(
9239 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut consumed),
9240 0
9241 );
9242 assert_eq!(consumed, 15);
9243
9244 let mut output = vec![0u8; input.len()];
9245 let mut src_size = encoded.len() - consumed;
9246 let mut dst_size = output.len();
9247 let code = LZ4F_decompress(
9248 dctx,
9249 output.as_mut_ptr(),
9250 &mut dst_size,
9251 encoded.as_ptr().add(consumed),
9252 &mut src_size,
9253 ptr::null(),
9254 );
9255 assert_eq!(LZ4F_isError(code), 0);
9256 assert_eq!(output, input);
9257 LZ4F_freeDecompressionContext(dctx);
9258 }
9259 }
9260
9261 #[test]
9262 fn frame_info_rejects_context_after_partial_header_decode_started() {
9263 unsafe {
9264 let input = b"partial header get frame info";
9265 let prefs = LZ4FPreferences {
9266 frame_info: LZ4FFrameInfo {
9267 block_size_id: BlockSize::Max64KB,
9268 block_mode: BlockMode::Independent,
9269 content_checksum_flag: ContentChecksum::NoChecksum,
9270 frame_type: FrameType::Frame,
9271 content_size: input.len() as u64,
9272 dict_id: 0,
9273 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9274 },
9275 compression_level: 0,
9276 auto_flush: 0,
9277 favor_dec_speed: 0,
9278 reserved: [0; 3],
9279 };
9280 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
9281 let encoded_len = LZ4F_compressFrame(
9282 encoded.as_mut_ptr() as *mut c_void,
9283 encoded.len(),
9284 input.as_ptr() as *const c_void,
9285 input.len(),
9286 &prefs,
9287 );
9288 assert_eq!(LZ4F_isError(encoded_len), 0);
9289 encoded.truncate(encoded_len);
9290
9291 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9292 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9293 let mut output = [0u8; 1];
9294 let mut src_size = 5usize;
9295 let mut dst_size = output.len();
9296 let hint = LZ4F_decompress(
9297 dctx,
9298 output.as_mut_ptr(),
9299 &mut dst_size,
9300 encoded.as_ptr(),
9301 &mut src_size,
9302 ptr::null(),
9303 );
9304 assert_eq!(LZ4F_isError(hint), 0);
9305 assert_eq!(src_size, 5);
9306 assert_eq!(dst_size, 0);
9307
9308 let mut info = LZ4FFrameInfo {
9309 block_size_id: BlockSize::Default,
9310 block_mode: BlockMode::Linked,
9311 content_checksum_flag: ContentChecksum::ChecksumEnabled,
9312 frame_type: FrameType::SkippableFrame,
9313 content_size: 99,
9314 dict_id: 99,
9315 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
9316 };
9317 let mut consumed = encoded.len();
9318 assert_eq!(
9319 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut consumed),
9320 ERROR_FRAME_DECODING_ALREADY_STARTED
9321 );
9322 assert_eq!(consumed, 0);
9323 assert!(matches!(info.frame_type, FrameType::SkippableFrame));
9324 LZ4F_freeDecompressionContext(dctx);
9325 }
9326 }
9327
9328 #[test]
9329 fn frame_using_dict_round_trip_first_independent_block() {
9330 unsafe {
9331 let dict = b"abcdefghijklmnop";
9332 let input = b"abcdefghijklmnop";
9333 let prefs = LZ4FPreferences {
9334 frame_info: LZ4FFrameInfo {
9335 block_size_id: BlockSize::Max64KB,
9336 block_mode: BlockMode::Independent,
9337 content_checksum_flag: ContentChecksum::NoChecksum,
9338 frame_type: FrameType::Frame,
9339 content_size: input.len() as u64,
9340 dict_id: 0,
9341 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9342 },
9343 compression_level: 9,
9344 auto_flush: 0,
9345 favor_dec_speed: 0,
9346 reserved: [0; 3],
9347 };
9348
9349 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
9350 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
9351 let mut encoded = vec![0u8; 256];
9352 let mut pos = LZ4F_compressBegin_usingDict(
9353 cctx,
9354 encoded.as_mut_ptr() as *mut c_void,
9355 encoded.len(),
9356 dict.as_ptr() as *const c_void,
9357 dict.len(),
9358 &prefs,
9359 );
9360 assert_eq!(LZ4F_isError(pos), 0);
9361 let update_len = LZ4F_compressUpdate(
9362 cctx,
9363 encoded.as_mut_ptr().add(pos),
9364 encoded.len() - pos,
9365 input.as_ptr(),
9366 input.len(),
9367 ptr::null(),
9368 );
9369 assert_eq!(LZ4F_isError(update_len), 0);
9370 let block_header = u32::from_le_bytes(encoded[pos..pos + 4].try_into().unwrap());
9371 assert_eq!(block_header & 0x8000_0000, 0);
9372 assert!((block_header as usize) < input.len());
9373 pos += update_len;
9374 pos += LZ4F_compressEnd(
9375 cctx,
9376 encoded.as_mut_ptr().add(pos),
9377 encoded.len() - pos,
9378 ptr::null(),
9379 );
9380 encoded.truncate(pos);
9381 LZ4F_freeCompressionContext(cctx);
9382
9383 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9384 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9385 let mut output = vec![0u8; input.len()];
9386 let mut dst_size = output.len();
9387 let mut src_size = encoded.len();
9388 let code = LZ4F_decompress_usingDict(
9389 dctx,
9390 output.as_mut_ptr() as *mut c_void,
9391 &mut dst_size,
9392 encoded.as_ptr() as *const c_void,
9393 &mut src_size,
9394 dict.as_ptr() as *const c_void,
9395 dict.len(),
9396 ptr::null(),
9397 );
9398 assert_eq!(LZ4F_isError(code), 0);
9399 assert_eq!(dst_size, input.len());
9400 assert_eq!(output, input);
9401 LZ4F_freeDecompressionContext(dctx);
9402 }
9403 }
9404
9405 #[test]
9406 fn frame_cdict_round_trip() {
9407 unsafe {
9408 let dict = b"abcdefghijklmnop";
9409 let input = b"abcdefghijklmnop";
9410 let cdict = LZ4F_createCDict(dict.as_ptr() as *const c_void, dict.len());
9411 assert!(!cdict.is_null());
9412 let prefs = LZ4FPreferences {
9413 frame_info: LZ4FFrameInfo {
9414 block_size_id: BlockSize::Max64KB,
9415 block_mode: BlockMode::Independent,
9416 content_checksum_flag: ContentChecksum::NoChecksum,
9417 frame_type: FrameType::Frame,
9418 content_size: input.len() as u64,
9419 dict_id: 0,
9420 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9421 },
9422 compression_level: 9,
9423 auto_flush: 0,
9424 favor_dec_speed: 0,
9425 reserved: [0; 3],
9426 };
9427
9428 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
9429 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
9430 let mut encoded = vec![0u8; 256];
9431 let encoded_len = LZ4F_compressFrame_usingCDict(
9432 cctx,
9433 encoded.as_mut_ptr() as *mut c_void,
9434 encoded.len(),
9435 input.as_ptr() as *const c_void,
9436 input.len(),
9437 cdict,
9438 &prefs,
9439 );
9440 assert_eq!(LZ4F_isError(encoded_len), 0);
9441 encoded.truncate(encoded_len);
9442 LZ4F_freeCompressionContext(cctx);
9443 LZ4F_freeCDict(cdict);
9444
9445 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9446 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9447 let mut output = vec![0u8; input.len()];
9448 let mut dst_size = output.len();
9449 let mut src_size = encoded.len();
9450 let code = LZ4F_decompress_usingDict(
9451 dctx,
9452 output.as_mut_ptr() as *mut c_void,
9453 &mut dst_size,
9454 encoded.as_ptr() as *const c_void,
9455 &mut src_size,
9456 dict.as_ptr() as *const c_void,
9457 dict.len(),
9458 ptr::null(),
9459 );
9460 assert_eq!(LZ4F_isError(code), 0);
9461 assert_eq!(dst_size, input.len());
9462 assert_eq!(output, input);
9463 LZ4F_freeDecompressionContext(dctx);
9464 }
9465 }
9466
9467 #[test]
9468 fn frame_skippable_frame_is_skipped() {
9469 unsafe {
9470 let mut skippable = Vec::new();
9471 skippable.extend_from_slice(&LZ4F_SKIPPABLE_MAGIC_MIN.to_le_bytes());
9472 skippable.extend_from_slice(&5u32.to_le_bytes());
9473 skippable.extend_from_slice(b"abcde");
9474
9475 let mut dctx_info = LZ4FDecompressionContext(ptr::null_mut());
9476 assert_eq!(
9477 LZ4F_createDecompressionContext(&mut dctx_info, LZ4F_VERSION),
9478 0
9479 );
9480 let mut info = LZ4FFrameInfo {
9481 block_size_id: BlockSize::Default,
9482 block_mode: BlockMode::Independent,
9483 content_checksum_flag: ContentChecksum::NoChecksum,
9484 frame_type: FrameType::Frame,
9485 content_size: 0,
9486 dict_id: 0,
9487 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9488 };
9489 let mut info_src_size = skippable.len();
9490 assert_eq!(
9491 LZ4F_getFrameInfo(dctx_info, &mut info, skippable.as_ptr(), &mut info_src_size),
9492 0
9493 );
9494 assert!(matches!(info.frame_type, FrameType::SkippableFrame));
9495 assert_eq!(info_src_size, skippable.len());
9496 LZ4F_freeDecompressionContext(dctx_info);
9497
9498 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9499 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9500 let mut output = [0u8; 1];
9501 let mut dst_size = output.len();
9502 let mut src_size = skippable.len();
9503 let code = LZ4F_decompress(
9504 dctx,
9505 output.as_mut_ptr(),
9506 &mut dst_size,
9507 skippable.as_ptr(),
9508 &mut src_size,
9509 ptr::null(),
9510 );
9511 assert_eq!(code, 0);
9512 assert_eq!(dst_size, 0);
9513 assert_eq!(src_size, skippable.len());
9514 LZ4F_freeDecompressionContext(dctx);
9515 }
9516 }
9517
9518 #[test]
9519 fn frame_info_consumes_header_for_subsequent_decompress() {
9520 unsafe {
9521 let input = patterned_hc_input(96 * 1024);
9522 let prefs = LZ4FPreferences {
9523 frame_info: LZ4FFrameInfo {
9524 block_size_id: BlockSize::Max64KB,
9525 block_mode: BlockMode::Independent,
9526 content_checksum_flag: ContentChecksum::ChecksumEnabled,
9527 frame_type: FrameType::Frame,
9528 content_size: input.len() as u64,
9529 dict_id: 0xfeed_beef,
9530 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9531 },
9532 compression_level: 0,
9533 auto_flush: 0,
9534 favor_dec_speed: 0,
9535 reserved: [0; 3],
9536 };
9537 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
9538 let encoded_len = LZ4F_compressFrame(
9539 encoded.as_mut_ptr() as *mut c_void,
9540 encoded.len(),
9541 input.as_ptr() as *const c_void,
9542 input.len(),
9543 &prefs,
9544 );
9545 assert_eq!(LZ4F_isError(encoded_len), 0);
9546 encoded.truncate(encoded_len);
9547
9548 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9549 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9550 let mut info = LZ4FFrameInfo {
9551 block_size_id: BlockSize::Default,
9552 block_mode: BlockMode::Linked,
9553 content_checksum_flag: ContentChecksum::NoChecksum,
9554 frame_type: FrameType::SkippableFrame,
9555 content_size: 1,
9556 dict_id: 123,
9557 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
9558 };
9559 let mut consumed = encoded.len();
9560 assert_eq!(
9561 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut consumed),
9562 0
9563 );
9564 assert_eq!(consumed, 19);
9565 assert!(matches!(info.frame_type, FrameType::Frame));
9566 assert!(matches!(info.block_size_id, BlockSize::Max64KB));
9567 assert_eq!(info.content_size, input.len() as u64);
9568 assert_eq!(info.dict_id, 0xfeed_beef);
9569
9570 let mut second_consumed = 123usize;
9571 let second_hint = LZ4F_getFrameInfo(dctx, &mut info, ptr::null(), &mut second_consumed);
9572 assert_eq!(LZ4F_isError(second_hint), 0);
9573 assert_eq!(second_consumed, 0);
9574 assert_eq!(second_hint, 4);
9575 assert_eq!(info.content_size, input.len() as u64);
9576 assert_eq!(info.dict_id, 0xfeed_beef);
9577
9578 let mut output = vec![0u8; input.len()];
9579 let mut src_offset = consumed;
9580 let mut dst_offset = 0usize;
9581 loop {
9582 let mut src_size = encoded.len() - src_offset;
9583 let mut dst_size = output.len() - dst_offset;
9584 let code = LZ4F_decompress(
9585 dctx,
9586 output[dst_offset..].as_mut_ptr(),
9587 &mut dst_size,
9588 encoded.as_ptr().add(src_offset),
9589 &mut src_size,
9590 ptr::null(),
9591 );
9592 assert_eq!(LZ4F_isError(code), 0);
9593 src_offset += src_size;
9594 dst_offset += dst_size;
9595 if code == 0 {
9596 break;
9597 }
9598 }
9599 assert_eq!(src_offset, encoded.len());
9600 assert_eq!(output, input);
9601 LZ4F_freeDecompressionContext(dctx);
9602 }
9603 }
9604
9605 #[test]
9606 fn frame_info_reports_state_after_partial_decode_and_reset() {
9607 unsafe {
9608 let input = patterned_hc_input(80 * 1024);
9609 let prefs = LZ4FPreferences {
9610 frame_info: LZ4FFrameInfo {
9611 block_size_id: BlockSize::Max64KB,
9612 block_mode: BlockMode::Linked,
9613 content_checksum_flag: ContentChecksum::ChecksumEnabled,
9614 frame_type: FrameType::Frame,
9615 content_size: input.len() as u64,
9616 dict_id: 0x0102_0304,
9617 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9618 },
9619 compression_level: 0,
9620 auto_flush: 0,
9621 favor_dec_speed: 0,
9622 reserved: [0; 3],
9623 };
9624 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
9625 let encoded_len = LZ4F_compressFrame(
9626 encoded.as_mut_ptr() as *mut c_void,
9627 encoded.len(),
9628 input.as_ptr() as *const c_void,
9629 input.len(),
9630 &prefs,
9631 );
9632 assert_eq!(LZ4F_isError(encoded_len), 0);
9633 encoded.truncate(encoded_len);
9634
9635 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9636 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9637 let mut info = LZ4FFrameInfo {
9638 block_size_id: BlockSize::Default,
9639 block_mode: BlockMode::Independent,
9640 content_checksum_flag: ContentChecksum::NoChecksum,
9641 frame_type: FrameType::SkippableFrame,
9642 content_size: 0,
9643 dict_id: 0,
9644 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9645 };
9646 let mut consumed = encoded.len();
9647 assert_eq!(
9648 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut consumed),
9649 0
9650 );
9651 assert_eq!(consumed, 19);
9652 assert!(matches!(info.block_mode, BlockMode::Linked));
9653 assert_eq!(info.content_size, input.len() as u64);
9654 assert_eq!(info.dict_id, 0x0102_0304);
9655
9656 let options = LZ4FDecompressOptions {
9657 stable_dst: 1,
9658 skipChecksums: 0,
9659 reserved: [0; 2],
9660 };
9661 let mut output = [0u8; 37];
9662 let mut src_size = encoded.len() - consumed;
9663 let mut dst_size = output.len();
9664 let hint = LZ4F_decompress(
9665 dctx,
9666 output.as_mut_ptr(),
9667 &mut dst_size,
9668 encoded.as_ptr().add(consumed),
9669 &mut src_size,
9670 &options,
9671 );
9672 assert_eq!(LZ4F_isError(hint), 0);
9673 assert_eq!(dst_size, output.len());
9674
9675 let mut second_consumed = usize::MAX;
9676 let second_hint = LZ4F_getFrameInfo(dctx, &mut info, ptr::null(), &mut second_consumed);
9677 assert_eq!(LZ4F_isError(second_hint), 0);
9678 assert_eq!(second_consumed, 0);
9679 assert!(matches!(info.block_mode, BlockMode::Linked));
9680 assert_eq!(info.content_size, input.len() as u64);
9681 assert_eq!(info.dict_id, 0x0102_0304);
9682
9683 LZ4F_resetDecompressionContext(dctx);
9684 second_consumed = usize::MAX;
9685 assert_eq!(
9686 LZ4F_getFrameInfo(dctx, &mut info, ptr::null(), &mut second_consumed),
9687 ERROR_SRC_PTR_WRONG
9688 );
9689 assert_eq!(second_consumed, 0);
9690 LZ4F_freeDecompressionContext(dctx);
9691 }
9692 }
9693
9694 #[test]
9695 fn frame_dict_id_decodes_from_upstream_generated_fixture() {
9696 unsafe {
9697 let encoded = decode_hex(
9698 "04224d186d401b00000000000000adfbcadeba1b0000806672616d65207769746820757073747265616d206469637420696400000000af334300",
9699 );
9700 let expected = b"frame with upstream dict id";
9701 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9702 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9703 let mut info = LZ4FFrameInfo {
9704 block_size_id: BlockSize::Default,
9705 block_mode: BlockMode::Linked,
9706 content_checksum_flag: ContentChecksum::NoChecksum,
9707 frame_type: FrameType::SkippableFrame,
9708 content_size: 0,
9709 dict_id: 0,
9710 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
9711 };
9712 let mut consumed = encoded.len();
9713 assert_eq!(
9714 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut consumed),
9715 0
9716 );
9717 assert_eq!(consumed, 19);
9718 assert!(matches!(info.block_size_id, BlockSize::Max64KB));
9719 assert!(matches!(info.block_mode, BlockMode::Independent));
9720 assert!(matches!(
9721 info.content_checksum_flag,
9722 ContentChecksum::ChecksumEnabled
9723 ));
9724 assert_eq!(info.content_size, expected.len() as u64);
9725 assert_eq!(info.dict_id, 0xdecafbad);
9726
9727 let mut output = vec![0u8; expected.len()];
9728 let mut src_offset = consumed;
9729 let mut dst_offset = 0usize;
9730 loop {
9731 let mut src_size = encoded.len() - src_offset;
9732 let mut dst_size = output.len() - dst_offset;
9733 let code = LZ4F_decompress(
9734 dctx,
9735 output[dst_offset..].as_mut_ptr(),
9736 &mut dst_size,
9737 encoded.as_ptr().add(src_offset),
9738 &mut src_size,
9739 ptr::null(),
9740 );
9741 assert_eq!(LZ4F_isError(code), 0);
9742 src_offset += src_size;
9743 dst_offset += dst_size;
9744 if code == 0 {
9745 break;
9746 }
9747 }
9748 assert_eq!(src_offset, encoded.len());
9749 assert_eq!(dst_offset, expected.len());
9750 assert_eq!(output, expected);
9751 LZ4F_freeDecompressionContext(dctx);
9752 }
9753 }
9754
9755 #[test]
9756 fn frame_decompress_resets_context_after_complete_frame() {
9757 unsafe {
9758 let first = b"first complete frame";
9759 let second = b"second complete frame";
9760 let prefs = LZ4FPreferences {
9761 frame_info: LZ4FFrameInfo {
9762 block_size_id: BlockSize::Max64KB,
9763 block_mode: BlockMode::Independent,
9764 content_checksum_flag: ContentChecksum::NoChecksum,
9765 frame_type: FrameType::Frame,
9766 content_size: 0,
9767 dict_id: 0,
9768 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9769 },
9770 compression_level: 0,
9771 auto_flush: 0,
9772 favor_dec_speed: 0,
9773 reserved: [0; 3],
9774 };
9775 let mut first_frame = vec![0u8; LZ4F_compressBound(first.len(), &prefs) + 32];
9776 let first_len = LZ4F_compressFrame(
9777 first_frame.as_mut_ptr() as *mut c_void,
9778 first_frame.len(),
9779 first.as_ptr() as *const c_void,
9780 first.len(),
9781 &prefs,
9782 );
9783 assert_eq!(LZ4F_isError(first_len), 0);
9784 first_frame.truncate(first_len);
9785
9786 let mut second_frame = vec![0u8; LZ4F_compressBound(second.len(), &prefs) + 32];
9787 let second_len = LZ4F_compressFrame(
9788 second_frame.as_mut_ptr() as *mut c_void,
9789 second_frame.len(),
9790 second.as_ptr() as *const c_void,
9791 second.len(),
9792 &prefs,
9793 );
9794 assert_eq!(LZ4F_isError(second_len), 0);
9795 second_frame.truncate(second_len);
9796
9797 let mut concatenated = first_frame.clone();
9798 concatenated.extend_from_slice(&second_frame);
9799
9800 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9801 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9802 let mut output = vec![0u8; first.len()];
9803 let mut src_offset = 0usize;
9804 let mut dst_offset = 0usize;
9805 loop {
9806 let mut src_size = first_frame.len() - src_offset;
9807 let mut dst_size = output.len() - dst_offset;
9808 let code = LZ4F_decompress(
9809 dctx,
9810 output[dst_offset..].as_mut_ptr(),
9811 &mut dst_size,
9812 concatenated.as_ptr().add(src_offset),
9813 &mut src_size,
9814 ptr::null(),
9815 );
9816 assert_eq!(LZ4F_isError(code), 0);
9817 src_offset += src_size;
9818 dst_offset += dst_size;
9819 if code == 0 {
9820 break;
9821 }
9822 }
9823 assert_eq!(src_offset, first_frame.len());
9824 assert_eq!(output, first);
9825
9826 let mut info = LZ4FFrameInfo {
9827 block_size_id: BlockSize::Default,
9828 block_mode: BlockMode::Linked,
9829 content_checksum_flag: ContentChecksum::ChecksumEnabled,
9830 frame_type: FrameType::SkippableFrame,
9831 content_size: 1,
9832 dict_id: 1,
9833 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
9834 };
9835 let mut consumed = usize::MAX;
9836 assert_eq!(
9837 LZ4F_getFrameInfo(dctx, &mut info, ptr::null(), &mut consumed),
9838 ERROR_SRC_PTR_WRONG
9839 );
9840 assert_eq!(consumed, 0);
9841
9842 let mut output = vec![0u8; second.len()];
9843 src_offset = first_frame.len();
9844 let mut dst_offset = 0usize;
9845 loop {
9846 let mut src_size = concatenated.len() - src_offset;
9847 let mut dst_size = output.len() - dst_offset;
9848 let code = LZ4F_decompress(
9849 dctx,
9850 output[dst_offset..].as_mut_ptr(),
9851 &mut dst_size,
9852 concatenated.as_ptr().add(src_offset),
9853 &mut src_size,
9854 ptr::null(),
9855 );
9856 assert_eq!(LZ4F_isError(code), 0);
9857 src_offset += src_size;
9858 dst_offset += dst_size;
9859 if code == 0 {
9860 break;
9861 }
9862 }
9863 assert_eq!(src_offset, concatenated.len());
9864 assert_eq!(output, second);
9865 LZ4F_freeDecompressionContext(dctx);
9866 }
9867 }
9868
9869 #[test]
9870 fn frame_free_decompression_context_reports_incomplete_frame() {
9871 unsafe {
9872 let input = b"incomplete frame free status";
9873 let prefs = LZ4FPreferences {
9874 frame_info: LZ4FFrameInfo {
9875 block_size_id: BlockSize::Max64KB,
9876 block_mode: BlockMode::Independent,
9877 content_checksum_flag: ContentChecksum::NoChecksum,
9878 frame_type: FrameType::Frame,
9879 content_size: 0,
9880 dict_id: 0,
9881 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9882 },
9883 compression_level: 0,
9884 auto_flush: 0,
9885 favor_dec_speed: 0,
9886 reserved: [0; 3],
9887 };
9888 let mut frame = vec![0u8; LZ4F_compressBound(input.len(), &prefs)];
9889 let frame_len = LZ4F_compressFrame(
9890 frame.as_mut_ptr() as *mut c_void,
9891 frame.len(),
9892 input.as_ptr() as *const c_void,
9893 input.len(),
9894 &prefs,
9895 );
9896 assert_eq!(LZ4F_isError(frame_len), 0);
9897 frame.truncate(frame_len);
9898
9899 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9900 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9901 assert_eq!(LZ4F_freeDecompressionContext(dctx), 0);
9902
9903 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9904 let mut info = LZ4FFrameInfo {
9905 block_size_id: BlockSize::Default,
9906 block_mode: BlockMode::Linked,
9907 content_checksum_flag: ContentChecksum::NoChecksum,
9908 frame_type: FrameType::Frame,
9909 content_size: 0,
9910 dict_id: 0,
9911 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9912 };
9913 let mut consumed = frame.len();
9914 let info_code = LZ4F_getFrameInfo(dctx, &mut info, frame.as_ptr(), &mut consumed);
9915 assert_eq!(LZ4F_isError(info_code), 0);
9916 assert!(consumed > 0);
9917 assert_ne!(LZ4F_freeDecompressionContext(dctx), 0);
9918
9919 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9920 let mut output = vec![0u8; input.len()];
9921 let mut src_size = frame.len();
9922 let mut dst_size = output.len();
9923 let code = LZ4F_decompress(
9924 dctx,
9925 output.as_mut_ptr(),
9926 &mut dst_size,
9927 frame.as_ptr(),
9928 &mut src_size,
9929 ptr::null(),
9930 );
9931 assert_eq!(code, 0);
9932 assert_eq!(output, input);
9933 assert_eq!(LZ4F_freeDecompressionContext(dctx), 0);
9934 }
9935 }
9936
9937 #[test]
9938 fn frame_update_compresses_blocks_when_level_is_set() {
9939 unsafe {
9940 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
9941 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
9942 let prefs = LZ4FPreferences {
9943 frame_info: LZ4FFrameInfo {
9944 block_size_id: BlockSize::Max64KB,
9945 block_mode: BlockMode::Independent,
9946 content_checksum_flag: ContentChecksum::NoChecksum,
9947 frame_type: FrameType::Frame,
9948 content_size: 0,
9949 dict_id: 0,
9950 block_checksum_flag: BlockChecksum::NoBlockChecksum,
9951 },
9952 compression_level: 9,
9953 auto_flush: 0,
9954 favor_dec_speed: 0,
9955 reserved: [0; 3],
9956 };
9957 let input = vec![b'a'; 16 * 1024];
9958 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
9959 let header_len = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
9960 assert!(!LZ4F_isError(header_len).eq(&1));
9961 let update_len = LZ4F_compressUpdate(
9962 cctx,
9963 encoded.as_mut_ptr().add(header_len),
9964 encoded.len() - header_len,
9965 input.as_ptr(),
9966 input.len(),
9967 ptr::null(),
9968 );
9969 assert!(!LZ4F_isError(update_len).eq(&1));
9970 let block_header =
9971 u32::from_le_bytes(encoded[header_len..header_len + 4].try_into().unwrap());
9972 assert_eq!(block_header & 0x8000_0000, 0);
9973 assert!((block_header as usize) < input.len());
9974
9975 let end_len = LZ4F_compressEnd(
9976 cctx,
9977 encoded.as_mut_ptr().add(header_len + update_len),
9978 encoded.len() - header_len - update_len,
9979 ptr::null(),
9980 );
9981 assert!(!LZ4F_isError(end_len).eq(&1));
9982 encoded.truncate(header_len + update_len + end_len);
9983 LZ4F_freeCompressionContext(cctx);
9984
9985 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
9986 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
9987 let mut output = vec![0u8; input.len()];
9988 let mut src_size = encoded.len();
9989 let mut dst_size = output.len();
9990 let code = LZ4F_decompress(
9991 dctx,
9992 output.as_mut_ptr(),
9993 &mut dst_size,
9994 encoded.as_ptr(),
9995 &mut src_size,
9996 ptr::null(),
9997 );
9998 assert!(!LZ4F_isError(code).eq(&1));
9999 assert_eq!(dst_size, input.len());
10000 assert_eq!(output, input);
10001 LZ4F_freeDecompressionContext(dctx);
10002 }
10003 }
10004
10005 #[test]
10006 fn frame_update_multiblock_dst_too_small_returns_error() {
10007 unsafe {
10008 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10009 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10010 let prefs = LZ4FPreferences {
10011 frame_info: LZ4FFrameInfo {
10012 block_size_id: BlockSize::Max64KB,
10013 block_mode: BlockMode::Independent,
10014 content_checksum_flag: ContentChecksum::NoChecksum,
10015 frame_type: FrameType::Frame,
10016 content_size: 0,
10017 dict_id: 0,
10018 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10019 },
10020 compression_level: 0,
10021 auto_flush: 0,
10022 favor_dec_speed: 0,
10023 reserved: [0; 3],
10024 };
10025 let input = vec![0x5au8; 128 * 1024];
10026 let mut output = vec![0u8; 16];
10027 assert_eq!(
10028 LZ4F_compressBegin(cctx, output.as_mut_ptr(), output.len(), &prefs),
10029 7
10030 );
10031 let code = LZ4F_compressUpdate(
10032 cctx,
10033 output.as_mut_ptr(),
10034 output.len(),
10035 input.as_ptr(),
10036 input.len(),
10037 ptr::null(),
10038 );
10039 assert_eq!(code, ERROR_DST_TOO_SMALL);
10040 LZ4F_freeCompressionContext(cctx);
10041 }
10042 }
10043
10044 #[test]
10045 fn frame_update_buffers_partial_block_until_flush_or_end() {
10046 unsafe {
10047 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10048 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10049 let prefs = LZ4FPreferences {
10050 frame_info: LZ4FFrameInfo {
10051 block_size_id: BlockSize::Max64KB,
10052 block_mode: BlockMode::Independent,
10053 content_checksum_flag: ContentChecksum::NoChecksum,
10054 frame_type: FrameType::Frame,
10055 content_size: 0,
10056 dict_id: 0,
10057 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10058 },
10059 compression_level: 0,
10060 auto_flush: 0,
10061 favor_dec_speed: 0,
10062 reserved: [0; 3],
10063 };
10064 let input = b"buffered partial frame block";
10065 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
10066 let mut pos = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
10067 let update_len = LZ4F_compressUpdate(
10068 cctx,
10069 encoded.as_mut_ptr().add(pos),
10070 encoded.len() - pos,
10071 input.as_ptr(),
10072 input.len(),
10073 ptr::null(),
10074 );
10075 assert_eq!(update_len, 0);
10076 let flush_len = LZ4F_flush(
10077 cctx,
10078 encoded.as_mut_ptr().add(pos),
10079 encoded.len() - pos,
10080 ptr::null(),
10081 );
10082 assert_eq!(LZ4F_isError(flush_len), 0);
10083 assert!(flush_len > 4);
10084 pos += flush_len;
10085 let end_len = LZ4F_compressEnd(
10086 cctx,
10087 encoded.as_mut_ptr().add(pos),
10088 encoded.len() - pos,
10089 ptr::null(),
10090 );
10091 assert_eq!(end_len, 4);
10092 pos += end_len;
10093 encoded.truncate(pos);
10094 LZ4F_freeCompressionContext(cctx);
10095
10096 let decoded = decode_frame_once(&encoded, input.len());
10097 assert_eq!(decoded, input);
10098 }
10099 }
10100
10101 #[test]
10102 fn frame_compress_end_flushes_buffered_partial_block() {
10103 unsafe {
10104 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10105 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10106 let prefs = LZ4FPreferences {
10107 frame_info: LZ4FFrameInfo {
10108 block_size_id: BlockSize::Max64KB,
10109 block_mode: BlockMode::Independent,
10110 content_checksum_flag: ContentChecksum::NoChecksum,
10111 frame_type: FrameType::Frame,
10112 content_size: 0,
10113 dict_id: 0,
10114 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10115 },
10116 compression_level: 0,
10117 auto_flush: 0,
10118 favor_dec_speed: 0,
10119 reserved: [0; 3],
10120 };
10121 let input = b"finish flushes this tail";
10122 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
10123 let mut pos = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
10124 assert_eq!(
10125 LZ4F_compressUpdate(
10126 cctx,
10127 encoded.as_mut_ptr().add(pos),
10128 encoded.len() - pos,
10129 input.as_ptr(),
10130 input.len(),
10131 ptr::null(),
10132 ),
10133 0
10134 );
10135 let end_len = LZ4F_compressEnd(
10136 cctx,
10137 encoded.as_mut_ptr().add(pos),
10138 encoded.len() - pos,
10139 ptr::null(),
10140 );
10141 assert_eq!(LZ4F_isError(end_len), 0);
10142 assert!(end_len > 4);
10143 pos += end_len;
10144 encoded.truncate(pos);
10145 LZ4F_freeCompressionContext(cctx);
10146
10147 let decoded = decode_frame_once(&encoded, input.len());
10148 assert_eq!(decoded, input);
10149 }
10150 }
10151
10152 #[test]
10153 fn frame_compress_end_rejects_declared_content_size_mismatch() {
10154 unsafe {
10155 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10156 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10157 let input = b"short";
10158 let prefs = LZ4FPreferences {
10159 frame_info: LZ4FFrameInfo {
10160 block_size_id: BlockSize::Max64KB,
10161 block_mode: BlockMode::Independent,
10162 content_checksum_flag: ContentChecksum::NoChecksum,
10163 frame_type: FrameType::Frame,
10164 content_size: input.len() as u64 + 1,
10165 dict_id: 0,
10166 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10167 },
10168 compression_level: 0,
10169 auto_flush: 0,
10170 favor_dec_speed: 0,
10171 reserved: [0; 3],
10172 };
10173 let mut output = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
10174 let mut pos = LZ4F_compressBegin(cctx, output.as_mut_ptr(), output.len(), &prefs);
10175 assert_eq!(LZ4F_isError(pos), 0);
10176 let update_len = LZ4F_compressUpdate(
10177 cctx,
10178 output.as_mut_ptr().add(pos),
10179 output.len() - pos,
10180 input.as_ptr(),
10181 input.len(),
10182 ptr::null(),
10183 );
10184 assert_eq!(update_len, 0);
10185 pos += update_len;
10186 assert_eq!(
10187 LZ4F_compressEnd(
10188 cctx,
10189 output.as_mut_ptr().add(pos),
10190 output.len() - pos,
10191 ptr::null(),
10192 ),
10193 ERROR_FRAME_SIZE_WRONG
10194 );
10195 LZ4F_freeCompressionContext(cctx);
10196 }
10197 }
10198
10199 #[test]
10200 fn frame_compress_frame_corrects_nonzero_declared_content_size() {
10201 unsafe {
10202 let input = b"actual content size";
10203 let mut prefs = LZ4FPreferences {
10204 frame_info: LZ4FFrameInfo {
10205 block_size_id: BlockSize::Max64KB,
10206 block_mode: BlockMode::Independent,
10207 content_checksum_flag: ContentChecksum::NoChecksum,
10208 frame_type: FrameType::Frame,
10209 content_size: input.len() as u64 + 100,
10210 dict_id: 0,
10211 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10212 },
10213 compression_level: 0,
10214 auto_flush: 0,
10215 favor_dec_speed: 0,
10216 reserved: [0; 3],
10217 };
10218 let mut encoded = vec![0u8; LZ4F_compressFrameBound(input.len(), &prefs)];
10219 let encoded_len = LZ4F_compressFrame(
10220 encoded.as_mut_ptr() as *mut c_void,
10221 encoded.len(),
10222 input.as_ptr() as *const c_void,
10223 input.len(),
10224 &prefs,
10225 );
10226 assert_eq!(LZ4F_isError(encoded_len), 0);
10227 encoded.truncate(encoded_len);
10228 assert_eq!(decode_frame_once(&encoded, input.len()), input);
10229
10230 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10231 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10232 prefs.frame_info.content_size = 0;
10233 let mut info = prefs.frame_info;
10234 let mut src_size = encoded.len();
10235 assert_eq!(
10236 LZ4F_getFrameInfo(dctx, &mut info, encoded.as_ptr(), &mut src_size),
10237 0
10238 );
10239 assert_eq!(info.content_size, input.len() as u64);
10240 LZ4F_freeDecompressionContext(dctx);
10241 }
10242 }
10243
10244 #[test]
10245 fn frame_linked_blocks_use_previous_block_history() {
10246 unsafe {
10247 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10248 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10249 let prefs = LZ4FPreferences {
10250 frame_info: LZ4FFrameInfo {
10251 block_size_id: BlockSize::Max64KB,
10252 block_mode: BlockMode::Linked,
10253 content_checksum_flag: ContentChecksum::NoChecksum,
10254 frame_type: FrameType::Frame,
10255 content_size: 0,
10256 dict_id: 0,
10257 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10258 },
10259 compression_level: 9,
10260 auto_flush: 0,
10261 favor_dec_speed: 0,
10262 reserved: [0; 3],
10263 };
10264 let first = b"abcdefghijklmnop";
10265 let second = b"abcdefghijklmnop";
10266 let mut encoded = vec![0u8; 256];
10267 let mut pos = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
10268
10269 let mut dctx_info = LZ4FDecompressionContext(ptr::null_mut());
10270 assert_eq!(
10271 LZ4F_createDecompressionContext(&mut dctx_info, LZ4F_VERSION),
10272 0
10273 );
10274 let mut info = LZ4FFrameInfo {
10275 block_size_id: BlockSize::Default,
10276 block_mode: BlockMode::Independent,
10277 content_checksum_flag: ContentChecksum::NoChecksum,
10278 frame_type: FrameType::Frame,
10279 content_size: 0,
10280 dict_id: 0,
10281 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10282 };
10283 let mut header_size = pos;
10284 assert_eq!(
10285 LZ4F_getFrameInfo(dctx_info, &mut info, encoded.as_ptr(), &mut header_size),
10286 0
10287 );
10288 assert!(matches!(info.block_mode, BlockMode::Linked));
10289 LZ4F_freeDecompressionContext(dctx_info);
10290
10291 pos += LZ4F_compressUpdate(
10292 cctx,
10293 encoded.as_mut_ptr().add(pos),
10294 encoded.len() - pos,
10295 first.as_ptr(),
10296 first.len(),
10297 ptr::null(),
10298 );
10299 let second_block_at = pos;
10300 let second_len = LZ4F_compressUpdate(
10301 cctx,
10302 encoded.as_mut_ptr().add(pos),
10303 encoded.len() - pos,
10304 second.as_ptr(),
10305 second.len(),
10306 ptr::null(),
10307 );
10308 pos += second_len;
10309 let second_header = u32::from_le_bytes(
10310 encoded[second_block_at..second_block_at + 4]
10311 .try_into()
10312 .unwrap(),
10313 );
10314 assert_eq!(second_header & 0x8000_0000, 0);
10315 assert!((second_header as usize) < second.len() + 1);
10316 pos += LZ4F_compressEnd(
10317 cctx,
10318 encoded.as_mut_ptr().add(pos),
10319 encoded.len() - pos,
10320 ptr::null(),
10321 );
10322 encoded.truncate(pos);
10323 LZ4F_freeCompressionContext(cctx);
10324
10325 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10326 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10327 let mut output = vec![0u8; first.len() + second.len()];
10328 let mut src_offset = 0usize;
10329 let mut dst_offset = 0usize;
10330 loop {
10331 let mut src_size = encoded.len() - src_offset;
10332 let mut dst_size = output.len() - dst_offset;
10333 let code = LZ4F_decompress(
10334 dctx,
10335 output[dst_offset..].as_mut_ptr(),
10336 &mut dst_size,
10337 encoded.as_ptr().add(src_offset),
10338 &mut src_size,
10339 ptr::null(),
10340 );
10341 assert_eq!(LZ4F_isError(code), 0);
10342 src_offset += src_size;
10343 dst_offset += dst_size;
10344 if code == 0 {
10345 break;
10346 }
10347 }
10348 assert_eq!(dst_offset, output.len());
10349 assert_eq!(&output[..first.len()], first);
10350 assert_eq!(&output[first.len()..], second);
10351 LZ4F_freeDecompressionContext(dctx);
10352 }
10353 }
10354
10355 #[test]
10356 fn frame_decompress_rejects_bad_checksums() {
10357 unsafe {
10358 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10359 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10360 let prefs = LZ4FPreferences {
10361 frame_info: LZ4FFrameInfo {
10362 block_size_id: BlockSize::Max64KB,
10363 block_mode: BlockMode::Independent,
10364 content_checksum_flag: ContentChecksum::ChecksumEnabled,
10365 frame_type: FrameType::Frame,
10366 content_size: 0,
10367 dict_id: 0,
10368 block_checksum_flag: BlockChecksum::BlockChecksumEnabled,
10369 },
10370 compression_level: 9,
10371 auto_flush: 0,
10372 favor_dec_speed: 0,
10373 reserved: [0; 3],
10374 };
10375 let input = b"checksum data ".repeat(1024);
10376 let mut encoded = vec![0u8; LZ4F_compressBound(input.len(), &prefs) + 32];
10377 let header_len = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
10378 let update_len = LZ4F_compressUpdate(
10379 cctx,
10380 encoded.as_mut_ptr().add(header_len),
10381 encoded.len() - header_len,
10382 input.as_ptr(),
10383 input.len(),
10384 ptr::null(),
10385 );
10386 let end_len = LZ4F_compressEnd(
10387 cctx,
10388 encoded.as_mut_ptr().add(header_len + update_len),
10389 encoded.len() - header_len - update_len,
10390 ptr::null(),
10391 );
10392 encoded.truncate(header_len + update_len + end_len);
10393 LZ4F_freeCompressionContext(cctx);
10394
10395 let mut bad_block = encoded.clone();
10396 let block_len =
10397 (u32::from_le_bytes(bad_block[header_len..header_len + 4].try_into().unwrap())
10398 & 0x7FFF_FFFF) as usize;
10399 bad_block[header_len + 4 + block_len] ^= 0x80;
10400 assert_corrupt_frame_fails("block checksum", &bad_block, input.len());
10401 assert_corrupt_frame_decodes_with_skip_checksums("block checksum", &bad_block, &input);
10402 assert_corrupt_frame_decodes_with_sticky_skip_checksums(
10403 "block checksum",
10404 &bad_block,
10405 &input,
10406 header_len,
10407 );
10408
10409 let mut bad_content = encoded;
10410 let last = bad_content.len() - 1;
10411 bad_content[last] ^= 0x80;
10412 assert_corrupt_frame_fails("content checksum", &bad_content, input.len());
10413 assert_corrupt_frame_decodes_with_skip_checksums(
10414 "content checksum",
10415 &bad_content,
10416 &input,
10417 );
10418 assert_corrupt_frame_decodes_with_sticky_skip_checksums(
10419 "content checksum",
10420 &bad_content,
10421 &input,
10422 header_len,
10423 );
10424 }
10425 }
10426
10427 #[test]
10428 fn frame_decompress_raw_block_consumes_only_output_capacity() {
10429 unsafe {
10430 let input = b"raw block incremental payload";
10431 let mut frame = frame_header_for_test(
10432 ContentChecksum::NoChecksum,
10433 BlockChecksum::NoBlockChecksum,
10434 0,
10435 );
10436 frame.extend_from_slice(&((input.len() as u32) | 0x8000_0000).to_le_bytes());
10437 frame.extend_from_slice(input);
10438 frame.extend_from_slice(&0u32.to_le_bytes());
10439
10440 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10441 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10442 let mut info = LZ4FFrameInfo {
10443 block_size_id: BlockSize::Default,
10444 block_mode: BlockMode::Linked,
10445 content_checksum_flag: ContentChecksum::NoChecksum,
10446 frame_type: FrameType::Frame,
10447 content_size: 0,
10448 dict_id: 0,
10449 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10450 };
10451 let mut header_size = frame.len();
10452 assert_eq!(
10453 LZ4F_getFrameInfo(dctx, &mut info, frame.as_ptr(), &mut header_size),
10454 0
10455 );
10456
10457 let payload = &frame[header_size..];
10458 let mut first = [0u8; 5];
10459 let mut src_size = payload.len();
10460 let mut dst_size = first.len();
10461 let hint = LZ4F_decompress(
10462 dctx,
10463 first.as_mut_ptr(),
10464 &mut dst_size,
10465 payload.as_ptr(),
10466 &mut src_size,
10467 ptr::null(),
10468 );
10469 assert_eq!(LZ4F_isError(hint), 0);
10470 assert_eq!(dst_size, first.len());
10471 assert_eq!(src_size, 4 + first.len());
10472 assert_eq!(&first, &input[..first.len()]);
10473
10474 let rest_payload = &payload[src_size..];
10475 let mut rest = vec![0u8; input.len() - first.len()];
10476 let mut rest_src_size = rest_payload.len();
10477 let mut rest_dst_size = rest.len();
10478 let code = LZ4F_decompress(
10479 dctx,
10480 rest.as_mut_ptr(),
10481 &mut rest_dst_size,
10482 rest_payload.as_ptr(),
10483 &mut rest_src_size,
10484 ptr::null(),
10485 );
10486 assert_eq!(code, 0);
10487 assert_eq!(rest_src_size, rest_payload.len());
10488 assert_eq!(rest_dst_size, rest.len());
10489 assert_eq!(&rest, &input[first.len()..]);
10490 LZ4F_freeDecompressionContext(dctx);
10491 }
10492 }
10493
10494 #[test]
10495 fn frame_decompress_rejects_bad_header_checksum() {
10496 unsafe {
10497 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10498 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10499 let prefs = LZ4FPreferences {
10500 frame_info: LZ4FFrameInfo {
10501 block_size_id: BlockSize::Max64KB,
10502 block_mode: BlockMode::Independent,
10503 content_checksum_flag: ContentChecksum::NoChecksum,
10504 frame_type: FrameType::Frame,
10505 content_size: 0,
10506 dict_id: 0,
10507 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10508 },
10509 compression_level: 0,
10510 auto_flush: 0,
10511 favor_dec_speed: 0,
10512 reserved: [0; 3],
10513 };
10514 let input = b"header checksum";
10515 let mut encoded = vec![0u8; 128];
10516 let mut pos = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
10517 encoded[pos - 1] ^= 0x80;
10518 pos += LZ4F_compressUpdate(
10519 cctx,
10520 encoded.as_mut_ptr().add(pos),
10521 encoded.len() - pos,
10522 input.as_ptr(),
10523 input.len(),
10524 ptr::null(),
10525 );
10526 pos += LZ4F_compressEnd(
10527 cctx,
10528 encoded.as_mut_ptr().add(pos),
10529 encoded.len() - pos,
10530 ptr::null(),
10531 );
10532 encoded.truncate(pos);
10533 LZ4F_freeCompressionContext(cctx);
10534
10535 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10536 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10537 let mut output = vec![0u8; input.len()];
10538 let mut src_size = encoded.len();
10539 let mut dst_size = output.len();
10540 let code = LZ4F_decompress(
10541 dctx,
10542 output.as_mut_ptr(),
10543 &mut dst_size,
10544 encoded.as_ptr(),
10545 &mut src_size,
10546 ptr::null(),
10547 );
10548 assert_eq!(code, ERROR_HEADER_CHECKSUM_INVALID);
10549 LZ4F_freeDecompressionContext(dctx);
10550 }
10551 }
10552
10553 #[test]
10554 fn frame_decompress_rejects_malformed_compressed_block() {
10555 unsafe {
10556 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10557 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10558 let prefs = LZ4FPreferences {
10559 frame_info: LZ4FFrameInfo {
10560 block_size_id: BlockSize::Max64KB,
10561 block_mode: BlockMode::Independent,
10562 content_checksum_flag: ContentChecksum::NoChecksum,
10563 frame_type: FrameType::Frame,
10564 content_size: 0,
10565 dict_id: 0,
10566 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10567 },
10568 compression_level: 0,
10569 auto_flush: 0,
10570 favor_dec_speed: 0,
10571 reserved: [0; 3],
10572 };
10573 let mut encoded = vec![0u8; 32];
10574 let header_len = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
10575 assert_eq!(LZ4F_isError(header_len), 0);
10576 encoded.truncate(header_len);
10577 LZ4F_freeCompressionContext(cctx);
10578
10579 let cases = [
10580 ("offset past output", [0x00, 0x01, 0x00]),
10581 ("zero offset", [0x00, 0x00, 0x00]),
10582 ];
10583 for (case, payload) in cases {
10584 let mut frame = encoded.clone();
10585 frame.extend_from_slice(&(payload.len() as u32).to_le_bytes());
10586 frame.extend_from_slice(&payload);
10587
10588 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10589 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10590 let mut output = vec![0u8; 64 * 1024];
10591 let mut src_size = frame.len();
10592 let mut dst_size = output.len();
10593 let code = LZ4F_decompress(
10594 dctx,
10595 output.as_mut_ptr(),
10596 &mut dst_size,
10597 frame.as_ptr(),
10598 &mut src_size,
10599 ptr::null(),
10600 );
10601 assert_eq!(code, ERROR_DECOMPRESSION_FAILED, "{case}");
10602 LZ4F_freeDecompressionContext(dctx);
10603 }
10604 }
10605 }
10606
10607 #[test]
10608 fn frame_decompress_reports_hints_for_truncated_frame_parts() {
10609 unsafe {
10610 let no_checksum = frame_header_for_test(
10611 ContentChecksum::NoChecksum,
10612 BlockChecksum::NoBlockChecksum,
10613 0,
10614 );
10615 assert_incomplete_frame_hint(
10616 "partial block header",
10617 {
10618 let mut frame = no_checksum.clone();
10619 frame.extend_from_slice(&[0x05, 0x00]);
10620 frame
10621 },
10622 2,
10623 0,
10624 );
10625 assert_incomplete_frame_hint(
10626 "partial raw block payload",
10627 {
10628 let mut frame = no_checksum.clone();
10629 frame.extend_from_slice(&(0x8000_0000u32 | 5).to_le_bytes());
10630 frame.extend_from_slice(b"ab");
10631 frame
10632 },
10633 7,
10634 2,
10635 );
10636
10637 let with_block_checksum = frame_header_for_test(
10638 ContentChecksum::NoChecksum,
10639 BlockChecksum::BlockChecksumEnabled,
10640 0,
10641 );
10642 assert_incomplete_frame_hint(
10643 "missing block checksum",
10644 {
10645 let mut frame = with_block_checksum;
10646 frame.extend_from_slice(&(0x8000_0000u32 | 3).to_le_bytes());
10647 frame.extend_from_slice(b"abc");
10648 frame
10649 },
10650 4,
10651 3,
10652 );
10653
10654 let with_content_checksum = frame_header_for_test(
10655 ContentChecksum::ChecksumEnabled,
10656 BlockChecksum::NoBlockChecksum,
10657 0,
10658 );
10659 assert_incomplete_frame_hint(
10660 "missing content checksum trailer",
10661 {
10662 let mut frame = with_content_checksum;
10663 frame.extend_from_slice(&(0x8000_0000u32 | 3).to_le_bytes());
10664 frame.extend_from_slice(b"abc");
10665 frame.extend_from_slice(&0u32.to_le_bytes());
10666 frame
10667 },
10668 4,
10669 3,
10670 );
10671 }
10672 }
10673
10674 #[test]
10675 fn frame_decompress_rejects_content_size_mismatch() {
10676 unsafe {
10677 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10678 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10679 let input = b"wrong content size";
10680 let prefs = LZ4FPreferences {
10681 frame_info: LZ4FFrameInfo {
10682 block_size_id: BlockSize::Max64KB,
10683 block_mode: BlockMode::Independent,
10684 content_checksum_flag: ContentChecksum::NoChecksum,
10685 frame_type: FrameType::Frame,
10686 content_size: input.len() as u64 + 1,
10687 dict_id: 0,
10688 block_checksum_flag: BlockChecksum::NoBlockChecksum,
10689 },
10690 compression_level: 0,
10691 auto_flush: 0,
10692 favor_dec_speed: 0,
10693 reserved: [0; 3],
10694 };
10695 let mut encoded = vec![0u8; 128];
10696 let mut pos = LZ4F_compressBegin(cctx, encoded.as_mut_ptr(), encoded.len(), &prefs);
10697 assert_eq!(LZ4F_isError(pos), 0);
10698 encoded[pos..pos + 4]
10699 .copy_from_slice(&((input.len() as u32) | 0x8000_0000).to_le_bytes());
10700 pos += 4;
10701 encoded[pos..pos + input.len()].copy_from_slice(input);
10702 pos += input.len();
10703 encoded[pos..pos + 4].copy_from_slice(&0u32.to_le_bytes());
10704 pos += 4;
10705 encoded.truncate(pos);
10706 LZ4F_freeCompressionContext(cctx);
10707
10708 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10709 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10710 let mut output = vec![0u8; input.len()];
10711 let mut src_size = encoded.len();
10712 let mut dst_size = output.len();
10713 let code = LZ4F_decompress(
10714 dctx,
10715 output.as_mut_ptr(),
10716 &mut dst_size,
10717 encoded.as_ptr(),
10718 &mut src_size,
10719 ptr::null(),
10720 );
10721 assert_eq!(code, ERROR_FRAME_SIZE_WRONG);
10722 LZ4F_freeDecompressionContext(dctx);
10723 }
10724 }
10725
10726 fn assert_corrupt_frame_fails(kind: &str, encoded: &[u8], output_len: usize) {
10727 unsafe {
10728 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10729 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10730 let mut output = vec![0u8; output_len];
10731 let mut offset = 0usize;
10732 for _ in 0..8 {
10733 let mut src_size = encoded.len() - offset;
10734 let src_ptr = if src_size == 0 {
10735 ptr::null()
10736 } else {
10737 encoded.as_ptr().add(offset)
10738 };
10739 let mut dst_size = output.len();
10740 let code = LZ4F_decompress(
10741 dctx,
10742 output.as_mut_ptr(),
10743 &mut dst_size,
10744 src_ptr,
10745 &mut src_size,
10746 ptr::null(),
10747 );
10748 if code == ERROR_BLOCK_CHECKSUM_INVALID || code == ERROR_CHECKSUM_INVALID {
10749 LZ4F_freeDecompressionContext(dctx);
10750 return;
10751 }
10752 assert_eq!(LZ4F_isError(code), 0);
10753 assert_ne!(code, 0, "{kind} corrupt frame decoded successfully");
10754 offset += src_size;
10755 }
10756 panic!("{kind} corrupt frame did not report checksum failure");
10757 }
10758 }
10759
10760 fn assert_corrupt_frame_decodes_with_skip_checksums(kind: &str, encoded: &[u8], input: &[u8]) {
10761 unsafe {
10762 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10763 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10764 let options = LZ4FDecompressOptions {
10765 stable_dst: 0,
10766 skipChecksums: 1,
10767 reserved: [0; 2],
10768 };
10769 let mut output = vec![0u8; input.len()];
10770 let mut src_size = encoded.len();
10771 let mut dst_size = output.len();
10772 let code = LZ4F_decompress(
10773 dctx,
10774 output.as_mut_ptr(),
10775 &mut dst_size,
10776 encoded.as_ptr(),
10777 &mut src_size,
10778 &options,
10779 );
10780 assert_eq!(code, 0, "{kind}");
10781 assert_eq!(src_size, encoded.len(), "{kind}");
10782 assert_eq!(dst_size, input.len(), "{kind}");
10783 assert_eq!(output, input, "{kind}");
10784 LZ4F_freeDecompressionContext(dctx);
10785 }
10786 }
10787
10788 fn assert_corrupt_frame_decodes_with_sticky_skip_checksums(
10789 kind: &str,
10790 encoded: &[u8],
10791 input: &[u8],
10792 header_len: usize,
10793 ) {
10794 unsafe {
10795 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10796 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10797 let options = LZ4FDecompressOptions {
10798 stable_dst: 0,
10799 skipChecksums: 1,
10800 reserved: [0; 2],
10801 };
10802 let mut src_size = header_len;
10803 let mut dst_size = 0usize;
10804 let code = LZ4F_decompress(
10805 dctx,
10806 ptr::null_mut(),
10807 &mut dst_size,
10808 encoded.as_ptr(),
10809 &mut src_size,
10810 &options,
10811 );
10812 assert_eq!(LZ4F_isError(code), 0, "{kind}");
10813 assert_eq!(src_size, header_len, "{kind}");
10814 assert_eq!(dst_size, 0, "{kind}");
10815
10816 let mut output = vec![0u8; input.len()];
10817 src_size = encoded.len() - header_len;
10818 dst_size = output.len();
10819 let code = LZ4F_decompress(
10820 dctx,
10821 output.as_mut_ptr(),
10822 &mut dst_size,
10823 encoded.as_ptr().add(header_len),
10824 &mut src_size,
10825 ptr::null(),
10826 );
10827 assert_eq!(code, 0, "{kind}");
10828 assert_eq!(src_size, encoded.len() - header_len, "{kind}");
10829 assert_eq!(dst_size, input.len(), "{kind}");
10830 assert_eq!(output, input, "{kind}");
10831 LZ4F_freeDecompressionContext(dctx);
10832 }
10833 }
10834
10835 unsafe fn frame_header_for_test(
10836 content_checksum: ContentChecksum,
10837 block_checksum: BlockChecksum,
10838 content_size: u64,
10839 ) -> Vec<u8> {
10840 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
10841 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
10842 let prefs = LZ4FPreferences {
10843 frame_info: LZ4FFrameInfo {
10844 block_size_id: BlockSize::Max64KB,
10845 block_mode: BlockMode::Independent,
10846 content_checksum_flag: content_checksum,
10847 frame_type: FrameType::Frame,
10848 content_size,
10849 dict_id: 0,
10850 block_checksum_flag: block_checksum,
10851 },
10852 compression_level: 0,
10853 auto_flush: 0,
10854 favor_dec_speed: 0,
10855 reserved: [0; 3],
10856 };
10857 let mut header = vec![0u8; 32];
10858 let header_len = LZ4F_compressBegin(cctx, header.as_mut_ptr(), header.len(), &prefs);
10859 assert_eq!(LZ4F_isError(header_len), 0);
10860 LZ4F_freeCompressionContext(cctx);
10861 header.truncate(header_len);
10862 header
10863 }
10864
10865 unsafe fn assert_incomplete_frame_hint(
10866 case: &str,
10867 frame: Vec<u8>,
10868 expected_hint: usize,
10869 expected_output: usize,
10870 ) {
10871 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10872 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10873 let mut output = vec![0u8; 64 * 1024];
10874 let mut src_size = frame.len();
10875 let mut dst_size = output.len();
10876 let code = LZ4F_decompress(
10877 dctx,
10878 output.as_mut_ptr(),
10879 &mut dst_size,
10880 frame.as_ptr(),
10881 &mut src_size,
10882 ptr::null(),
10883 );
10884 assert_eq!(LZ4F_isError(code), 0, "{case}");
10885 assert_eq!(code, expected_hint, "{case}");
10886 assert_eq!(dst_size, expected_output, "{case}");
10887 assert_eq!(src_size, frame.len(), "{case}");
10888 LZ4F_freeDecompressionContext(dctx);
10889 }
10890
10891 unsafe fn decode_frame_once(frame: &[u8], output_len: usize) -> Vec<u8> {
10892 let mut dctx = LZ4FDecompressionContext(ptr::null_mut());
10893 assert_eq!(LZ4F_createDecompressionContext(&mut dctx, LZ4F_VERSION), 0);
10894 let mut output = vec![0u8; output_len + 16];
10895 let mut src_offset = 0usize;
10896 let mut dst_offset = 0usize;
10897 loop {
10898 let mut src_size = frame.len() - src_offset;
10899 let mut dst_size = output.len() - dst_offset;
10900 let code = LZ4F_decompress(
10901 dctx,
10902 output.as_mut_ptr().add(dst_offset),
10903 &mut dst_size,
10904 frame.as_ptr().add(src_offset),
10905 &mut src_size,
10906 ptr::null(),
10907 );
10908 assert_eq!(LZ4F_isError(code), 0);
10909 src_offset += src_size;
10910 dst_offset += dst_size;
10911 if code == 0 {
10912 break;
10913 }
10914 }
10915 assert_eq!(src_offset, frame.len());
10916 output.truncate(dst_offset);
10917 LZ4F_freeDecompressionContext(dctx);
10918 output
10919 }
10920
10921 #[test]
10922 fn hc_round_trip_and_improves_repetitive_block() {
10923 let mut input = Vec::new();
10924 for _ in 0..4096 {
10925 input.extend_from_slice(b"the quick brown fox jumps over the lazy dog. ");
10926 }
10927
10928 let bound = unsafe { LZ4_compressBound(input.len() as c_int) } as usize;
10929 let mut fast = vec![0u8; bound];
10930 let fast_len = unsafe {
10931 LZ4_compress_default(
10932 input.as_ptr() as *const c_char,
10933 fast.as_mut_ptr() as *mut c_char,
10934 input.len() as c_int,
10935 fast.len() as c_int,
10936 )
10937 };
10938 assert!(fast_len > 0);
10939
10940 let mut hc = vec![0u8; bound];
10941 let hc_len = unsafe {
10942 LZ4_compress_HC(
10943 input.as_ptr() as *const c_char,
10944 hc.as_mut_ptr() as *mut c_char,
10945 input.len() as c_int,
10946 hc.len() as c_int,
10947 9,
10948 )
10949 };
10950 assert!(hc_len > 0);
10951 assert!(hc_len <= fast_len);
10952
10953 let mut output = vec![0u8; input.len()];
10954 let output_len = unsafe {
10955 LZ4_decompress_safe(
10956 hc.as_ptr() as *const c_char,
10957 output.as_mut_ptr() as *mut c_char,
10958 hc_len,
10959 output.len() as c_int,
10960 )
10961 };
10962 assert_eq!(output_len as usize, input.len());
10963 assert_eq!(output, input);
10964 }
10965
10966 #[test]
10967 fn hc_levels_round_trip_varied_inputs() {
10968 let mut inputs = Vec::new();
10969 inputs.push((0..4096).map(|n| (n & 0xff) as u8).collect::<Vec<_>>());
10970 inputs.push(vec![b'a'; 128 * 1024]);
10971 let mut patterned = Vec::new();
10972 for n in 0..32 * 1024 {
10973 patterned.push(if n % 97 < 64 {
10974 b"ACGT"[(n / 3) % 4]
10975 } else {
10976 (n & 0xff) as u8
10977 });
10978 }
10979 inputs.push(patterned);
10980
10981 for input in inputs {
10982 let bound = unsafe { LZ4_compressBound(input.len() as c_int) } as usize;
10983 for level in 1..=12 {
10984 let mut compressed = vec![0u8; bound];
10985 let compressed_len = unsafe {
10986 LZ4_compress_HC(
10987 input.as_ptr() as *const c_char,
10988 compressed.as_mut_ptr() as *mut c_char,
10989 input.len() as c_int,
10990 compressed.len() as c_int,
10991 level,
10992 )
10993 };
10994 assert!(compressed_len > 0, "level {level}");
10995
10996 let mut output = vec![0u8; input.len()];
10997 let output_len = unsafe {
10998 LZ4_decompress_safe(
10999 compressed.as_ptr() as *const c_char,
11000 output.as_mut_ptr() as *mut c_char,
11001 compressed_len,
11002 output.len() as c_int,
11003 )
11004 };
11005 assert_eq!(output_len as usize, input.len(), "level {level}");
11006 assert_eq!(output, input, "level {level}");
11007 }
11008 }
11009 }
11010
11011 #[test]
11012 fn hc_matches_upstream_bytes_for_representative_levels() {
11013 let cases = [
11014 (
11015 1,
11016 patterned_hc_input(128),
11017 "ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000d4e00503334353637",
11018 ),
11019 (
11020 2,
11021 patterned_hc_input(1024),
11022 "ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000f4e00320f9c0000144662000d1a000fd00034144562000f4e00320f520100144462000d1a000f86013403e3012f43444e00320f86010003e3012d42431a000f860134144162000f4e00320f860100144762000d1a000f860134144662000f4e00320f86010003e3012d45461a000f86010450666768696a",
11023 ),
11024 (
11025 12,
11026 b"the quick brown fox jumps over the lazy dog. "
11027 .iter()
11028 .copied()
11029 .cycle()
11030 .take(4096)
11031 .collect::<Vec<_>>(),
11032 "f01074686520717569636b2062726f776e20666f78206a756d7073206f766572201f00af6c617a7920646f672e202d00ffffffffffffffffffffffffffffffca506f672e2074",
11033 ),
11034 (
11035 9,
11036 patterned_hc_input(128),
11037 "ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000d1a00503334353637",
11038 ),
11039 (
11040 10,
11041 patterned_hc_input(1024),
11042 "ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000f4e00320f1a0000144662000fb60039081a00144562000f1e0140011a00144462000f1e0140011a00144362000f1e0140011a00144262000f1e0140011a0005a7020fa4024235333435a7020fa40242356d6e6fa7020fa4024235666768a7020f34001550666768696a",
11043 ),
11044 ];
11045
11046 for (level, input, expected_hex) in cases {
11047 let expected = decode_hex(expected_hex);
11048 let mut compressed =
11049 vec![0u8; unsafe { LZ4_compressBound(input.len() as c_int) } as usize];
11050 let compressed_len = unsafe {
11051 LZ4_compress_HC(
11052 input.as_ptr() as *const c_char,
11053 compressed.as_mut_ptr() as *mut c_char,
11054 input.len() as c_int,
11055 compressed.len() as c_int,
11056 level,
11057 )
11058 };
11059 assert_eq!(compressed_len as usize, expected.len(), "level {level}");
11060 assert_eq!(
11061 &compressed[..compressed_len as usize],
11062 &expected,
11063 "level {level}"
11064 );
11065 }
11066 }
11067
11068 #[test]
11069 fn fast_matches_upstream_bytes_for_representative_blocks() {
11070 let cases = [
11071 (
11072 1,
11073 b"the quick brown fox jumps over the lazy dog. "
11074 .iter()
11075 .copied()
11076 .cycle()
11077 .take(4096)
11078 .collect::<Vec<_>>(),
11079 "f01074686520717569636b2062726f776e20666f78206a756d7073206f766572201f00916c617a7920646f672e0e000f2d00ffffffffffffffffffffffffffffffc6506f672e2074",
11080 ),
11081 (
11082 1,
11083 patterned_hc_input(512),
11084 "ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000f4e00320f9c000000bd0001c4000fb60039086800011f010062000f1e01400168000281013f4344456c012d014e000fba010000bd0001880109d401506e6f703031",
11085 ),
11086 (
11087 4,
11088 patterned_hc_input(512),
11089 "ff2e4142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768696a6b6c6d6e6f7030313233343536373839616263646566676869340011144762000f3400180f68001a144662000fb60039088200144562000f1e0140016800144462000f6c012d014e000fa0010003e301294344d401506e6f703031",
11090 ),
11091 ];
11092
11093 for (acceleration, input, expected_hex) in cases {
11094 let expected = decode_hex(expected_hex);
11095 let mut compressed =
11096 vec![0u8; unsafe { LZ4_compressBound(input.len() as c_int) } as usize];
11097 let compressed_len = unsafe {
11098 LZ4_compress_fast(
11099 input.as_ptr() as *const c_char,
11100 compressed.as_mut_ptr() as *mut c_char,
11101 input.len() as c_int,
11102 compressed.len() as c_int,
11103 acceleration,
11104 )
11105 };
11106 assert_eq!(compressed_len as usize, expected.len(), "{acceleration}");
11107 assert_eq!(
11108 &compressed[..compressed_len as usize],
11109 &expected,
11110 "{acceleration}"
11111 );
11112 }
11113 }
11114
11115 #[test]
11116 fn fast_continue_matches_upstream_bytes_with_dictionary() {
11117 unsafe {
11118 let dict = b"abcdefghijklmnop0123456789abcdefghijklmnop0123456789";
11119 let input = b"abcdefghijklmnop0123456789ZZabcdefghijklmnop0123456789";
11120 for acceleration in [1, 4] {
11121 let expected = decode_hex("0f1a00072f5a5a1c0002503536373839");
11122 let stream = LZ4_createStream();
11123 assert!(!stream.is_null());
11124 assert_eq!(
11125 LZ4_loadDict(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
11126 dict.len() as c_int
11127 );
11128
11129 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
11130 let compressed_len = LZ4_compress_fast_continue(
11131 stream,
11132 input.as_ptr() as *const c_char,
11133 compressed.as_mut_ptr() as *mut c_char,
11134 input.len() as c_int,
11135 compressed.len() as c_int,
11136 acceleration,
11137 );
11138 LZ4_freeStream(stream);
11139
11140 assert_eq!(compressed_len as usize, expected.len(), "{acceleration}");
11141 assert_eq!(
11142 &compressed[..compressed_len as usize],
11143 &expected,
11144 "{acceleration}"
11145 );
11146 }
11147 }
11148 }
11149
11150 #[test]
11151 fn hc_frame_matches_upstream_bytes_for_representative_input() {
11152 unsafe {
11153 let input = patterned_hc_input(1024);
11154 let cases = [
11155 (
11156 9,
11157 "04224d186440a78f000000ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000e4e000f680033144662000eb6000f680033144562000f1e0140011a00144462002f6869860143144362002f61628601430445023f43333486014305a7022f6d6e86014305a7022f666786014305a7022f383986014305a7020f34001550666768696a00000000e112173a",
11158 ),
11159 (
11160 10,
11161 "04224d186440a796000000ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000f4e00320f1a0000144662000fb60039081a00144562000f1e0140011a00144462000f1e0140011a00144362000f1e0140011a00144262000f1e0140011a0005a7020fa4024235333435a7020fa40242356d6e6fa7020fa4024235666768a7020f34001550666768696a00000000e112173a",
11162 ),
11163 (
11164 12,
11165 "04224d186440a78f000000ff144142434445464741426a6b6c6d6e6f70303132333435363738396162636465666768691a002b144762000e4e000f680033144662000fb60039081a00144562000f1e0140011a00144462002f6869860143144362002f6162860143144262002f333486014305a7022f6d6e86014305a7022f666786014305a7022f383986014305a7020f34001550666768696a00000000e112173a",
11166 ),
11167 ];
11168
11169 for (level, expected_hex) in cases {
11170 let expected = decode_hex(expected_hex);
11171 let prefs = hc_frame_fixture_prefs(level);
11172 let bound = LZ4F_compressFrameBound(input.len(), &prefs);
11173 let mut encoded = vec![0u8; bound];
11174 let encoded_len = LZ4F_compressFrame(
11175 encoded.as_mut_ptr() as *mut c_void,
11176 encoded.len(),
11177 input.as_ptr() as *const c_void,
11178 input.len(),
11179 &prefs,
11180 );
11181
11182 assert_eq!(LZ4F_isError(encoded_len), 0, "level {level}");
11183 assert_eq!(encoded_len, expected.len(), "level {level}");
11184 assert_eq!(&encoded[..encoded_len], &expected, "level {level}");
11185 }
11186 }
11187 }
11188
11189 #[test]
11190 fn frame_levels_one_and_two_use_fast_path() {
11191 unsafe {
11192 let input = patterned_hc_input(32 * 1024);
11193 let mut expected = Vec::new();
11194 for level in [0, 1, 2] {
11195 let prefs = hc_frame_fixture_prefs(level);
11196 let mut encoded = vec![0u8; LZ4F_compressFrameBound(input.len(), &prefs)];
11197 let encoded_len = LZ4F_compressFrame(
11198 encoded.as_mut_ptr() as *mut c_void,
11199 encoded.len(),
11200 input.as_ptr() as *const c_void,
11201 input.len(),
11202 &prefs,
11203 );
11204 assert_eq!(LZ4F_isError(encoded_len), 0, "level {level}");
11205 encoded.truncate(encoded_len);
11206 if level == 0 {
11207 expected = encoded;
11208 } else {
11209 assert_eq!(encoded, expected, "level {level}");
11210 }
11211 }
11212 }
11213 }
11214
11215 #[test]
11216 fn hc_frame_cdict_matches_upstream_bytes() {
11217 unsafe {
11218 let dict = b"abcdefghijklmnop0123456789abcdefghijklmnop0123456789";
11219 let input = b"abcdefghijklmnop0123456789ZZabcdefghijklmnop0123456789";
11220 let expected = decode_hex(
11221 "04224d186440a7100000000f1a00072f5a5a1c000250353637383900000000af554433",
11222 );
11223 let prefs = hc_frame_fixture_prefs(9);
11224 let cdict = LZ4F_createCDict(dict.as_ptr() as *const c_void, dict.len());
11225 assert!(!cdict.is_null());
11226 let mut cctx = LZ4FCompressionContext(ptr::null_mut());
11227 assert_eq!(LZ4F_createCompressionContext(&mut cctx, LZ4F_VERSION), 0);
11228 let mut encoded = vec![0u8; LZ4F_compressFrameBound(input.len(), &prefs)];
11229 let encoded_len = LZ4F_compressFrame_usingCDict(
11230 cctx,
11231 encoded.as_mut_ptr() as *mut c_void,
11232 encoded.len(),
11233 input.as_ptr() as *const c_void,
11234 input.len(),
11235 cdict,
11236 &prefs,
11237 );
11238 LZ4F_freeCompressionContext(cctx);
11239 LZ4F_freeCDict(cdict);
11240
11241 assert_eq!(LZ4F_isError(encoded_len), 0);
11242 assert_eq!(encoded_len, expected.len());
11243 assert_eq!(&encoded[..encoded_len], &expected);
11244 }
11245 }
11246
11247 #[test]
11248 fn hc_multiblock_frame_matches_upstream_hashes() {
11249 unsafe {
11250 let input = patterned_hc_input(150_000);
11251 let cases = [
11252 (9, 4504usize, 0x859b_76b8u32),
11253 (12, 4504usize, 0x8eb7_3b33u32),
11254 ];
11255
11256 for (level, expected_len, expected_hash) in cases {
11257 let prefs = hc_frame_fixture_prefs(level);
11258 let mut encoded = vec![0u8; LZ4F_compressFrameBound(input.len(), &prefs)];
11259 let encoded_len = LZ4F_compressFrame(
11260 encoded.as_mut_ptr() as *mut c_void,
11261 encoded.len(),
11262 input.as_ptr() as *const c_void,
11263 input.len(),
11264 &prefs,
11265 );
11266
11267 assert_eq!(LZ4F_isError(encoded_len), 0, "level {level}");
11268 assert_eq!(encoded_len, expected_len, "level {level}");
11269 assert_eq!(
11270 xxhash32(&encoded[..encoded_len], 0),
11271 expected_hash,
11272 "level {level}"
11273 );
11274 }
11275 }
11276 }
11277
11278 #[test]
11279 fn hc_large_frame_matches_upstream_hash() {
11280 unsafe {
11281 let input = patterned_hc_input(8 * 1024 * 1024);
11282 let prefs = hc_frame_fixture_prefs(9);
11283 let mut encoded = vec![0u8; LZ4F_compressFrameBound(input.len(), &prefs)];
11284 let encoded_len = LZ4F_compressFrame(
11285 encoded.as_mut_ptr() as *mut c_void,
11286 encoded.len(),
11287 input.as_ptr() as *const c_void,
11288 input.len(),
11289 &prefs,
11290 );
11291
11292 assert_eq!(LZ4F_isError(encoded_len), 0);
11293 assert_eq!(encoded_len, 199_444);
11294 assert_eq!(xxhash32(&encoded[..encoded_len], 0), 0x6f9f_bc8e);
11295 }
11296 }
11297
11298 #[test]
11299 fn hc_large_frame_with_cli_block_size_matches_upstream_hash() {
11300 unsafe {
11301 let input = patterned_hc_input(8 * 1024 * 1024);
11302 let mut prefs = hc_frame_fixture_prefs(9);
11303 prefs.frame_info.block_size_id = BlockSize::Max4MB;
11304 let mut encoded = vec![0u8; LZ4F_compressFrameBound(input.len(), &prefs)];
11305 let encoded_len = LZ4F_compressFrame(
11306 encoded.as_mut_ptr() as *mut c_void,
11307 encoded.len(),
11308 input.as_ptr() as *const c_void,
11309 input.len(),
11310 &prefs,
11311 );
11312
11313 assert_eq!(LZ4F_isError(encoded_len), 0);
11314 assert_eq!(encoded_len, 35_508);
11315 assert_eq!(xxhash32(&encoded[..encoded_len], 0), 0x5af1_b15f);
11316 }
11317 }
11318
11319 fn hc_frame_fixture_prefs(level: u32) -> LZ4FPreferences {
11320 LZ4FPreferences {
11321 frame_info: LZ4FFrameInfo {
11322 block_size_id: BlockSize::Max64KB,
11323 block_mode: BlockMode::Independent,
11324 content_checksum_flag: ContentChecksum::ChecksumEnabled,
11325 frame_type: FrameType::Frame,
11326 content_size: 0,
11327 dict_id: 0,
11328 block_checksum_flag: BlockChecksum::NoBlockChecksum,
11329 },
11330 compression_level: level,
11331 auto_flush: 0,
11332 favor_dec_speed: 0,
11333 reserved: [0; 3],
11334 }
11335 }
11336
11337 fn patterned_hc_input(len: usize) -> Vec<u8> {
11338 let pattern = b"abcdefghijklmnop0123456789";
11339 let mut input = vec![0u8; len];
11340 for i in 0..len {
11341 input[i] = pattern[i % pattern.len()];
11342 if (i % 97) < 9 {
11343 input[i] = b'A' + (i % 7) as u8;
11344 }
11345 }
11346 input
11347 }
11348
11349 fn decode_hex(hex: &str) -> Vec<u8> {
11350 assert_eq!(hex.len() % 2, 0);
11351 (0..hex.len())
11352 .step_by(2)
11353 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
11354 .collect()
11355 }
11356
11357 fn block_has_offset_below(block: &[u8], threshold: usize) -> bool {
11358 let mut pos = 0usize;
11359 while pos < block.len() {
11360 let token = block[pos];
11361 pos += 1;
11362 let mut lit_len = (token >> 4) as usize;
11363 if lit_len == 15 {
11364 loop {
11365 if pos >= block.len() {
11366 return false;
11367 }
11368 let value = block[pos] as usize;
11369 pos += 1;
11370 lit_len += value;
11371 if value != 255 {
11372 break;
11373 }
11374 }
11375 }
11376 pos += lit_len;
11377 if pos >= block.len() {
11378 return false;
11379 }
11380 if pos + 2 > block.len() {
11381 return false;
11382 }
11383 let offset = u16::from_le_bytes([block[pos], block[pos + 1]]) as usize;
11384 if offset < threshold {
11385 return true;
11386 }
11387 pos += 2;
11388 if token & 0x0f == 15 {
11389 loop {
11390 if pos >= block.len() {
11391 return false;
11392 }
11393 let value = block[pos] as usize;
11394 pos += 1;
11395 if value != 255 {
11396 break;
11397 }
11398 }
11399 }
11400 }
11401 false
11402 }
11403
11404 #[test]
11405 fn hc_ext_state_dest_size_and_stream_wrappers_round_trip() {
11406 let input = b"abcdefabcdefabcdefabcdef-".repeat(4096);
11407 let bound = unsafe { LZ4_compressBound(input.len() as c_int) } as usize;
11408
11409 let state_size = LZ4_sizeofStateHC();
11410 assert!(state_size > 0);
11411 let mut state = vec![0u8; state_size as usize];
11412 let mut compressed = vec![0u8; bound];
11413 let compressed_len = unsafe {
11414 LZ4_compress_HC_extStateHC(
11415 state.as_mut_ptr() as *mut c_void,
11416 input.as_ptr() as *const c_char,
11417 compressed.as_mut_ptr() as *mut c_char,
11418 input.len() as c_int,
11419 compressed.len() as c_int,
11420 9,
11421 )
11422 };
11423 assert!(compressed_len > 0);
11424
11425 let mut output = vec![0u8; input.len()];
11426 let output_len = unsafe {
11427 LZ4_decompress_safe(
11428 compressed.as_ptr() as *const c_char,
11429 output.as_mut_ptr() as *mut c_char,
11430 compressed_len,
11431 output.len() as c_int,
11432 )
11433 };
11434 assert_eq!(output_len as usize, input.len());
11435 assert_eq!(output, input);
11436
11437 let mut source_size = input.len() as c_int;
11438 let mut tiny = vec![0u8; compressed_len as usize / 2];
11439 let partial_len = unsafe {
11440 LZ4_compress_HC_destSize(
11441 state.as_mut_ptr() as *mut c_void,
11442 input.as_ptr() as *const c_char,
11443 tiny.as_mut_ptr() as *mut c_char,
11444 &mut source_size,
11445 tiny.len() as c_int,
11446 9,
11447 )
11448 };
11449 assert!(partial_len > 0);
11450 assert!(source_size > 0);
11451 assert!(source_size < input.len() as c_int);
11452
11453 let stream = unsafe { LZ4_createStreamHC() };
11454 assert!(!stream.is_null());
11455 unsafe { LZ4_resetStreamHC_fast(stream, 9) };
11456 let mut streamed = vec![0u8; bound];
11457 let streamed_len = unsafe {
11458 LZ4_compress_HC_continue(
11459 stream,
11460 input.as_ptr() as *const c_char,
11461 streamed.as_mut_ptr() as *mut c_char,
11462 input.len() as c_int,
11463 streamed.len() as c_int,
11464 )
11465 };
11466 assert!(streamed_len > 0);
11467 unsafe { LZ4_freeStreamHC(stream) };
11468
11469 output.fill(0);
11470 let output_len = unsafe {
11471 LZ4_decompress_safe(
11472 streamed.as_ptr() as *const c_char,
11473 output.as_mut_ptr() as *mut c_char,
11474 streamed_len,
11475 output.len() as c_int,
11476 )
11477 };
11478 assert_eq!(output_len as usize, input.len());
11479 assert_eq!(output, input);
11480 }
11481
11482 #[test]
11483 fn hc_deprecated_state_wrappers_round_trip_and_reset() {
11484 let input = b"deprecated-state-hc-wrapper-".repeat(2048);
11485 let bound = unsafe { LZ4_compressBound(input.len() as c_int) } as usize;
11486 let state_size = LZ4_sizeofStreamStateHC();
11487 assert!(state_size > 0);
11488 let mut state = vec![0u8; state_size as usize];
11489
11490 let reset = unsafe {
11491 LZ4_resetStreamStateHC(
11492 state.as_mut_ptr() as *mut c_void,
11493 input.as_ptr() as *mut c_char,
11494 )
11495 };
11496 assert_eq!(reset, 0);
11497 assert_eq!(
11498 unsafe { LZ4_resetStreamStateHC(ptr::null_mut(), ptr::null_mut()) },
11499 1
11500 );
11501
11502 let mut compressed = vec![0u8; bound];
11503 let compressed_len = unsafe {
11504 LZ4_compressHC2_withStateHC(
11505 state.as_mut_ptr() as *mut c_void,
11506 input.as_ptr() as *const c_char,
11507 compressed.as_mut_ptr() as *mut c_char,
11508 input.len() as c_int,
11509 9,
11510 )
11511 };
11512 assert!(compressed_len > 0);
11513
11514 let mut output = vec![0u8; input.len()];
11515 let output_len = unsafe {
11516 LZ4_decompress_safe(
11517 compressed.as_ptr() as *const c_char,
11518 output.as_mut_ptr() as *mut c_char,
11519 compressed_len,
11520 output.len() as c_int,
11521 )
11522 };
11523 assert_eq!(output_len as usize, input.len());
11524 assert_eq!(output, input);
11525
11526 let mut limited = vec![0u8; compressed_len as usize];
11527 let limited_len = unsafe {
11528 LZ4_compressHC_limitedOutput_withStateHC(
11529 state.as_mut_ptr() as *mut c_void,
11530 input.as_ptr() as *const c_char,
11531 limited.as_mut_ptr() as *mut c_char,
11532 input.len() as c_int,
11533 limited.len() as c_int,
11534 )
11535 };
11536 assert!(limited_len > 0);
11537
11538 output.fill(0);
11539 let output_len = unsafe {
11540 LZ4_decompress_safe(
11541 limited.as_ptr() as *const c_char,
11542 output.as_mut_ptr() as *mut c_char,
11543 limited_len,
11544 output.len() as c_int,
11545 )
11546 };
11547 assert_eq!(output_len as usize, input.len());
11548 assert_eq!(output, input);
11549 }
11550
11551 #[test]
11552 fn hc_stream_compression_references_loaded_dictionary() {
11553 unsafe {
11554 let dict = b"abcdefghijklmnop";
11555 let input = b"abcdefghijklmnop";
11556 let stream = LZ4_createStreamHC();
11557 assert!(!stream.is_null());
11558 assert_eq!(
11559 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
11560 dict.len() as c_int
11561 );
11562
11563 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
11564 let compressed_len = LZ4_compress_HC_continue(
11565 stream,
11566 input.as_ptr() as *const c_char,
11567 compressed.as_mut_ptr() as *mut c_char,
11568 input.len() as c_int,
11569 compressed.len() as c_int,
11570 );
11571 assert!(compressed_len > 0);
11572 assert!((compressed_len as usize) < input.len() + 1);
11573 LZ4_freeStreamHC(stream);
11574
11575 let mut output = vec![0u8; input.len()];
11576 let output_len = LZ4_decompress_safe_usingDict(
11577 compressed.as_ptr() as *const c_char,
11578 output.as_mut_ptr() as *mut c_char,
11579 compressed_len,
11580 output.len() as c_int,
11581 dict.as_ptr() as *const c_char,
11582 dict.len() as c_int,
11583 );
11584 assert_eq!(output_len as usize, input.len());
11585 assert_eq!(output, input);
11586 }
11587 }
11588
11589 #[test]
11590 fn hc_mid_levels_continue_match_upstream_bytes_with_dictionary() {
11591 unsafe {
11592 let dict = b"abcdefghijklmnop0123456789abcdefghijklmnop0123456789";
11593 let input = b"abcdefghijklmnop0123456789ZZabcdefghijklmnop0123456789";
11594 let expected = decode_hex("0f1a00072f5a5a1c0002503536373839");
11595
11596 for level in [1, 2] {
11597 let stream = LZ4_createStreamHC();
11598 assert!(!stream.is_null());
11599 LZ4_setCompressionLevel(stream, level);
11600 assert_eq!(
11601 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
11602 dict.len() as c_int
11603 );
11604
11605 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
11606 let compressed_len = LZ4_compress_HC_continue(
11607 stream,
11608 input.as_ptr() as *const c_char,
11609 compressed.as_mut_ptr() as *mut c_char,
11610 input.len() as c_int,
11611 compressed.len() as c_int,
11612 );
11613 LZ4_freeStreamHC(stream);
11614
11615 assert_eq!(compressed_len as usize, expected.len(), "level {level}");
11616 assert_eq!(
11617 &compressed[..compressed_len as usize],
11618 &expected,
11619 "level {level}"
11620 );
11621 }
11622 }
11623 }
11624
11625 #[test]
11631 fn hc_pattern_analysis_across_dict_boundary_matches_upstream_bytes() {
11632 let mut dict = vec![b'A'; 200];
11633 for i in 0..56 {
11634 dict.push(b'a' + ((i % 26) as u8));
11635 }
11636 assert_eq!(dict.len(), 256);
11637
11638 let mut input = Vec::with_capacity(512);
11639 for i in 0..64 {
11640 input.push(b'b' + ((i % 13) as u8));
11641 }
11642 input.extend(std::iter::repeat_n(b'A', 200));
11643 for i in 0..(512 - 264) {
11644 input.push(b'c' + ((i % 13) as u8));
11645 }
11646 assert_eq!(input.len(), 512);
11647
11648 let cases: &[(c_int, &str)] = &[
11649 (3, "091d000f0d00201f410100b408e0001f6f0d00d3506c6d6e6f63"),
11650 (4, "091d000f0d00201f410100b40924010f0d00d3506c6d6e6f63"),
11651 (5, "091d000f0d00201f410100b40924010f0d00d3506c6d6e6f63"),
11652 (6, "091d000f0d00201f410100b40924010f0d00d3506c6d6e6f63"),
11653 (7, "091d000f0d00201f410100b40924010f0d00d3506c6d6e6f63"),
11654 (8, "091d000f0d00201f410100b40924010f0d00d3506c6d6e6f63"),
11655 (9, "091d000f0d00200f4001b50924010f0d00d3506c6d6e6f63"),
11656 (10, "091d000f0d00200f4001b50924010f0d00d3506c6d6e6f63"),
11657 (11, "091d000f0d00200f4001b50924010f0d00d3506c6d6e6f63"),
11658 (12, "091d000f0d00200f4001b50924010f0d00d3506c6d6e6f63"),
11659 ];
11660
11661 for (level, expected_hex) in cases {
11662 let expected = decode_hex(expected_hex);
11663 unsafe {
11664 let stream = LZ4_createStreamHC();
11665 assert!(!stream.is_null());
11666 LZ4_resetStreamHC_fast(stream, *level);
11667 assert_eq!(
11668 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
11669 dict.len() as c_int
11670 );
11671
11672 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
11673 let compressed_len = LZ4_compress_HC_continue(
11674 stream,
11675 input.as_ptr() as *const c_char,
11676 compressed.as_mut_ptr() as *mut c_char,
11677 input.len() as c_int,
11678 compressed.len() as c_int,
11679 );
11680 LZ4_freeStreamHC(stream);
11681
11682 assert_eq!(compressed_len as usize, expected.len(), "level {level}");
11683 assert_eq!(
11684 &compressed[..compressed_len as usize],
11685 &expected,
11686 "level {level}"
11687 );
11688 }
11689 }
11690 }
11691
11692 #[test]
11697 fn hc_optimal_levels_continue_match_upstream_bytes_with_dictionary() {
11698 let dict = patterned_hc_input(256);
11699 let mut input = patterned_hc_input(1024);
11700 for (i, byte) in input.iter_mut().enumerate() {
11701 *byte ^= ((i >> 7) & 1) as u8;
11702 }
11703 let cases: &[(c_int, &str)] = &[
11704 (10, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000e04010f1e012e011a00144262000e34000fd000330445021f434e001e0fd00014234043c4000f1e013d049c0005a7020fa40342356d6e6fa7030a1a000fd00037144409030f34001550676669686b"),
11705 (11, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000f0401260f1a000c144262000e34000fd000330445021f434e001e0fd00014234043c4000f1e013d049c0005a7020ea4030fee013305a7030a1a000fd00037144409030f34001550676669686b"),
11706 (12, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159f474640434245444746340018061a000fd00010144562010f1e014010339c00234544c4000f0401260f1a000c144262000e34000fd000330445021f434e001e0fd00014234043c4000f1e013d049c0005a7020fa40341001a0005a7030a1a000fd00037144409030f34001550676669686b"),
11707 ];
11708
11709 for (level, expected_hex) in cases {
11710 let expected = decode_hex(expected_hex);
11711 unsafe {
11712 let stream = LZ4_createStreamHC();
11713 assert!(!stream.is_null());
11714 LZ4_resetStreamHC_fast(stream, *level);
11715 assert_eq!(
11716 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
11717 dict.len() as c_int
11718 );
11719
11720 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
11721 let compressed_len = LZ4_compress_HC_continue(
11722 stream,
11723 input.as_ptr() as *const c_char,
11724 compressed.as_mut_ptr() as *mut c_char,
11725 input.len() as c_int,
11726 compressed.len() as c_int,
11727 );
11728 LZ4_freeStreamHC(stream);
11729
11730 assert_eq!(compressed_len as usize, expected.len(), "level {level}");
11731 assert_eq!(
11732 &compressed[..compressed_len as usize],
11733 &expected,
11734 "level {level}"
11735 );
11736 }
11737 }
11738 }
11739
11740 #[test]
11745 fn hc_hashchain_levels_continue_match_upstream_bytes_with_dictionary() {
11746 let dict = patterned_hc_input(256);
11747 let mut input = patterned_hc_input(1024);
11748 for (i, byte) in input.iter_mut().enumerate() {
11749 *byte ^= ((i >> 7) & 1) as u8;
11750 }
11751 let cases: &[(c_int, &str)] = &[
11752 (3, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010fea00070f1a0027009c00234544c4000fb600070f1a002b144262000e34000fd000330445021f434e001e0fd0001403e3012f40431e013d049c0005a7020fb600070f1a002b05a7030a1a000fd000370445021f4734001550676669686b"),
11753 (4, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000e04010f1e012e011a00144262000e34000fd000330445021f434e001e0fd0001403e3012f40431e013d049c0005a7020ed4010fee013305a7030a1a000fd000370445021f4734001550676669686b"),
11754 (5, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000e04010f1e012e011a00144262000e34000fd000330445021f434e001e0fd0001403e3012f40431e013d049c0005a7020ed4010f0c033305a7030a1a000fd000370445021f4734001550676669686b"),
11755 (6, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000e04010f1e012e011a00144262000e34000fd000330445021f434e001e0fd0001403e3012f40431e013d049c0005a7020fa40342356d6e6fa7030a1a000fd000370445021f4734001550676669686b"),
11756 (7, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000e04010f1e012e011a00144262000e34000fd000330445021f434e001e0fd0001403e3012f40431e013d049c0005a7020fa40342356d6e6fa7030a1a000fd000370445021f4734001550676669686b"),
11757 (8, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000e04010f1e012e011a00144262000e34000fd000330445021f434e001e0fd0001403e3012f40431e013d049c0005a7020fa40342356d6e6fa7030a1a000fd000370445021f4734001550676669686b"),
11758 (9, "0f00016dff0b39386063626564676669686b6a6d6c6f6e7131303332353437361a00159e47464043424544474634000f4e00100fd00010144562010f1e014010339c00234544c4000e04010f1e012e011a00144262000e34000fd000330445021f434e001e0fd0001403e3012f40431e013d049c0005a7020fa40342356d6e6fa7030a1a000fd000370445021f4734001550676669686b"),
11759 ];
11760
11761 for (level, expected_hex) in cases {
11762 let expected = decode_hex(expected_hex);
11763 unsafe {
11764 let stream = LZ4_createStreamHC();
11765 assert!(!stream.is_null());
11766 LZ4_resetStreamHC_fast(stream, *level);
11767 assert_eq!(
11768 LZ4_loadDictHC(stream, dict.as_ptr() as *const c_char, dict.len() as c_int),
11769 dict.len() as c_int
11770 );
11771
11772 let mut compressed = vec![0u8; LZ4_compressBound(input.len() as c_int) as usize];
11773 let compressed_len = LZ4_compress_HC_continue(
11774 stream,
11775 input.as_ptr() as *const c_char,
11776 compressed.as_mut_ptr() as *mut c_char,
11777 input.len() as c_int,
11778 compressed.len() as c_int,
11779 );
11780 LZ4_freeStreamHC(stream);
11781
11782 assert_eq!(compressed_len as usize, expected.len(), "level {level}");
11783 assert_eq!(
11784 &compressed[..compressed_len as usize],
11785 &expected,
11786 "level {level}"
11787 );
11788 }
11789 }
11790 }
11791}