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