hexz_core/algo/compression/mod.rs
1//! Compression codecs for snapshot blocks.
2//!
3//! Provides the `Compressor` trait and concrete implementations (LZ4, Zstd).
4//! The format layer uses this trait to encode and decode blocks without
5//! depending on concrete algorithms.
6
7use hexz_common::Result;
8use std::fmt::Debug;
9
10/// Pluggable interface for block-oriented compressors.
11///
12/// **Architectural intent:** Allows snapshot writers and readers to operate
13/// against an abstraction rather than a specific compression library, making
14/// it possible to add or swap algorithms without touching the format layer.
15///
16/// **Constraints:** Implementations must be thread-safe and stateless or
17/// internally synchronized; all methods are expected to be pure functions of
18/// their inputs.
19pub trait Compressor: Send + Sync + Debug {
20 /// Compresses `data` into an owned buffer.
21 ///
22 /// **Architectural intent:** Encodes a single logical block using the
23 /// compressor's native framing.
24 ///
25 /// **Constraints:** The caller is responsible for choosing an appropriate
26 /// block size; extremely large inputs may cause excessive memory usage.
27 fn compress(&self, data: &[u8]) -> Result<Vec<u8>>;
28
29 /// Decompresses an encoded block into a new buffer.
30 ///
31 /// **Architectural intent:** Reverses `compress`, returning the original
32 /// block bytes or failing if corruption or format errors are detected.
33 ///
34 /// **Constraints:** The input must have been produced by a compatible
35 /// encoder; malformed data must surface as a `Error::Compression`.
36 fn decompress(&self, data: &[u8]) -> Result<Vec<u8>>;
37
38 /// Compresses `data` into a caller-provided buffer, enabling buffer reuse.
39 ///
40 /// **Architectural intent:** Eliminates per-call allocation in hot write
41 /// paths by reusing a `Vec` across iterations.
42 ///
43 /// **Default impl:** Falls back to `compress()` and copies the result.
44 fn compress_into(&self, data: &[u8], out: &mut Vec<u8>) -> Result<()> {
45 let compressed = self.compress(data)?;
46 out.clear();
47 out.extend_from_slice(&compressed);
48 Ok(())
49 }
50
51 /// Decompresses an encoded block into a caller-provided buffer.
52 ///
53 /// **Architectural intent:** Enables buffer reuse for hot paths to reduce
54 /// allocation pressure.
55 ///
56 /// **Constraints:** Implementations may fail if `out` is too small for the
57 /// decompressed payload; the return value is the number of bytes written.
58 fn decompress_into(&self, data: &[u8], out: &mut [u8]) -> Result<usize>;
59}
60
61/// LZ4 compression backend.
62///
63/// Provides a fast, lightweight `Compressor` implementation tuned for
64/// low-latency reads.
65pub mod lz4;
66
67/// Zstandard compression backend with optional dictionary support.
68///
69/// Used when higher compression ratios or trained dictionaries are desired.
70pub mod zstd;
71
72pub use lz4::Lz4Compressor;
73pub use zstd::ZstdCompressor;
74
75use crate::format::header::CompressionType;
76use hexz_common::constants::DEFAULT_ZSTD_LEVEL;
77
78/// Create a compressor from a [`CompressionType`] enum.
79pub fn create_compressor(
80 comp_type: CompressionType,
81 level: Option<i32>,
82 dictionary: Option<Vec<u8>>,
83) -> Box<dyn Compressor> {
84 match comp_type {
85 CompressionType::Lz4 => Box::new(Lz4Compressor::new()),
86 CompressionType::Zstd => Box::new(ZstdCompressor::new(
87 level.unwrap_or(DEFAULT_ZSTD_LEVEL),
88 dictionary,
89 )),
90 }
91}
92
93/// Parse a compression string ("lz4"/"zstd") and create a compressor + type.
94///
95/// Returns an error for unrecognized compression names.
96pub fn create_compressor_from_str(
97 s: &str,
98 level: Option<i32>,
99 dictionary: Option<Vec<u8>>,
100) -> hexz_common::Result<(Box<dyn Compressor>, CompressionType)> {
101 match s {
102 "zstd" => Ok((
103 create_compressor(CompressionType::Zstd, level, dictionary),
104 CompressionType::Zstd,
105 )),
106 "lz4" => Ok((
107 create_compressor(CompressionType::Lz4, level, dictionary),
108 CompressionType::Lz4,
109 )),
110 other => Err(hexz_common::Error::Format(format!(
111 "Unknown compression type '{}'. Supported: lz4, zstd",
112 other
113 ))),
114 }
115}