lzzzz/lz4f/
frame.rs

1//! LZ4 Frame Compressor/Decompressor
2
3use super::{api, Result};
4use crate::{common::DEFAULT_BUF_SIZE, lz4f::Preferences, Error, ErrorKind};
5use std::{cell::RefCell, ops::Deref};
6
7/// Calculates the maximum size of the compressed output.
8///
9/// If `original_size` is too large to compress, this returns `0`.
10///
11/// Returned values are reliable only for [`compress`] and [`compress_to_vec`].
12/// Streaming compressors may produce larger compressed frames.
13///
14/// [`compress`]: fn.compress.html
15/// [`compress_to_vec`]: fn.compress_to_vec.html
16#[must_use]
17pub fn max_compressed_size(original_size: usize, prefs: &Preferences) -> usize {
18    api::compress_frame_bound(original_size, prefs)
19}
20
21/// Performs LZ4F compression.
22///
23/// Ensure that the destination slice has enough capacity.
24/// If `dst.len()` is smaller than `lz4f::max_compressed_size(src.len())`,
25/// this function may fail.
26///
27/// Returns the number of bytes written into the destination buffer.
28///
29/// # Example
30///
31/// Compress data with the default compression mode:
32/// ```
33/// use lzzzz::lz4f;
34///
35/// let prefs = lz4f::Preferences::default();
36/// let data = b"The quick brown fox jumps over the lazy dog.";
37/// let mut buf = [0u8; 2048];
38///
39/// // The slice should have enough capacity.
40/// assert!(buf.len() >= lz4f::max_compressed_size(data.len(), &prefs));
41///
42/// let len = lz4f::compress(data, &mut buf, &prefs)?;
43/// let compressed = &buf[..len];
44/// # let mut buf = Vec::new();
45/// # lz4f::decompress_to_vec(compressed, &mut buf)?;
46/// # assert_eq!(buf.as_slice(), &data[..]);
47/// # Ok::<(), std::io::Error>(())
48/// ```
49pub fn compress(src: &[u8], dst: &mut [u8], prefs: &Preferences) -> Result<usize> {
50    compress_to_ptr(src, dst.as_mut_ptr(), dst.len(), prefs)
51}
52
53fn compress_to_ptr(src: &[u8], dst: *mut u8, dst_len: usize, prefs: &Preferences) -> Result<usize> {
54    let mut prefs = *prefs;
55    if prefs.frame_info().content_size() > 0 {
56        prefs.set_content_size(src.len());
57    }
58    api::compress(src, dst, dst_len, &prefs)
59}
60
61/// Appends a compressed frame to `Vec<u8>`.
62///
63/// Returns the number of bytes appended to the given `Vec<u8>`.
64///
65/// # Example
66///
67/// Compress data with the default compression mode:
68/// ```
69/// use lzzzz::lz4f;
70///
71/// let prefs = lz4f::Preferences::default();
72/// let data = b"The quick brown fox jumps over the lazy dog.";
73/// let mut buf = Vec::new();
74///
75/// let len = lz4f::compress_to_vec(data, &mut buf, &prefs)?;
76/// let compressed = &buf;
77/// # let mut buf = Vec::new();
78/// # lz4f::decompress_to_vec(compressed, &mut buf)?;
79/// # assert_eq!(buf.as_slice(), &data[..]);
80/// # Ok::<(), std::io::Error>(())
81/// ```
82pub fn compress_to_vec(src: &[u8], dst: &mut Vec<u8>, prefs: &Preferences) -> Result<usize> {
83    let orig_len = dst.len();
84    dst.reserve(max_compressed_size(src.len(), prefs));
85    #[allow(unsafe_code)]
86    unsafe {
87        let result = compress_to_ptr(
88            src,
89            dst.as_mut_ptr().add(orig_len),
90            dst.capacity() - orig_len,
91            prefs,
92        );
93        dst.set_len(orig_len + result.as_ref().unwrap_or(&0));
94        result
95    }
96}
97
98/// Decompresses an LZ4 frame.
99///
100/// Returns the number of bytes appended to the given `Vec<u8>`.
101///
102/// # Example
103///
104/// ```
105/// use lzzzz::lz4f;
106///
107/// const COMPRESSED_DATA: &str =
108///     "BCJNGGBAgiwAAIBUaGUgcXVpY2sgYnJvd24gZm94IGp1bXBzIG92ZXIgdGhlIGxhenkgZG9nLgAAAAA=";
109///
110/// let data = base64::decode(COMPRESSED_DATA).unwrap();
111/// let mut buf = Vec::new();
112///
113/// lz4f::decompress_to_vec(&data[..], &mut buf)?;
114///
115/// assert_eq!(
116///     &buf[..],
117///     &b"The quick brown fox jumps over the lazy dog."[..]
118/// );
119/// # Ok::<(), std::io::Error>(())
120/// ```
121pub fn decompress_to_vec(src: &[u8], dst: &mut Vec<u8>) -> Result<usize> {
122    let header_len = dst.len();
123    let mut src_offset = 0;
124    let mut dst_offset = header_len;
125    DecompressionCtx::with(|ctx| {
126        let mut ctx = ctx.borrow_mut();
127        ctx.reset();
128        loop {
129            dst.resize_with(dst.len() + DEFAULT_BUF_SIZE, Default::default);
130            match ctx.decompress_dict(&src[src_offset..], &mut dst[dst_offset..], &[], false) {
131                Ok((src_len, dst_len, expected)) => {
132                    src_offset += src_len;
133                    dst_offset += dst_len;
134                    if expected == 0 {
135                        dst.resize_with(dst_offset, Default::default);
136                        return Ok(dst_offset - header_len);
137                    } else if src_offset >= src.len() {
138                        dst.resize_with(header_len, Default::default);
139                        return Err(Error::new(ErrorKind::CompressedDataIncomplete).into());
140                    }
141                }
142                Err(err) => {
143                    dst.resize_with(header_len, Default::default);
144                    return Err(err);
145                }
146            }
147        }
148    })
149}
150
151struct DecompressionCtx(RefCell<api::DecompressionContext>);
152
153impl DecompressionCtx {
154    fn new() -> Self {
155        Self(RefCell::new(api::DecompressionContext::new().unwrap()))
156    }
157
158    fn with<F, R>(f: F) -> R
159    where
160        F: FnOnce(&RefCell<api::DecompressionContext>) -> R,
161    {
162        DECOMPRESSION_CTX.with(|state| (f)(state))
163    }
164}
165
166impl Deref for DecompressionCtx {
167    type Target = RefCell<api::DecompressionContext>;
168
169    fn deref(&self) -> &Self::Target {
170        &self.0
171    }
172}
173
174thread_local!(static DECOMPRESSION_CTX: DecompressionCtx = DecompressionCtx::new());