applesauce_core/compressor/
mod.rs1#[cfg(feature = "lzvn")]
2use self::lzvn::Lzvn;
3#[cfg(any(feature = "lzfse", feature = "system-lzfse"))]
5use self::lzfse::Lzfse;
6#[cfg(feature = "zlib")]
7use self::zlib::Zlib;
8use crate::decmpfs::BlockInfo;
9use std::{fmt, io};
10
11#[cfg(any(feature = "lzfse", feature = "lzvn"))]
12mod lz;
13#[cfg(feature = "lzfse")]
14mod lzfse;
15#[cfg(feature = "lzvn")]
16mod lzvn;
17#[cfg(feature = "zlib")]
18mod zlib;
19
20pub(crate) trait CompressorImpl {
21 #[must_use]
23 fn header_size(block_count: u64) -> u64;
24
25 #[must_use]
26 fn trailer_size() -> u64 {
27 0
28 }
29
30 fn compress(&mut self, dst: &mut [u8], src: &[u8], level: u32) -> io::Result<usize>;
31 fn decompress(&mut self, dst: &mut [u8], src: &[u8]) -> io::Result<usize>;
32
33 fn read_block_info<R: io::Read + io::Seek>(
34 reader: R,
35 orig_file_size: u64,
36 ) -> io::Result<Vec<BlockInfo>>;
37
38 fn finish<W: io::Write + io::Seek>(writer: W, block_sizes: &[u32]) -> io::Result<()>;
39}
40
41pub struct Compressor(Data);
42
43impl Compressor {
44 #[cfg(feature = "zlib")]
45 #[must_use]
46 pub fn zlib() -> Self {
47 Self(Data::Zlib(Zlib::new()))
48 }
49
50 #[cfg(feature = "lzfse")]
51 #[must_use]
52 pub fn lzfse() -> Self {
53 Self(Data::Lzfse(Lzfse::new()))
54 }
55
56 #[cfg(feature = "lzvn")]
57 #[must_use]
58 pub fn lzvn() -> Self {
59 Self(Data::Lzvn(Lzvn::new()))
60 }
61
62 #[must_use]
63 pub fn kind(&self) -> Kind {
64 match self.0 {
65 #[cfg(feature = "zlib")]
66 Data::Zlib(_) => Kind::Zlib,
67 #[cfg(feature = "lzfse")]
68 Data::Lzfse(_) => Kind::Lzfse,
69 #[cfg(feature = "lzvn")]
70 Data::Lzvn(_) => Kind::Lzvn,
71 }
72 }
73}
74
75enum Data {
76 #[cfg(feature = "zlib")]
77 Zlib(Zlib),
78 #[cfg(feature = "lzfse")]
79 Lzfse(Lzfse),
80 #[cfg(feature = "lzvn")]
81 Lzvn(Lzvn),
82}
83
84impl Compressor {
85 #[must_use]
86 pub fn blocks_start(&self, block_count: u64) -> u64 {
87 self.kind().header_size(block_count)
88 }
89
90 pub fn compress(&mut self, dst: &mut [u8], src: &[u8], level: u32) -> io::Result<usize> {
91 match self.0 {
92 #[cfg(feature = "zlib")]
93 Data::Zlib(ref mut i) => i.compress(dst, src, level),
94 #[cfg(feature = "lzfse")]
95 Data::Lzfse(ref mut i) => i.compress(dst, src, level),
96 #[cfg(feature = "lzvn")]
97 Data::Lzvn(ref mut i) => i.compress(dst, src, level),
98 }
99 }
100
101 pub fn decompress(&mut self, dst: &mut [u8], src: &[u8]) -> io::Result<usize> {
102 match self.0 {
103 #[cfg(feature = "zlib")]
104 Data::Zlib(ref mut i) => i.decompress(dst, src),
105 #[cfg(feature = "lzfse")]
106 Data::Lzfse(ref mut i) => i.decompress(dst, src),
107 #[cfg(feature = "lzvn")]
108 Data::Lzvn(ref mut i) => i.decompress(dst, src),
109 }
110 }
111
112 pub fn finish<W: io::Write + io::Seek>(
113 &mut self,
114 writer: W,
115 block_sizes: &[u32],
116 ) -> io::Result<()> {
117 self.kind().finish(writer, block_sizes)
118 }
119}
120
121#[derive(Debug, Copy, Clone, PartialEq, Eq)]
122#[repr(u8)]
123pub enum Kind {
124 Zlib = 0,
125 Lzvn,
126 Lzfse,
127}
128
129impl fmt::Display for Kind {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 f.write_str(self.name())
132 }
133}
134
135impl Kind {
136 #[must_use]
137 pub const fn name(self) -> &'static str {
138 match self {
139 Kind::Zlib => "ZLIB",
140 Kind::Lzvn => "LZVN",
141 Kind::Lzfse => "LZFSE",
142 }
143 }
144
145 #[must_use]
146 #[inline]
147 pub const fn supported(self) -> bool {
148 #[allow(clippy::match_same_arms)]
151 match self {
152 Kind::Zlib => cfg!(feature = "zlib"),
153 Kind::Lzvn => cfg!(feature = "lzvn"),
154 Kind::Lzfse => cfg!(feature = "lzfse"),
155 }
156 }
157
158 #[must_use]
159 pub fn compressor(self) -> Option<Compressor> {
160 let data = match self {
161 #[cfg(feature = "zlib")]
162 Kind::Zlib => Data::Zlib(Zlib::new()),
163 #[cfg(feature = "lzfse")]
164 Kind::Lzfse => Data::Lzfse(Lzfse::new()),
165 #[cfg(feature = "lzvn")]
166 Kind::Lzvn => Data::Lzvn(Lzvn::new()),
167 #[allow(unreachable_patterns)]
168 _ => return None,
169 };
170 Some(Compressor(data))
171 }
172
173 #[must_use]
174 pub fn header_size(self, block_count: u64) -> u64 {
175 match self {
176 #[cfg(feature = "zlib")]
177 Kind::Zlib => Zlib::header_size(block_count),
178 #[cfg(feature = "lzvn")]
179 Kind::Lzvn => Lzvn::header_size(block_count),
180 #[cfg(feature = "lzfse")]
181 Kind::Lzfse => Lzfse::header_size(block_count),
182 #[allow(unreachable_patterns)]
183 _ => panic!("Unsupported compression kind {self}"),
184 }
185 }
186
187 pub fn read_block_info<R: io::Read + io::Seek>(
188 self,
189 reader: R,
190 orig_file_size: u64,
191 ) -> io::Result<Vec<BlockInfo>> {
192 match self {
193 #[cfg(feature = "zlib")]
194 Kind::Zlib => Zlib::read_block_info(reader, orig_file_size),
195 #[cfg(feature = "lzvn")]
196 Kind::Lzvn => Lzvn::read_block_info(reader, orig_file_size),
197 #[cfg(feature = "lzfse")]
198 Kind::Lzfse => Lzfse::read_block_info(reader, orig_file_size),
199 #[allow(unreachable_patterns)]
200 _ => panic!("Unsupported compression kind {self}"),
201 }
202 }
203
204 pub fn finish<W: io::Write + io::Seek>(self, writer: W, block_sizes: &[u32]) -> io::Result<()> {
205 match self {
206 #[cfg(feature = "zlib")]
207 Kind::Zlib => Zlib::finish(writer, block_sizes),
208 #[cfg(feature = "lzvn")]
209 Kind::Lzvn => Lzvn::finish(writer, block_sizes),
210 #[cfg(feature = "lzfse")]
211 Kind::Lzfse => Lzfse::finish(writer, block_sizes),
212 #[allow(unreachable_patterns)]
213 _ => panic!("Unsupported compression kind {self}"),
214 }
215 }
216}
217
218impl Default for Kind {
219 #[inline]
220 fn default() -> Self {
221 if Self::Lzfse.supported() {
222 Self::Lzfse
223 } else if Self::Zlib.supported() {
224 Self::Zlib
225 } else {
226 Self::Lzvn
227 }
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 const PLAINTEXT: &[u8] = include_bytes!("mod.rs");
236
237 pub(super) fn compressor_round_trip<C: CompressorImpl>(c: &mut C) {
238 let mut buf = vec![0u8; PLAINTEXT.len() * 2];
239 let len = c.compress(&mut buf, PLAINTEXT, 6).unwrap();
240 assert!(len > 0);
241 assert!(len < buf.len());
242 let ciphertext = &buf[..len];
243 let mut buf = vec![0u8; PLAINTEXT.len() + 1];
244 let len = c.decompress(&mut buf, ciphertext).unwrap();
245 assert_eq!(&buf[..len], PLAINTEXT);
246 }
247}