Skip to main content

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    /// Decompresses an encoded block into a caller-provided buffer.
39    ///
40    /// **Architectural intent:** Enables buffer reuse for hot paths to reduce
41    /// allocation pressure.
42    ///
43    /// **Constraints:** Implementations may fail if `out` is too small for the
44    /// decompressed payload; the return value is the number of bytes written.
45    fn decompress_into(&self, data: &[u8], out: &mut [u8]) -> Result<usize>;
46}
47
48/// LZ4 compression backend.
49///
50/// Provides a fast, lightweight `Compressor` implementation tuned for
51/// low-latency reads.
52pub mod lz4;
53
54/// Zstandard compression backend with optional dictionary support.
55///
56/// Used when higher compression ratios or trained dictionaries are desired.
57pub mod zstd;
58
59pub use lz4::Lz4Compressor;
60pub use zstd::ZstdCompressor;
61
62use crate::format::header::CompressionType;
63use hexz_common::constants::DEFAULT_ZSTD_LEVEL;
64
65/// Create a compressor from a [`CompressionType`] enum.
66pub fn create_compressor(
67    comp_type: CompressionType,
68    level: Option<i32>,
69    dictionary: Option<Vec<u8>>,
70) -> Box<dyn Compressor> {
71    match comp_type {
72        CompressionType::Lz4 => Box::new(Lz4Compressor::new()),
73        CompressionType::Zstd => Box::new(ZstdCompressor::new(
74            level.unwrap_or(DEFAULT_ZSTD_LEVEL),
75            dictionary,
76        )),
77    }
78}
79
80/// Parse a compression string ("lz4"/"zstd") and create a compressor + type.
81///
82/// Returns an error for unrecognized compression names.
83pub fn create_compressor_from_str(
84    s: &str,
85    level: Option<i32>,
86    dictionary: Option<Vec<u8>>,
87) -> hexz_common::Result<(Box<dyn Compressor>, CompressionType)> {
88    match s {
89        "zstd" => Ok((
90            create_compressor(CompressionType::Zstd, level, dictionary),
91            CompressionType::Zstd,
92        )),
93        "lz4" => Ok((
94            create_compressor(CompressionType::Lz4, level, dictionary),
95            CompressionType::Lz4,
96        )),
97        other => Err(hexz_common::Error::Format(format!(
98            "Unknown compression type '{}'. Supported: lz4, zstd",
99            other
100        ))),
101    }
102}