1pub(crate) mod block_header;
4pub(crate) mod blocks;
5mod file_profile;
6pub(crate) mod frame_header;
7pub(crate) mod match_generator;
8pub(crate) mod util;
9
10mod compression_level;
11mod compression_parameters;
12mod frame_compressor;
13mod levels;
14#[cfg(all(feature = "std", feature = "multithreading"))]
15mod parallel_encoder;
16#[cfg(feature = "std")]
17mod streaming_encoder;
18mod workspace;
19pub use compression_level::{CompressionLevel, InvalidCompressionLevel};
20pub use compression_parameters::{
21 CompressionStrategy, CompressionTuning, ContentSizePolicy, LongDistanceMatching,
22};
23pub(crate) use file_profile::CompressionFileProfile;
24pub use file_profile::CompressionFileType;
25#[cfg(feature = "std")]
26pub(crate) use file_profile::{compression_file_profile_for_path_and_data, read_file_type_sample};
27#[cfg(feature = "std")]
28pub use file_profile::{compression_file_type_for_path, compression_file_type_for_path_and_data};
29pub use frame_compressor::FrameCompressor;
30pub use match_generator::MatchGeneratorDriver;
31#[cfg(all(feature = "std", feature = "multithreading"))]
32pub use parallel_encoder::{encode_all_parallel, encode_parallel, ParallelEncoder};
33#[cfg(feature = "std")]
34pub use streaming_encoder::{
35 encode, encode_all, DictionaryError, EncodeError, Encoder, EncoderDictionary, EncoderOptions,
36 DEFAULT_FRAME_CHUNK_SIZE, DEFAULT_MEMORY_LIMIT,
37};
38pub use workspace::{EncoderWorkspace, EncoderWorkspaceError, StaticEncoderWorkspace};
39
40use crate::io::{Read, Write};
41use alloc::vec::Vec;
42#[cfg(feature = "std")]
43use std::path::Path;
44
45pub fn compress<R: Read, W: Write>(source: R, target: W, level: CompressionLevel) {
53 let mut frame_enc = FrameCompressor::new(level);
54 frame_enc.set_source(source);
55 frame_enc.set_drain(target);
56 frame_enc.compress();
57}
58
59pub fn compress_with_file_type<R: Read, W: Write>(
64 source: R,
65 target: W,
66 file_type: CompressionFileType,
67 level: CompressionLevel,
68) {
69 let mut frame_enc =
70 FrameCompressor::new_with_hints(level, file_type, CompressionFileProfile::None);
71 frame_enc.set_source(source);
72 frame_enc.set_drain(target);
73 frame_enc.compress();
74}
75
76#[cfg(feature = "std")]
78pub fn compress_with_path<R: Read, W: Write>(
79 mut source: R,
80 target: W,
81 path: &Path,
82 level: CompressionLevel,
83) {
84 let sample = read_file_type_sample(&mut source);
85 let file_type = compression_file_type_for_path_and_data(path, &sample);
86 let file_profile = compression_file_profile_for_path_and_data(path, &sample);
87 let mut frame_enc = FrameCompressor::new_with_hints(level, file_type, file_profile);
88 frame_enc.set_source(sample.as_slice().chain(source));
89 frame_enc.set_drain(target);
90 frame_enc.compress();
91}
92
93pub fn compress_to_vec<R: Read>(source: R, level: CompressionLevel) -> Vec<u8> {
100 let mut vec = Vec::new();
101 compress(source, &mut vec, level);
102 vec
103}
104
105#[cfg(any(test, feature = "c-port-validation"))]
111pub fn compress_c_level<R: Read, W: Write>(mut source: R, mut target: W, level: i32) {
112 let mut input = Vec::new();
113 source.read_to_end(&mut input).unwrap();
114 let compressed = levels::c_port::encode_frame_no_dict(&input, level);
115 target.write_all(&compressed).unwrap();
116}
117
118#[cfg(any(test, feature = "c-port-validation"))]
120pub fn compress_to_vec_c_level<R: Read>(mut source: R, level: i32) -> Vec<u8> {
121 let mut input = Vec::new();
122 source.read_to_end(&mut input).unwrap();
123 compress_slice_c_level(&input, level)
124}
125
126#[cfg(any(test, feature = "c-port-validation"))]
132pub fn compress_slice_c_level(source: &[u8], level: i32) -> Vec<u8> {
133 levels::c_port::encode_frame_no_dict(source, level)
134}
135
136#[cfg(any(test, feature = "c-port-validation"))]
141pub fn compress_slice_c_level_with_target_c_block_size(
142 source: &[u8],
143 level: i32,
144 target_c_block_size: usize,
145) -> Option<Vec<u8>> {
146 levels::c_port::encode_frame_no_dict_with_target_c_block_size(
147 source,
148 level,
149 target_c_block_size,
150 )
151}
152
153#[derive(Copy, Clone, Debug, Eq, PartialEq)]
155#[cfg(any(test, feature = "c-port-validation"))]
156pub enum CLevelDictionaryError {
157 WrongDictionary,
158 CorruptedDictionary,
159}
160
161#[derive(Clone)]
167#[cfg(any(test, feature = "c-port-validation"))]
168pub struct CLevelEncoderDictionary {
169 level: i32,
170 dictionary: Option<levels::c_port::PreparedDictionary>,
171}
172
173#[cfg(any(test, feature = "c-port-validation"))]
174impl CLevelEncoderDictionary {
175 pub fn copy(dictionary: &[u8], level: i32) -> Result<Self, CLevelDictionaryError> {
177 let dictionary = levels::c_port::PreparedDictionary::from_bytes(dictionary)
178 .map_err(map_c_level_dictionary_error)?;
179 Ok(Self { level, dictionary })
180 }
181
182 pub fn level(&self) -> i32 {
184 self.level
185 }
186}
187
188#[cfg(any(test, feature = "c-port-validation"))]
190pub fn compress_slice_c_level_with_prepared_dictionary(
191 source: &[u8],
192 dictionary: &CLevelEncoderDictionary,
193) -> Vec<u8> {
194 match dictionary.dictionary.as_ref() {
195 Some(prepared) => levels::c_port::encode_frame_with_prepared_dictionary(
196 source,
197 dictionary.level,
198 prepared,
199 ),
200 None => levels::c_port::encode_frame_no_dict(source, dictionary.level),
201 }
202}
203
204#[cfg(any(test, feature = "c-port-validation"))]
206pub fn compress_to_vec_c_level_with_prepared_dictionary<R: Read>(
207 mut source: R,
208 dictionary: &CLevelEncoderDictionary,
209) -> Vec<u8> {
210 let mut input = Vec::new();
211 source.read_to_end(&mut input).unwrap();
212 compress_slice_c_level_with_prepared_dictionary(&input, dictionary)
213}
214
215#[cfg(any(test, feature = "c-port-validation"))]
218pub fn compress_c_level_with_dictionary<R: Read, W: Write>(
219 mut source: R,
220 mut target: W,
221 level: i32,
222 dictionary: &[u8],
223) -> Result<(), CLevelDictionaryError> {
224 let mut input = Vec::new();
225 source.read_to_end(&mut input).unwrap();
226 let compressed = levels::c_port::encode_frame_with_dictionary(&input, level, dictionary)
227 .map_err(map_c_level_dictionary_error)?;
228 target.write_all(&compressed).unwrap();
229 Ok(())
230}
231
232#[cfg(any(test, feature = "c-port-validation"))]
235pub fn compress_to_vec_c_level_with_dictionary<R: Read>(
236 mut source: R,
237 level: i32,
238 dictionary: &[u8],
239) -> Result<Vec<u8>, CLevelDictionaryError> {
240 let mut input = Vec::new();
241 source.read_to_end(&mut input).unwrap();
242 compress_slice_c_level_with_dictionary(&input, level, dictionary)
243}
244
245#[cfg(any(test, feature = "c-port-validation"))]
248pub fn compress_slice_c_level_with_dictionary(
249 source: &[u8],
250 level: i32,
251 dictionary: &[u8],
252) -> Result<Vec<u8>, CLevelDictionaryError> {
253 levels::c_port::encode_frame_with_dictionary(source, level, dictionary)
254 .map_err(map_c_level_dictionary_error)
255}
256
257#[cfg(any(test, feature = "c-port-validation"))]
263pub fn compress_slice_c_level_with_dictionary_and_target_c_block_size(
264 source: &[u8],
265 level: i32,
266 dictionary: &[u8],
267 target_c_block_size: usize,
268) -> Result<Option<Vec<u8>>, CLevelDictionaryError> {
269 levels::c_port::encode_frame_with_dictionary_and_target_c_block_size(
270 source,
271 level,
272 dictionary,
273 target_c_block_size,
274 )
275 .map_err(map_c_level_dictionary_error)
276}
277
278#[cfg(any(test, feature = "c-port-validation"))]
279fn map_c_level_dictionary_error(
280 error: levels::c_port::DictionaryParseError,
281) -> CLevelDictionaryError {
282 match error {
283 levels::c_port::DictionaryParseError::WrongDictionary => {
284 CLevelDictionaryError::WrongDictionary
285 }
286 levels::c_port::DictionaryParseError::CorruptedDictionary => {
287 CLevelDictionaryError::CorruptedDictionary
288 }
289 }
290}
291
292pub fn compress_to_vec_with_file_type<R: Read>(
294 source: R,
295 file_type: CompressionFileType,
296 level: CompressionLevel,
297) -> Vec<u8> {
298 let mut vec = Vec::new();
299 compress_with_file_type(source, &mut vec, file_type, level);
300 vec
301}
302
303#[cfg(feature = "std")]
305pub fn compress_to_vec_with_path<R: Read>(
306 source: R,
307 path: &Path,
308 level: CompressionLevel,
309) -> Vec<u8> {
310 let mut vec = Vec::new();
311 compress_with_path(source, &mut vec, path, level);
312 vec
313}
314
315pub trait Matcher {
331 fn get_next_space(&mut self) -> alloc::vec::Vec<u8>;
333 fn get_last_space(&self) -> &[u8];
335 fn commit_space(&mut self, space: alloc::vec::Vec<u8>);
337 fn skip_matching(&mut self);
339 fn start_matching(&mut self, handle_sequence: impl for<'a> FnMut(Sequence<'a>));
341 fn reset(&mut self, level: CompressionLevel);
343 fn set_file_type_hint(&mut self, _file_type: CompressionFileType) {}
347 fn set_internal_file_profile_hint(&mut self, _file_profile_code: u8) {}
352 fn set_repeat_offsets(&mut self, _newest: u32, _second: u32, _third: u32) {}
356 fn skip_matching_for_incompressible(&mut self) {
361 self.skip_matching();
362 }
363 fn skip_matching_for_rle(&mut self) {
369 self.skip_matching();
370 }
371 fn window_size(&self) -> u64;
375}
376
377#[derive(PartialEq, Eq, Debug)]
378pub enum Sequence<'data> {
380 Triple {
385 literals: &'data [u8],
386 offset: usize,
387 match_len: usize,
388 },
389 Literals { literals: &'data [u8] },
393}
394
395#[cfg(test)]
396mod tests {
397 use super::{
398 compress_c_level, compress_c_level_with_dictionary, compress_slice_c_level,
399 compress_slice_c_level_with_dictionary,
400 compress_slice_c_level_with_dictionary_and_target_c_block_size, compress_to_vec_c_level,
401 compress_to_vec_c_level_with_dictionary, CLevelDictionaryError, CompressionLevel,
402 };
403 use crate::decoding::{dictionary::Dictionary, FrameDecoder};
404 use alloc::vec::Vec;
405
406 #[test]
407 fn compression_level_equality_is_available_for_api_comparisons() {
408 assert_eq!(CompressionLevel::Fastest, CompressionLevel::Fastest);
409 }
410
411 #[test]
412 fn c_level_compression_round_trips_representative_strategies() {
413 let mut data = Vec::new();
414 while data.len() < (crate::common::MAX_BLOCK_SIZE as usize * 2) + 2048 {
415 data.extend_from_slice(b"public-c-level route=/archive status=200 bytes=1874\n");
416 }
417
418 for level in [1, 3, 5, 8, 13, 16, 18, 19, 22] {
419 let encoded = compress_to_vec_c_level(data.as_slice(), level);
420 assert_round_trips(&encoded, &data);
421 let encoded_from_slice = compress_slice_c_level(&data, level);
422 assert_eq!(encoded_from_slice, encoded);
423 }
424 }
425
426 #[test]
427 fn c_level_compression_writes_to_target() {
428 let data = b"public-c-level-writer public-c-level-writer";
429 let mut encoded = Vec::new();
430
431 compress_c_level(data.as_slice(), &mut encoded, 16);
432
433 assert_round_trips(&encoded, data);
434 }
435
436 #[test]
437 fn c_level_dictionary_compression_round_trips_representative_strategies() {
438 let dict = full_dictionary_fixture();
439 let data = dictionary_payload();
440
441 for level in [1, 3, 5, 8, 13, 16, 18, 19] {
442 let encoded =
443 compress_to_vec_c_level_with_dictionary(data.as_slice(), level, &dict).unwrap();
444 assert_round_trips_with_dictionary(&encoded, &data, &dict);
445 let encoded_from_slice =
446 compress_slice_c_level_with_dictionary(&data, level, &dict).unwrap();
447 assert_eq!(encoded_from_slice, encoded);
448 }
449 }
450
451 #[test]
452 fn c_level_dictionary_compression_writes_to_target() {
453 let dict = full_dictionary_fixture();
454 let data = dictionary_payload();
455 let mut encoded = Vec::new();
456
457 compress_c_level_with_dictionary(data.as_slice(), &mut encoded, 13, &dict).unwrap();
458
459 assert_round_trips_with_dictionary(&encoded, &data, &dict);
460 }
461
462 #[test]
463 fn c_level_dictionary_target_c_block_size_round_trips_optimal_strategy() {
464 let dict = full_dictionary_fixture();
465 let data = dictionary_payload();
466
467 let encoded =
468 compress_slice_c_level_with_dictionary_and_target_c_block_size(&data, 16, &dict, 2048)
469 .unwrap()
470 .expect("level 16 dictionary target mode is target-wired");
471
472 assert_round_trips_with_dictionary(&encoded, &data, &dict);
473 }
474
475 #[test]
476 fn c_level_dictionary_target_c_block_size_round_trips_hash_chain_strategy() {
477 let dict = full_dictionary_fixture();
478 let data = dictionary_payload();
479
480 let encoded =
481 compress_slice_c_level_with_dictionary_and_target_c_block_size(&data, 5, &dict, 2048)
482 .unwrap()
483 .expect("level 5 dictionary target mode is target-wired");
484
485 assert_round_trips_with_dictionary(&encoded, &data, &dict);
486 }
487
488 #[test]
489 fn c_level_dictionary_target_c_block_size_round_trips_fast_strategies() {
490 let dict = full_dictionary_fixture();
491 let data = dictionary_payload();
492
493 for level in [1, 3] {
494 let encoded = compress_slice_c_level_with_dictionary_and_target_c_block_size(
495 &data, level, &dict, 2048,
496 )
497 .unwrap();
498
499 assert_round_trips_with_dictionary(
500 &encoded.expect("fast dictionary target mode is target-wired"),
501 &data,
502 &dict,
503 );
504 }
505 }
506
507 #[test]
508 fn c_level_dictionary_target_c_block_size_rejects_invalid_sizes() {
509 let dict = full_dictionary_fixture();
510 let data = dictionary_payload();
511
512 let encoded = compress_slice_c_level_with_dictionary_and_target_c_block_size(
513 &data,
514 1,
515 &dict,
516 128 * 1024 + 1,
517 )
518 .unwrap();
519
520 assert_eq!(encoded, None);
521 }
522
523 #[test]
524 fn c_level_dictionary_compression_reports_corrupt_full_dictionary() {
525 let mut dict = full_dictionary_fixture();
526 let repeat_offset_start = dict.len() - dictionary_content().len() - 12;
527 dict[repeat_offset_start..repeat_offset_start + 4]
528 .copy_from_slice(&0x00AB_CDEF_u32.to_le_bytes());
529
530 let error = compress_to_vec_c_level_with_dictionary(b"data".as_slice(), 3, &dict)
531 .expect_err("invalid full dictionary offsets must be rejected");
532
533 assert_eq!(error, CLevelDictionaryError::CorruptedDictionary);
534 }
535
536 fn assert_round_trips(encoded: &[u8], expected: &[u8]) {
537 let mut decoded = Vec::with_capacity(expected.len());
538 FrameDecoder::new()
539 .decode_all_to_vec(encoded, &mut decoded)
540 .unwrap();
541
542 assert_eq!(decoded, expected);
543 }
544
545 fn assert_round_trips_with_dictionary(encoded: &[u8], expected: &[u8], dict: &[u8]) {
546 let mut decoded = Vec::with_capacity(expected.len());
547 let mut decoder = FrameDecoder::new();
548 decoder
549 .add_dict(Dictionary::decode_dict(dict).unwrap())
550 .unwrap();
551 decoder.decode_all_to_vec(encoded, &mut decoded).unwrap();
552
553 assert_eq!(decoded, expected);
554 }
555
556 fn dictionary_payload() -> Vec<u8> {
557 let mut data = Vec::new();
558 for _ in 0..10 {
559 data.extend_from_slice(dictionary_content());
560 }
561 data
562 }
563
564 const DICT_ID: u32 = 0x4723_2101;
565
566 fn full_dictionary_fixture() -> Vec<u8> {
567 let mut raw = Vec::new();
568 raw.extend_from_slice(&crate::decoding::dictionary::MAGIC_NUM);
569 raw.extend_from_slice(&DICT_ID.to_le_bytes());
570 raw.extend_from_slice(dictionary_tables());
571 for offset in [3_u32, 10, 25] {
572 raw.extend_from_slice(&offset.to_le_bytes());
573 }
574 raw.extend_from_slice(dictionary_content());
575 raw
576 }
577
578 fn dictionary_tables() -> &'static [u8] {
579 &[
580 54, 16, 192, 155, 4, 0, 207, 59, 239, 121, 158, 116, 220, 93, 114, 229, 110, 41, 249,
581 95, 165, 255, 83, 202, 254, 68, 74, 159, 63, 161, 100, 151, 137, 21, 184, 183, 189,
582 100, 235, 209, 251, 174, 91, 75, 91, 185, 19, 39, 75, 146, 98, 177, 249, 14, 4, 35, 0,
583 0, 0, 40, 40, 20, 10, 12, 204, 37, 196, 1, 173, 122, 0, 4, 0, 128, 1, 2, 2, 25, 32, 27,
584 27, 22, 24, 26, 18, 12, 12, 15, 16, 11, 69, 37, 225, 48, 20, 12, 6, 2, 161, 80, 40, 20,
585 44, 137, 145, 204, 46, 0, 0, 0, 0, 0, 116, 253, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
586 0, 0, 0,
587 ]
588 }
589
590 fn dictionary_content() -> &'static [u8] {
591 b"method=GET path=/v1/projects/beta status=200 bytes=1847\n\
592 method=POST path=/v1/projects/beta status=202 bytes=932\n"
593 }
594}