Skip to main content

lz4rip_encode/
compressor.rs

1use core::fmt;
2
3use alloc::vec::Vec;
4
5use crate::compress::CompressorRef;
6use lz4rip_core::CompressError;
7
8/// A reusable block compressor that owns its dictionary.
9///
10/// This is the ergonomic API for use with `alloc`. For a no-alloc variant that
11/// borrows the dictionary, see [`CompressorRef`].
12///
13/// For one-shot compression, use [`compress`](crate::compress) or
14/// [`compress_into`](crate::compress_into) instead.
15///
16/// # Example
17/// ```
18/// use lz4rip_encode::{Compressor, get_maximum_output_size};
19///
20/// let mut comp = Compressor::new();
21/// let input = b"hello world, hello world, hello!";
22/// let mut output = vec![0u8; get_maximum_output_size(input.len())];
23/// let compressed_len = comp.compress_into(input, &mut output).unwrap();
24/// ```
25pub struct Compressor {
26    // SAFETY: `inner` is declared before `dict` so it is dropped first.
27    // The `CompressorRef` may hold a `&[u8]` pointing into `dict`'s heap buffer.
28    // This is sound because:
29    // - `dict` is private and never reallocated after construction
30    // - `inner` is dropped before `dict` (field declaration order)
31    // - `CompressorRef` has no Drop impl that accesses the slice
32    inner: CompressorRef<'static>,
33    #[allow(dead_code)] // Backing store for the slice in `inner`.
34    dict: Vec<u8>,
35}
36
37impl fmt::Debug for Compressor {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("Compressor")
40            .field("dict_len", &self.dict.len())
41            .finish()
42    }
43}
44
45impl Compressor {
46    /// Create a new compressor without a dictionary.
47    pub fn new() -> Self {
48        Compressor {
49            inner: CompressorRef::new(),
50            dict: Vec::new(),
51        }
52    }
53
54    /// Create a new compressor seeded with an external dictionary.
55    ///
56    /// The dictionary is cloned into owned storage.
57    /// If `dict` is shorter than 4 bytes, it is ignored.
58    pub fn with_dict(dict: &[u8]) -> Self {
59        let dict = dict.to_vec();
60        // SAFETY: We create a &'static [u8] pointing into `dict`'s heap buffer.
61        // Sound because `dict` is stored in self, never reallocated, and `inner`
62        // (which holds the reference) is dropped before `dict` per field order.
63        let dict_ref: &'static [u8] =
64            unsafe { core::slice::from_raw_parts(dict.as_ptr(), dict.len()) };
65        Compressor {
66            inner: CompressorRef::with_dict(dict_ref),
67            dict,
68        }
69    }
70
71    /// Compress `input` into `output`, returning the number of compressed bytes.
72    ///
73    /// `output` must be at least [`get_maximum_output_size`]`(input.len())` bytes.
74    pub fn compress_into(
75        &mut self,
76        input: &[u8],
77        output: &mut [u8],
78    ) -> Result<usize, CompressError> {
79        self.inner.compress_into(input, output)
80    }
81
82    /// Compress `input` into a new `Vec<u8>`.
83    pub fn compress(&mut self, input: &[u8]) -> Vec<u8> {
84        self.inner.compress(input)
85    }
86}
87
88impl Default for Compressor {
89    fn default() -> Self {
90        Self::new()
91    }
92}