1#[cfg(feature = "alloc")]
2use alloc::borrow::Cow;
3#[cfg(feature = "alloc")]
4use alloc::boxed::Box;
5#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7
8use crate::BlockDecodeWorkspace;
9use zrip_core::dict::Dictionary;
10use zrip_core::error::DecompressError;
11
12pub struct DecompressContext {
28 dict: Option<Dictionary>,
29 output: Vec<u8>,
30 ws: Box<BlockDecodeWorkspace>,
31}
32
33impl Default for DecompressContext {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl DecompressContext {
40 pub fn new() -> Self {
42 Self {
43 dict: None,
44 output: Vec::new(),
45 ws: Box::new(BlockDecodeWorkspace::new()),
46 }
47 }
48
49 pub fn with_dict(dict: Dictionary) -> Self {
51 let mut ws = Box::new(BlockDecodeWorkspace::new());
52 ws.cache_dict(&dict);
53 Self {
54 dict: Some(dict),
55 output: Vec::new(),
56 ws,
57 }
58 }
59
60 pub fn decompress(&mut self, input: &[u8]) -> Result<Cow<'_, [u8]>, DecompressError> {
62 self.decompress_with_limit(input, zrip_core::DEFAULT_DECOMPRESS_LIMIT)
63 }
64
65 pub fn decompress_with_limit(
70 &mut self,
71 input: &[u8],
72 max_output: usize,
73 ) -> Result<Cow<'_, [u8]>, DecompressError> {
74 self.output.clear();
75 let dict_ref = self.dict.as_ref();
76 if input.len() >= 4 {
77 let magic = u32::from_le_bytes([input[0], input[1], input[2], input[3]]);
78 if magic == zrip_core::frame::ZSTD_MAGIC {
79 let consumed = 4 + super::decompress_frame_after_magic(
80 &input[4..],
81 &mut self.output,
82 max_output,
83 dict_ref,
84 &mut self.ws,
85 )?;
86 if consumed == input.len() {
87 return Ok(Cow::Borrowed(&self.output));
88 }
89 return self.decompress_tail(input, consumed, max_output);
90 }
91 }
92 self.decompress_tail(input, 0, max_output)
93 }
94
95 fn decompress_tail(
96 &mut self,
97 input: &[u8],
98 mut offset: usize,
99 max_output: usize,
100 ) -> Result<Cow<'_, [u8]>, DecompressError> {
101 let dict_ref = self.dict.as_ref();
102 while offset < input.len() {
103 let remaining = &input[offset..];
104 if let Some(skip_len) = super::skip_skippable_frame(remaining) {
105 offset += skip_len;
106 continue;
107 }
108 let frame_limit = super::remaining_output_limit(self.output.len(), 0, max_output)?;
109 let consumed = super::decompress_frame(
110 remaining,
111 &mut self.output,
112 frame_limit,
113 dict_ref,
114 &mut self.ws,
115 )?;
116 offset += consumed;
117 }
118 Ok(Cow::Borrowed(&self.output))
119 }
120
121 pub fn decompress_after_magic_with_limit(
125 &mut self,
126 input: &[u8],
127 max_output: usize,
128 ) -> Result<Cow<'_, [u8]>, DecompressError> {
129 self.output.clear();
130 super::decompress_frame_after_magic(
131 input,
132 &mut self.output,
133 max_output,
134 self.dict.as_ref(),
135 &mut self.ws,
136 )?;
137 Ok(Cow::Borrowed(&self.output))
138 }
139
140 pub fn decompress_after_magic_into(
144 &mut self,
145 input: &[u8],
146 output: &mut Vec<u8>,
147 max_output: usize,
148 ) -> Result<usize, DecompressError> {
149 let start = output.len();
150 super::decompress_frame_after_magic(
151 input,
152 output,
153 max_output,
154 self.dict.as_ref(),
155 &mut self.ws,
156 )?;
157 Ok(output.len() - start)
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use alloc::vec::Vec;
165
166 fn push_block_header(out: &mut Vec<u8>, last: bool, block_type: u32, block_size: usize) {
167 let raw = ((block_size as u32) << 3) | (block_type << 1) | u32::from(last);
168 out.push(raw as u8);
169 out.push((raw >> 8) as u8);
170 out.push((raw >> 16) as u8);
171 }
172
173 #[test]
174 fn decompress_after_magic_into_appends_output() {
175 let mut frame = Vec::new();
176 frame.push(0x20);
177 frame.push(5);
178 push_block_header(&mut frame, true, 0, 5);
179 frame.extend_from_slice(b"hello");
180
181 let mut ctx = DecompressContext::new();
182 let mut output = b"prefix".to_vec();
183 let written = ctx
184 .decompress_after_magic_into(&frame, &mut output, usize::MAX)
185 .unwrap();
186 assert_eq!(written, 5);
187 assert_eq!(output, b"prefixhello");
188 }
189
190 #[test]
191 fn decompress_fast_path_continues_after_first_frame() {
192 fn raw_frame(bytes: &[u8]) -> Vec<u8> {
193 let mut frame = Vec::new();
194 frame.extend_from_slice(&zrip_core::frame::ZSTD_MAGIC.to_le_bytes());
195 frame.push(0x20);
196 frame.push(bytes.len() as u8);
197 push_block_header(&mut frame, true, 0, bytes.len());
198 frame.extend_from_slice(bytes);
199 frame
200 }
201
202 let mut stream = raw_frame(b"hello");
203 stream.extend_from_slice(&raw_frame(b"there"));
204
205 let mut ctx = DecompressContext::new();
206 let output = ctx.decompress(&stream).unwrap();
207 assert_eq!(&*output, b"hellothere");
208 }
209}