1use std::io;
8
9pub use zstd_safe::{CParameter, DParameter, InBuffer, OutBuffer, WriteBuf};
10
11use crate::dict::{DecoderDictionary, EncoderDictionary};
12use crate::map_error_code;
13
14pub trait Operation {
18 fn run<C: WriteBuf + ?Sized>(
25 &mut self,
26 input: &mut InBuffer<'_>,
27 output: &mut OutBuffer<'_, C>,
28 ) -> io::Result<usize>;
29
30 fn run_on_buffers(
35 &mut self,
36 input: &[u8],
37 output: &mut [u8],
38 ) -> io::Result<Status> {
39 let mut input = InBuffer::around(input);
40 let mut output = OutBuffer::around(output);
41
42 let remaining = self.run(&mut input, &mut output)?;
43
44 Ok(Status {
45 remaining,
46 bytes_read: input.pos(),
47 bytes_written: output.pos(),
48 })
49 }
50
51 fn flush<C: WriteBuf + ?Sized>(
56 &mut self,
57 output: &mut OutBuffer<'_, C>,
58 ) -> io::Result<usize> {
59 let _ = output;
60 Ok(0)
61 }
62
63 fn reinit(&mut self) -> io::Result<()> {
67 Ok(())
68 }
69
70 fn finish<C: WriteBuf + ?Sized>(
77 &mut self,
78 output: &mut OutBuffer<'_, C>,
79 finished_frame: bool,
80 ) -> io::Result<usize> {
81 let _ = output;
82 let _ = finished_frame;
83 Ok(0)
84 }
85}
86
87pub struct NoOp;
89
90impl Operation for NoOp {
91 fn run<C: WriteBuf + ?Sized>(
92 &mut self,
93 input: &mut InBuffer<'_>,
94 output: &mut OutBuffer<'_, C>,
95 ) -> io::Result<usize> {
96 let src = &input.src[input.pos..];
98 let output_pos = output.pos();
100 let dst = unsafe { output.as_mut_ptr().add(output_pos) };
101
102 let len = usize::min(src.len(), output.capacity() - output_pos);
104 let src = &src[..len];
105
106 unsafe { std::ptr::copy_nonoverlapping(src.as_ptr(), dst, len) };
110 input.set_pos(input.pos() + len);
111 unsafe { output.set_pos(output_pos + len) };
112
113 Ok(0)
114 }
115}
116
117pub struct Status {
119 pub remaining: usize,
125
126 pub bytes_read: usize,
128
129 pub bytes_written: usize,
131}
132
133pub struct Decoder<'a> {
135 context: MaybeOwnedDCtx<'a>,
136}
137
138fn create_dctx<'a>() -> io::Result<zstd_safe::DCtx<'a>> {
139 zstd_safe::DCtx::try_create().ok_or_else(|| {
140 io::Error::new(
141 io::ErrorKind::Other,
142 "zstd could not allocate a decompression context",
143 )
144 })
145}
146
147impl Decoder<'static> {
148 pub fn new() -> io::Result<Self> {
150 Self::with_dictionary(&[])
151 }
152
153 pub fn with_dictionary(dictionary: &[u8]) -> io::Result<Self> {
155 let mut context = create_dctx()?;
156 context.init().map_err(map_error_code)?;
157 context
158 .load_dictionary(dictionary)
159 .map_err(map_error_code)?;
160 Ok(Decoder {
161 context: MaybeOwnedDCtx::Owned(context),
162 })
163 }
164}
165
166impl<'a> Decoder<'a> {
167 pub fn with_context(context: &'a mut zstd_safe::DCtx<'static>) -> Self {
173 let _ = context.reset(zstd_safe::ResetDirective::SessionOnly);
176
177 Self {
178 context: MaybeOwnedDCtx::Borrowed(context),
179 }
180 }
181
182 pub fn with_prepared_dictionary<'b>(
184 dictionary: &'a DecoderDictionary<'b>,
185 ) -> io::Result<Self>
186 where
187 'b: 'a,
188 {
189 let mut context = create_dctx()?;
190 context
191 .ref_ddict(dictionary.as_ddict())
192 .map_err(map_error_code)?;
193 Ok(Decoder {
194 context: MaybeOwnedDCtx::Owned(context),
195 })
196 }
197
198 pub fn with_ref_prefix<'b>(ref_prefix: &'b [u8]) -> io::Result<Self>
200 where
201 'b: 'a,
202 {
203 let mut context = create_dctx()?;
204 context.ref_prefix(ref_prefix).map_err(map_error_code)?;
205 Ok(Decoder {
206 context: MaybeOwnedDCtx::Owned(context),
207 })
208 }
209
210 pub fn set_parameter(&mut self, parameter: DParameter) -> io::Result<()> {
212 match &mut self.context {
213 MaybeOwnedDCtx::Owned(x) => x.set_parameter(parameter),
214 MaybeOwnedDCtx::Borrowed(x) => x.set_parameter(parameter),
215 }
216 .map_err(map_error_code)?;
217 Ok(())
218 }
219}
220
221impl Operation for Decoder<'_> {
222 fn run<C: WriteBuf + ?Sized>(
223 &mut self,
224 input: &mut InBuffer<'_>,
225 output: &mut OutBuffer<'_, C>,
226 ) -> io::Result<usize> {
227 match &mut self.context {
228 MaybeOwnedDCtx::Owned(x) => x.decompress_stream(output, input),
229 MaybeOwnedDCtx::Borrowed(x) => x.decompress_stream(output, input),
230 }
231 .map_err(map_error_code)
232 }
233
234 fn flush<C: WriteBuf + ?Sized>(
235 &mut self,
236 output: &mut OutBuffer<'_, C>,
237 ) -> io::Result<usize> {
238 self.run(&mut InBuffer::around(&[]), output)?;
240
241 if output.pos() < output.capacity() {
243 Ok(0)
245 } else {
246 Ok(1)
248 }
249 }
250
251 fn reinit(&mut self) -> io::Result<()> {
252 match &mut self.context {
253 MaybeOwnedDCtx::Owned(x) => {
254 x.reset(zstd_safe::ResetDirective::SessionOnly)
255 }
256 MaybeOwnedDCtx::Borrowed(x) => {
257 x.reset(zstd_safe::ResetDirective::SessionOnly)
258 }
259 }
260 .map_err(map_error_code)?;
261 Ok(())
262 }
263
264 fn finish<C: WriteBuf + ?Sized>(
265 &mut self,
266 _output: &mut OutBuffer<'_, C>,
267 finished_frame: bool,
268 ) -> io::Result<usize> {
269 if finished_frame {
270 Ok(0)
271 } else {
272 Err(io::Error::new(
273 io::ErrorKind::UnexpectedEof,
274 "incomplete frame",
275 ))
276 }
277 }
278}
279
280pub struct Encoder<'a> {
282 context: MaybeOwnedCCtx<'a>,
283}
284
285impl Encoder<'static> {
286 pub fn new(level: i32) -> io::Result<Self> {
288 Self::with_dictionary(level, &[])
289 }
290
291 pub fn with_dictionary(level: i32, dictionary: &[u8]) -> io::Result<Self> {
293 let mut context = zstd_safe::CCtx::create();
294
295 context
296 .set_parameter(CParameter::CompressionLevel(level))
297 .map_err(map_error_code)?;
298
299 context
300 .load_dictionary(dictionary)
301 .map_err(map_error_code)?;
302
303 Ok(Encoder {
304 context: MaybeOwnedCCtx::Owned(context),
305 })
306 }
307}
308
309impl<'a> Encoder<'a> {
310 pub fn with_context(context: &'a mut zstd_safe::CCtx<'static>) -> Self {
317 let _ = context.reset(zstd_safe::ResetDirective::SessionOnly);
320
321 Self {
322 context: MaybeOwnedCCtx::Borrowed(context),
323 }
324 }
325
326 pub fn with_prepared_dictionary<'b>(
328 dictionary: &'a EncoderDictionary<'b>,
329 ) -> io::Result<Self>
330 where
331 'b: 'a,
332 {
333 let mut context = zstd_safe::CCtx::create();
334 context
335 .ref_cdict(dictionary.as_cdict())
336 .map_err(map_error_code)?;
337 Ok(Encoder {
338 context: MaybeOwnedCCtx::Owned(context),
339 })
340 }
341
342 pub fn with_ref_prefix<'b>(
344 level: i32,
345 ref_prefix: &'b [u8],
346 ) -> io::Result<Self>
347 where
348 'b: 'a,
349 {
350 let mut context = zstd_safe::CCtx::create();
351
352 context
353 .set_parameter(CParameter::CompressionLevel(level))
354 .map_err(map_error_code)?;
355
356 context.ref_prefix(ref_prefix).map_err(map_error_code)?;
357
358 Ok(Encoder {
359 context: MaybeOwnedCCtx::Owned(context),
360 })
361 }
362
363 pub fn set_parameter(&mut self, parameter: CParameter) -> io::Result<()> {
365 match &mut self.context {
366 MaybeOwnedCCtx::Owned(x) => x.set_parameter(parameter),
367 MaybeOwnedCCtx::Borrowed(x) => x.set_parameter(parameter),
368 }
369 .map_err(map_error_code)?;
370 Ok(())
371 }
372
373 pub fn set_pledged_src_size(
382 &mut self,
383 pledged_src_size: Option<u64>,
384 ) -> io::Result<()> {
385 match &mut self.context {
386 MaybeOwnedCCtx::Owned(x) => {
387 x.set_pledged_src_size(pledged_src_size)
388 }
389 MaybeOwnedCCtx::Borrowed(x) => {
390 x.set_pledged_src_size(pledged_src_size)
391 }
392 }
393 .map_err(map_error_code)?;
394 Ok(())
395 }
396}
397
398impl<'a> Operation for Encoder<'a> {
399 fn run<C: WriteBuf + ?Sized>(
400 &mut self,
401 input: &mut InBuffer<'_>,
402 output: &mut OutBuffer<'_, C>,
403 ) -> io::Result<usize> {
404 match &mut self.context {
405 MaybeOwnedCCtx::Owned(x) => x.compress_stream(output, input),
406 MaybeOwnedCCtx::Borrowed(x) => x.compress_stream(output, input),
407 }
408 .map_err(map_error_code)
409 }
410
411 fn flush<C: WriteBuf + ?Sized>(
412 &mut self,
413 output: &mut OutBuffer<'_, C>,
414 ) -> io::Result<usize> {
415 match &mut self.context {
416 MaybeOwnedCCtx::Owned(x) => x.flush_stream(output),
417 MaybeOwnedCCtx::Borrowed(x) => x.flush_stream(output),
418 }
419 .map_err(map_error_code)
420 }
421
422 fn finish<C: WriteBuf + ?Sized>(
423 &mut self,
424 output: &mut OutBuffer<'_, C>,
425 _finished_frame: bool,
426 ) -> io::Result<usize> {
427 match &mut self.context {
428 MaybeOwnedCCtx::Owned(x) => x.end_stream(output),
429 MaybeOwnedCCtx::Borrowed(x) => x.end_stream(output),
430 }
431 .map_err(map_error_code)
432 }
433
434 fn reinit(&mut self) -> io::Result<()> {
435 match &mut self.context {
436 MaybeOwnedCCtx::Owned(x) => {
437 x.reset(zstd_safe::ResetDirective::SessionOnly)
438 }
439 MaybeOwnedCCtx::Borrowed(x) => {
440 x.reset(zstd_safe::ResetDirective::SessionOnly)
441 }
442 }
443 .map_err(map_error_code)?;
444 Ok(())
445 }
446}
447
448enum MaybeOwnedCCtx<'a> {
449 Owned(zstd_safe::CCtx<'a>),
450 Borrowed(&'a mut zstd_safe::CCtx<'static>),
451}
452
453enum MaybeOwnedDCtx<'a> {
454 Owned(zstd_safe::DCtx<'a>),
455 Borrowed(&'a mut zstd_safe::DCtx<'static>),
456}
457
458#[cfg(test)]
459mod tests {
460
461 #[cfg(feature = "arrays")]
463 #[test]
464 fn test_cycle() {
465 use super::{Decoder, Encoder, InBuffer, Operation, OutBuffer};
466
467 let mut encoder = Encoder::new(1).unwrap();
468 let mut decoder = Decoder::new().unwrap();
469
470 let mut input = InBuffer::around(b"AbcdefAbcdefabcdef");
472
473 let mut output = [0u8; 128];
474 let mut output = OutBuffer::around(&mut output);
475
476 loop {
477 encoder.run(&mut input, &mut output).unwrap();
478
479 if input.pos == input.src.len() {
480 break;
481 }
482 }
483 encoder.finish(&mut output, true).unwrap();
484
485 let initial_data = input.src;
486
487 let mut input = InBuffer::around(output.as_slice());
489 let mut output = [0u8; 128];
490 let mut output = OutBuffer::around(&mut output);
491
492 loop {
493 decoder.run(&mut input, &mut output).unwrap();
494
495 if input.pos == input.src.len() {
496 break;
497 }
498 }
499
500 assert_eq!(initial_data, output.as_slice());
501 }
502}