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());