zstd_safe/lib.rs
1#![no_std]
2//! Minimal safe wrapper around zstd-sys.
3//!
4//! This crates provides a minimal translation of the [zstd-sys] methods.
5//! For a more comfortable high-level library, see the [zstd] crate.
6//!
7//! [zstd-sys]: https://crates.io/crates/zstd-sys
8//! [zstd]: https://crates.io/crates/zstd
9//!
10//! Most of the functions here map 1-for-1 to a function from
11//! [the C zstd library][zstd-c] mentioned in their descriptions.
12//! Check the [source documentation][doc] for more information on their
13//! behaviour.
14//!
15//! [doc]: https://facebook.github.io/zstd/zstd_manual.html
16//! [zstd-c]: https://facebook.github.io/zstd/
17//!
18//! Features denoted as experimental in the C library are hidden behind an
19//! `experimental` feature.
20#![cfg_attr(feature = "doc-cfg", feature(doc_cfg))]
21
22// TODO: Use alloc feature instead to implement stuff for Vec
23// TODO: What about Cursor?
24#[cfg(feature = "std")]
25extern crate std;
26
27#[cfg(test)]
28mod tests;
29
30#[cfg(feature = "with-rust-allocator")]
31mod rust_allocator;
32
33#[cfg(feature = "seekable")]
34pub mod seekable;
35
36// Re-export zstd-sys
37pub use zstd_sys;
38
39/// How to compress data.
40pub use zstd_sys::ZSTD_strategy as Strategy;
41
42/// Frame progression state.
43#[cfg(feature = "experimental")]
44#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
45pub use zstd_sys::ZSTD_frameProgression as FrameProgression;
46
47/// Reset directive.
48// pub use zstd_sys::ZSTD_ResetDirective as ResetDirective;
49use core::ffi::{c_char, c_int, c_ulonglong, c_void};
50
51use core::marker::PhantomData;
52use core::num::{NonZeroU32, NonZeroU64};
53use core::ops::{Deref, DerefMut};
54use core::ptr::NonNull;
55use core::str;
56
57include!("constants.rs");
58
59#[cfg(feature = "experimental")]
60include!("constants_experimental.rs");
61
62#[cfg(feature = "seekable")]
63include!("constants_seekable.rs");
64
65/// Represents the compression level used by zstd.
66pub type CompressionLevel = i32;
67
68/// Represents a possible error from the zstd library.
69pub type ErrorCode = usize;
70
71/// Wrapper result around most zstd functions.
72///
73/// Either a success code (usually number of bytes written), or an error code.
74pub type SafeResult = Result<usize, ErrorCode>;
75
76/// The error code zstd itself returns when an allocation fails
77/// (`-ZSTD_error_memory_allocation` as a `size_t`), for the places where a
78/// context is allocated on the Rust side of a call.
79#[cfg(feature = "experimental")]
80const MEMORY_ALLOCATION_ERROR: ErrorCode =
81 (zstd_sys::ZSTD_ErrorCode::ZSTD_error_memory_allocation as usize)
82 .wrapping_neg();
83
84/// The error code zstd itself returns for a parameter it does not support
85/// (`-ZSTD_error_parameter_unsupported` as a `size_t`), for the parameters
86/// that are refused on the Rust side of a call.
87#[cfg(feature = "experimental")]
88const PARAMETER_UNSUPPORTED_ERROR: ErrorCode =
89 (zstd_sys::ZSTD_ErrorCode::ZSTD_error_parameter_unsupported as usize)
90 .wrapping_neg();
91
92/// The error code zstd itself returns for an operation the context is in no
93/// state to perform (`-ZSTD_error_stage_wrong` as a `size_t`), for the places
94/// where that has to be decided on the Rust side of a call.
95const STAGE_WRONG_ERROR: ErrorCode =
96 (zstd_sys::ZSTD_ErrorCode::ZSTD_error_stage_wrong as usize).wrapping_neg();
97
98/// Indicates an error happened when parsing the frame content size.
99///
100/// The stream may be corrupted, or the given frame prefix was too small.
101#[derive(Debug)]
102pub struct ContentSizeError;
103
104impl core::fmt::Display for ContentSizeError {
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106 f.write_str("Could not get content size")
107 }
108}
109
110/// Returns true if code represents error.
111fn is_error(code: usize) -> bool {
112 // Safety: Just FFI
113 unsafe { zstd_sys::ZSTD_isError(code) != 0 }
114}
115
116/// Parse the result code
117///
118/// Returns the number of bytes written if the code represents success,
119/// or the error message code otherwise.
120fn parse_code(code: usize) -> SafeResult {
121 if !is_error(code) {
122 Ok(code)
123 } else {
124 Err(code)
125 }
126}
127
128/// Parse a content size value.
129///
130/// zstd uses 2 special content size values to indicate either unknown size or parsing error.
131fn parse_content_size(
132 content_size: u64,
133) -> Result<Option<u64>, ContentSizeError> {
134 match content_size {
135 CONTENTSIZE_ERROR => Err(ContentSizeError),
136 CONTENTSIZE_UNKNOWN => Ok(None),
137 other => Ok(Some(other)),
138 }
139}
140
141fn ptr_void(src: &[u8]) -> *const c_void {
142 src.as_ptr() as *const c_void
143}
144
145fn ptr_mut_void(dst: &mut (impl WriteBuf + ?Sized)) -> *mut c_void {
146 dst.as_mut_ptr() as *mut c_void
147}
148
149/// Returns the ZSTD version.
150///
151/// Returns `major * 10_000 + minor * 100 + patch`.
152/// So 1.5.3 would be returned as `10_503`.
153pub fn version_number() -> u32 {
154 // Safety: Just FFI
155 unsafe { zstd_sys::ZSTD_versionNumber() as u32 }
156}
157
158/// Returns a string representation of the ZSTD version.
159///
160/// For example "1.5.3".
161pub fn version_string() -> &'static str {
162 // Safety: Assumes `ZSTD_versionString` returns a valid utf8 string.
163 unsafe { c_char_to_str(zstd_sys::ZSTD_versionString()) }
164}
165
166/// Returns the minimum (fastest) compression level supported.
167///
168/// This is likely going to be a _very_ large negative number.
169pub fn min_c_level() -> CompressionLevel {
170 // Safety: Just FFI
171 unsafe { zstd_sys::ZSTD_minCLevel() as CompressionLevel }
172}
173
174/// Returns the maximum (slowest) compression level supported.
175pub fn max_c_level() -> CompressionLevel {
176 // Safety: Just FFI
177 unsafe { zstd_sys::ZSTD_maxCLevel() as CompressionLevel }
178}
179
180/// Wraps the `ZSTD_compress` function.
181///
182/// This will try to compress `src` entirely and write the result to `dst`, returning the number of
183/// bytes written. If `dst` is too small to hold the compressed content, an error will be returned.
184///
185/// For streaming operations that don't require to store the entire input/output in memory, see
186/// `compress_stream`.
187pub fn compress<C: WriteBuf + ?Sized>(
188 dst: &mut C,
189 src: &[u8],
190 compression_level: CompressionLevel,
191) -> SafeResult {
192 // `ZSTD_compress` allocates its workspace with the C runtime's `malloc`.
193 // Route it through a context built on the Rust global allocator instead;
194 // `ZSTD_compressCCtx` on a fresh context is exactly what `ZSTD_compress`
195 // does internally.
196 #[cfg(feature = "with-rust-allocator")]
197 {
198 let mut cctx = CCtx::try_create().ok_or(MEMORY_ALLOCATION_ERROR)?;
199 cctx.compress(dst, src, compression_level)
200 }
201
202 // Safety: ZSTD_compress indeed returns how many bytes have been written.
203 #[cfg(not(feature = "with-rust-allocator"))]
204 unsafe {
205 dst.write_from(|buffer, capacity| {
206 parse_code(zstd_sys::ZSTD_compress(
207 buffer,
208 capacity,
209 ptr_void(src),
210 src.len(),
211 compression_level,
212 ))
213 })
214 }
215}
216
217/// Wraps the `ZSTD_decompress` function.
218///
219/// This is a one-step decompression (not streaming).
220///
221/// You will need to make sure `dst` is large enough to store all the decompressed content, or an
222/// error will be returned.
223///
224/// If decompression was a success, the number of bytes written will be returned.
225pub fn decompress<C: WriteBuf + ?Sized>(
226 dst: &mut C,
227 src: &[u8],
228) -> SafeResult {
229 // See `compress()`: keep the implicit context's allocations visible to
230 // the Rust global allocator.
231 #[cfg(feature = "with-rust-allocator")]
232 {
233 let mut dctx = DCtx::try_create().ok_or(MEMORY_ALLOCATION_ERROR)?;
234 dctx.decompress(dst, src)
235 }
236
237 // Safety: ZSTD_decompress indeed returns how many bytes have been written.
238 #[cfg(not(feature = "with-rust-allocator"))]
239 unsafe {
240 dst.write_from(|buffer, capacity| {
241 parse_code(zstd_sys::ZSTD_decompress(
242 buffer,
243 capacity,
244 ptr_void(src),
245 src.len(),
246 ))
247 })
248 }
249}
250
251/// Wraps the `ZSTD_getDecompressedSize` function.
252///
253/// Returns `None` if the size could not be found, or if the content is actually empty.
254#[deprecated(note = "Use get_frame_content_size instead.")]
255pub fn get_decompressed_size(src: &[u8]) -> Option<NonZeroU64> {
256 // Safety: Just FFI
257 NonZeroU64::new(unsafe {
258 zstd_sys::ZSTD_getDecompressedSize(ptr_void(src), src.len()) as u64
259 })
260}
261
262/// Maximum compressed size in worst case single-pass scenario
263pub fn compress_bound(src_size: usize) -> usize {
264 // Safety: Just FFI
265 unsafe { zstd_sys::ZSTD_compressBound(src_size) }
266}
267
268/// Remembers an error that may have left a context in a state zstd considers
269/// undefined.
270///
271/// zstd.h says of both `ZSTD_compressStream2()` and `ZSTD_decompressStream()`
272/// that "if an operation ends with an error, it may leave [the context] in an
273/// undefined state", and that calling them again on such a state is undefined
274/// behaviour - the context has to be reset first. So once a streaming
275/// operation fails, refuse to run another one until something resets the
276/// context, and hand the original error back instead.
277#[derive(Clone, Debug, Default)]
278struct Poison(Option<ErrorCode>);
279
280impl Poison {
281 /// Fails with the error that poisoned the context, if there was one.
282 fn guard(&self) -> Result<(), ErrorCode> {
283 match self.0 {
284 Some(code) => Err(code),
285 None => Ok(()),
286 }
287 }
288
289 /// Remembers `res` if it failed, and passes it through.
290 fn record(&mut self, res: SafeResult) -> SafeResult {
291 if let Err(code) = res {
292 self.0 = Some(code);
293 }
294 res
295 }
296
297 /// The context was reset, so it is usable again.
298 fn clear(&mut self) {
299 self.0 = None;
300 }
301}
302
303/// What a `CCtx` knows about its worker threads (`NbWorkers`).
304#[derive(Clone, Copy, Debug, Default)]
305struct Workers {
306 /// `NbWorkers` was ever set to a non-zero value, see `guard_own_tables`.
307 ever: bool,
308 /// `NbWorkers` is currently set to a non-zero value.
309 requested: bool,
310 /// A multithreaded frame was started and not finished.
311 ///
312 /// Its jobs may still be running on the worker threads, reading the
313 /// context's dictionary and job table: zstd only waits for them when the
314 /// next frame starts, or when the context is freed. Until then, nothing
315 /// may free or resize what they use. See `CCtx::wait_for_workers()`.
316 pending: bool,
317}
318
319/// Compression context
320///
321/// It is recommended to allocate a single context per thread and re-use it
322/// for many compression operations.
323pub struct CCtx<'a>(
324 NonNull<zstd_sys::ZSTD_CCtx>,
325 PhantomData<&'a ()>,
326 Poison,
327 Workers,
328);
329
330impl Default for CCtx<'_> {
331 fn default() -> Self {
332 CCtx::create()
333 }
334}
335
336impl<'a> CCtx<'a> {
337 /// Tries to create a new context.
338 ///
339 /// Returns `None` if zstd returns a NULL pointer - may happen if allocation fails.
340 ///
341 /// With the `with-rust-allocator` feature enabled, the context's internal
342 /// allocations go through Rust's global allocator instead of the C
343 /// runtime's `malloc`.
344 pub fn try_create() -> Option<Self> {
345 #[cfg(feature = "with-rust-allocator")]
346 // Safety: the callbacks satisfy zstd's allocation contract and remain
347 // valid for the lifetime of the context.
348 let context = unsafe {
349 zstd_sys::ZSTD_createCCtx_advanced(
350 crate::rust_allocator::RUST_GLOBAL_ALLOCATOR,
351 )
352 };
353 #[cfg(not(feature = "with-rust-allocator"))]
354 // Safety: zstd returns a newly owned context or null.
355 let context = unsafe { zstd_sys::ZSTD_createCCtx() };
356
357 Some(Self(
358 NonNull::new(context)?,
359 PhantomData,
360 Poison::default(),
361 Workers::default(),
362 ))
363 }
364
365 /// Wrap `ZSTD_createCCtx`
366 ///
367 /// # Panics
368 ///
369 /// If zstd returns a NULL pointer.
370 pub fn create() -> Self {
371 Self::try_create()
372 .expect("zstd returned null pointer when creating new context")
373 }
374
375 /// Ends the session of a one-shot compression, and passes `res` through.
376 ///
377 /// zstd.h has these calls imply a reset, but most of them leave the stream
378 /// stage alone (and their source size behind as the pledged one), while
379 /// `ZSTD_compress2()` stays mid-frame when it fails. A later stream call
380 /// would then carry on from a state that still points at buffers and
381 /// dictionaries borrowed for that one call. So reset explicitly: the next
382 /// stream starts from scratch.
383 fn finish_one_shot(&mut self, res: SafeResult) -> SafeResult {
384 self.reset(ResetDirective::SessionOnly).and(res)
385 }
386
387 /// Wraps the `ZSTD_compressCCtx()` function
388 pub fn compress<C: WriteBuf + ?Sized>(
389 &mut self,
390 dst: &mut C,
391 src: &[u8],
392 compression_level: CompressionLevel,
393 ) -> SafeResult {
394 // Safety: ZSTD_compressCCtx returns how many bytes were written.
395 let res = unsafe {
396 dst.write_from(|buffer, capacity| {
397 parse_code(zstd_sys::ZSTD_compressCCtx(
398 self.0.as_ptr(),
399 buffer,
400 capacity,
401 ptr_void(src),
402 src.len(),
403 compression_level,
404 ))
405 })
406 };
407 self.finish_one_shot(res)
408 }
409
410 /// Wraps the `ZSTD_compress2()` function.
411 pub fn compress2<C: WriteBuf + ?Sized>(
412 &mut self,
413 dst: &mut C,
414 src: &[u8],
415 ) -> SafeResult {
416 // Safety: ZSTD_compress2 returns how many bytes were written.
417 let res = unsafe {
418 dst.write_from(|buffer, capacity| {
419 parse_code(zstd_sys::ZSTD_compress2(
420 self.0.as_ptr(),
421 buffer,
422 capacity,
423 ptr_void(src),
424 src.len(),
425 ))
426 })
427 };
428 // Unlike the others, this starts by resetting the session, and
429 // ends it when it succeeds. When it fails, it stays mid-frame,
430 // possibly with worker threads still running: leave it so (zstd then
431 // refuses to change the dictionary they read from), and make
432 // streaming calls wait for an explicit reset.
433 self.2.clear();
434 self.track_frame(res.is_ok());
435 self.2.record(res)
436 }
437
438 /// Wraps the `ZSTD_compress_usingDict()` function.
439 pub fn compress_using_dict<C: WriteBuf + ?Sized>(
440 &mut self,
441 dst: &mut C,
442 src: &[u8],
443 dict: &[u8],
444 compression_level: CompressionLevel,
445 ) -> SafeResult {
446 // Safety: ZSTD_compress_usingDict returns how many bytes were written.
447 let res = unsafe {
448 dst.write_from(|buffer, capacity| {
449 parse_code(zstd_sys::ZSTD_compress_usingDict(
450 self.0.as_ptr(),
451 buffer,
452 capacity,
453 ptr_void(src),
454 src.len(),
455 ptr_void(dict),
456 dict.len(),
457 compression_level,
458 ))
459 })
460 };
461 self.finish_one_shot(res)
462 }
463
464 /// Wraps the `ZSTD_compress_usingCDict()` function.
465 pub fn compress_using_cdict<C: WriteBuf + ?Sized>(
466 &mut self,
467 dst: &mut C,
468 src: &[u8],
469 cdict: &CDict<'_>,
470 ) -> SafeResult {
471 // Safety: ZSTD_compress_usingCDict returns how many bytes were written.
472 let res = unsafe {
473 dst.write_from(|buffer, capacity| {
474 parse_code(zstd_sys::ZSTD_compress_usingCDict(
475 self.0.as_ptr(),
476 buffer,
477 capacity,
478 ptr_void(src),
479 src.len(),
480 cdict.0.as_ptr(),
481 ))
482 })
483 };
484 self.finish_one_shot(res)
485 }
486
487 /// Initializes the context with the given compression level.
488 ///
489 /// This is equivalent to running:
490 /// * `reset()`
491 /// * `set_parameter(CompressionLevel, compression_level)`
492 ///
493 /// Fails while a multithreaded frame is left unfinished, see
494 /// [`CCtx::wait_for_workers()`].
495 pub fn init(&mut self, compression_level: CompressionLevel) -> SafeResult {
496 // This also unloads the dictionary.
497 self.guard_workers()?;
498 self.2.clear();
499 // Safety: Just FFI
500 let code = unsafe {
501 zstd_sys::ZSTD_initCStream(self.0.as_ptr(), compression_level)
502 };
503 self.2.record(parse_code(code))
504 }
505
506 /// Wraps the `ZSTD_initCStream_srcSize()` function.
507 #[cfg(feature = "experimental")]
508 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
509 #[deprecated(
510 note = "Use reset, set_parameter, and set_pledged_src_size instead."
511 )]
512 pub fn init_src_size(
513 &mut self,
514 compression_level: CompressionLevel,
515 pledged_src_size: u64,
516 ) -> SafeResult {
517 self.guard_workers()?;
518 self.2.clear();
519 // Safety: Just FFI
520 let code = unsafe {
521 zstd_sys::ZSTD_initCStream_srcSize(
522 self.0.as_ptr(),
523 compression_level as c_int,
524 pledged_src_size as c_ulonglong,
525 )
526 };
527 parse_code(code)
528 }
529
530 /// Wraps the `ZSTD_initCStream_usingDict()` function.
531 #[cfg(feature = "experimental")]
532 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
533 #[deprecated(
534 note = "Use reset, set_parameter, and load_dictionary instead."
535 )]
536 pub fn init_using_dict(
537 &mut self,
538 dict: &[u8],
539 compression_level: CompressionLevel,
540 ) -> SafeResult {
541 self.guard_workers()?;
542 self.2.clear();
543 // Safety: Just FFI
544 let code = unsafe {
545 zstd_sys::ZSTD_initCStream_usingDict(
546 self.0.as_ptr(),
547 ptr_void(dict),
548 dict.len(),
549 compression_level,
550 )
551 };
552 parse_code(code)
553 }
554
555 /// Wraps the `ZSTD_initCStream_usingCDict()` function.
556 ///
557 /// Dictionary must outlive the context.
558 ///
559 /// ```compile_fail,E0515
560 /// # #![allow(deprecated)]
561 /// fn compressor() -> zstd_safe::CCtx<'static> {
562 /// let cdict = zstd_safe::CDict::create(b"dictionary", 3);
563 /// let mut cctx = zstd_safe::CCtx::create();
564 /// cctx.init_using_cdict(&cdict).unwrap();
565 /// cctx // `cdict` would be dropped while `cctx` still points at it.
566 /// }
567 /// ```
568 #[cfg(feature = "experimental")]
569 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
570 #[deprecated(note = "Use reset and ref_cdict instead.")]
571 pub fn init_using_cdict<'b>(&mut self, cdict: &'a CDict<'b>) -> SafeResult
572 where
573 'b: 'a, // Dictionary outlives the stream.
574 {
575 self.guard_workers()?;
576 // Safety: Just FFI
577 let code = unsafe {
578 zstd_sys::ZSTD_initCStream_usingCDict(
579 self.0.as_ptr(),
580 cdict.0.as_ptr(),
581 )
582 };
583 parse_code(code)
584 }
585
586 /// Tries to load a dictionary.
587 ///
588 /// The dictionary content will be copied internally and does not need to be kept alive after
589 /// calling this function.
590 ///
591 /// If you need to use the same dictionary for multiple contexts, it may be more efficient to
592 /// create a `CDict` first, then loads that.
593 ///
594 /// The dictionary will apply to all compressed frames, until a new dictionary is set.
595 ///
596 /// Fails while a multithreaded frame is left unfinished, see
597 /// [`CCtx::wait_for_workers()`].
598 pub fn load_dictionary(&mut self, dict: &[u8]) -> SafeResult {
599 self.guard_workers()?;
600 // Safety: Just FFI
601 parse_code(unsafe {
602 zstd_sys::ZSTD_CCtx_loadDictionary(
603 self.0.as_ptr(),
604 ptr_void(dict),
605 dict.len(),
606 )
607 })
608 }
609
610 /// Wraps the `ZSTD_CCtx_refCDict()` function.
611 ///
612 /// Dictionary must outlive the context.
613 ///
614 /// Fails while a multithreaded frame is left unfinished, see
615 /// [`CCtx::wait_for_workers()`].
616 pub fn ref_cdict<'b>(&mut self, cdict: &'a CDict<'b>) -> SafeResult
617 where
618 'b: 'a,
619 {
620 self.guard_workers()?;
621 // Safety: Just FFI
622 parse_code(unsafe {
623 zstd_sys::ZSTD_CCtx_refCDict(self.0.as_ptr(), cdict.0.as_ptr())
624 })
625 }
626
627 /// Return to "no-dictionary" mode.
628 ///
629 /// This will disable any dictionary/prefix previously registered for future frames.
630 ///
631 /// Fails while a multithreaded frame is left unfinished, see
632 /// [`CCtx::wait_for_workers()`].
633 pub fn disable_dictionary(&mut self) -> SafeResult {
634 self.guard_workers()?;
635 // Safety: Just FFI
636 parse_code(unsafe {
637 zstd_sys::ZSTD_CCtx_loadDictionary(
638 self.0.as_ptr(),
639 core::ptr::null(),
640 0,
641 )
642 })
643 }
644
645 /// Use some prefix as single-use dictionary for the next compressed frame.
646 ///
647 /// Just like a dictionary, decompression will need to be given the same prefix.
648 ///
649 /// This is best used if the "prefix" looks like the data to be compressed.
650 ///
651 /// Fails while a multithreaded frame is left unfinished, see
652 /// [`CCtx::wait_for_workers()`].
653 pub fn ref_prefix<'b>(&mut self, prefix: &'b [u8]) -> SafeResult
654 where
655 'b: 'a,
656 {
657 self.guard_workers()?;
658 // Safety: Just FFI
659 parse_code(unsafe {
660 zstd_sys::ZSTD_CCtx_refPrefix(
661 self.0.as_ptr(),
662 ptr_void(prefix),
663 prefix.len(),
664 )
665 })
666 }
667
668 /// Performs a step of a streaming compression operation.
669 ///
670 /// This will read some data from `input` and/or write some data to `output`.
671 ///
672 /// # Returns
673 ///
674 /// A hint for the "ideal" amount of input data to provide in the next call.
675 ///
676 /// This hint is only for performance purposes.
677 ///
678 /// Wraps the `ZSTD_compressStream()` function.
679 pub fn compress_stream<C: WriteBuf + ?Sized>(
680 &mut self,
681 output: &mut OutBuffer<'_, C>,
682 input: &mut InBuffer<'_>,
683 ) -> SafeResult {
684 self.2.guard()?;
685 let mut output = output.wrap();
686 let mut input = input.wrap();
687 // Safety: Just FFI
688 let code = unsafe {
689 zstd_sys::ZSTD_compressStream(
690 self.0.as_ptr(),
691 ptr_mut(&mut output),
692 ptr_mut(&mut input),
693 )
694 };
695 let res = output.check(parse_code(code));
696 let res = self.2.record(input.check(res));
697 self.track_frame(false);
698 res
699 }
700
701 /// Performs a step of a streaming compression operation.
702 ///
703 /// This will read some data from `input` and/or write some data to `output`.
704 ///
705 /// The `end_op` directive can be used to specify what to do after: nothing special, flush
706 /// internal buffers, or end the frame.
707 ///
708 /// # Returns
709 ///
710 /// An lower bound for the amount of data that still needs to be flushed out.
711 ///
712 /// This is useful when flushing or ending the frame: you need to keep calling this function
713 /// until it returns 0.
714 ///
715 /// Wraps the `ZSTD_compressStream2()` function.
716 pub fn compress_stream2<C: WriteBuf + ?Sized>(
717 &mut self,
718 output: &mut OutBuffer<'_, C>,
719 input: &mut InBuffer<'_>,
720 end_op: zstd_sys::ZSTD_EndDirective,
721 ) -> SafeResult {
722 self.2.guard()?;
723 let mut output = output.wrap();
724 let mut input = input.wrap();
725 // Safety: Just FFI
726 let code = unsafe {
727 zstd_sys::ZSTD_compressStream2(
728 self.0.as_ptr(),
729 ptr_mut(&mut output),
730 ptr_mut(&mut input),
731 end_op,
732 )
733 };
734 let res = output.check(parse_code(code));
735 let res = self.2.record(input.check(res));
736 self.track_frame(
737 end_op == zstd_sys::ZSTD_EndDirective::ZSTD_e_end && res == Ok(0),
738 );
739 res
740 }
741
742 /// Flush any intermediate buffer.
743 ///
744 /// To fully flush, you should keep calling this function until it returns `Ok(0)`.
745 ///
746 /// Wraps the `ZSTD_flushStream()` function.
747 pub fn flush_stream<C: WriteBuf + ?Sized>(
748 &mut self,
749 output: &mut OutBuffer<'_, C>,
750 ) -> SafeResult {
751 self.2.guard()?;
752 let mut output = output.wrap();
753 // Safety: Just FFI
754 let code = unsafe {
755 zstd_sys::ZSTD_flushStream(self.0.as_ptr(), ptr_mut(&mut output))
756 };
757 let res = self.2.record(output.check(parse_code(code)));
758 self.track_frame(false);
759 res
760 }
761
762 /// Ends the stream.
763 ///
764 /// You should keep calling this function until it returns `Ok(0)`.
765 ///
766 /// Wraps the `ZSTD_endStream()` function.
767 pub fn end_stream<C: WriteBuf + ?Sized>(
768 &mut self,
769 output: &mut OutBuffer<'_, C>,
770 ) -> SafeResult {
771 self.2.guard()?;
772 let mut output = output.wrap();
773 // Safety: Just FFI
774 let code = unsafe {
775 zstd_sys::ZSTD_endStream(self.0.as_ptr(), ptr_mut(&mut output))
776 };
777 let res = self.2.record(output.check(parse_code(code)));
778 self.track_frame(res == Ok(0));
779 res
780 }
781
782 /// Records whether a streaming call left a multithreaded frame open.
783 fn track_frame(&mut self, frame_ended: bool) {
784 if self.3.requested {
785 self.3.pending = !frame_ended;
786 }
787 }
788
789 /// Fails if worker threads may still be running jobs of an unfinished
790 /// frame, see `Workers::pending`.
791 fn guard_workers(&self) -> Result<(), ErrorCode> {
792 if self.3.pending {
793 return Err(STAGE_WRONG_ERROR);
794 }
795 Ok(())
796 }
797
798 /// Abandons the current frame, if any, and waits for the worker threads
799 /// to be done with it.
800 ///
801 /// When a multithreaded frame (see `CParameter::NbWorkers`) is left
802 /// unfinished - abandoned, or interrupted by an error - its jobs may
803 /// still be running, reading the dictionary. zstd only waits for them
804 /// when the next frame starts or when the context is dropped. Until then,
805 /// anything that would unload the dictionary or change `NbWorkers` fails
806 /// with a `stage_wrong` error instead: `load_dictionary()`,
807 /// `ref_cdict()`, `ref_prefix()`, `disable_dictionary()`, `init()`, and
808 /// `reset()` with parameters.
809 ///
810 /// Calling this first lets them succeed. Like `reset(SessionOnly)`, it
811 /// discards the current frame and any pledged source size, and keeps
812 /// parameters and dictionary. It does nothing if no multithreaded frame
813 /// is unfinished.
814 pub fn wait_for_workers(&mut self) -> SafeResult {
815 if !self.3.pending {
816 return Ok(0);
817 }
818 self.reset(ResetDirective::SessionOnly)?;
819
820 // Starting a new frame is what makes zstd wait for the jobs of the
821 // previous one. With no input, this one has no jobs of its own.
822 let mut output = OutBuffer::around(&mut [][..]);
823 let mut input = InBuffer::around(&[]);
824 self.compress_stream2(
825 &mut output,
826 &mut input,
827 zstd_sys::ZSTD_EndDirective::ZSTD_e_continue,
828 )?;
829
830 self.reset(ResetDirective::SessionOnly)?;
831 self.3.pending = false;
832 Ok(0)
833 }
834
835 /// Returns the size currently used by this context.
836 ///
837 /// This may change over time.
838 pub fn sizeof(&self) -> usize {
839 // Safety: Just FFI
840 unsafe { zstd_sys::ZSTD_sizeof_CCtx(self.0.as_ptr()) }
841 }
842
843 /// Resets the state of the context.
844 ///
845 /// Depending on the reset mode, it can reset the session, the parameters, or both.
846 ///
847 /// Resetting the parameters also unloads the dictionary: this fails while
848 /// a multithreaded frame is left unfinished, see
849 /// [`CCtx::wait_for_workers()`]. Resetting only the session never does.
850 ///
851 /// Wraps the `ZSTD_CCtx_reset()` function.
852 pub fn reset(&mut self, reset: ResetDirective) -> SafeResult {
853 if !matches!(reset, ResetDirective::SessionOnly) {
854 self.guard_workers()?;
855 }
856 // Safety: Just FFI
857 let res = parse_code(unsafe {
858 zstd_sys::ZSTD_CCtx_reset(self.0.as_ptr(), reset.as_sys())
859 });
860 if res.is_ok() && reset.resets_session() {
861 self.2.clear();
862 }
863 if res.is_ok() && !matches!(reset, ResetDirective::SessionOnly) {
864 self.3.requested = false;
865 }
866 res
867 }
868
869 /// Sets a compression parameter.
870 ///
871 /// Some of these parameters need to be set during de-compression as well.
872 ///
873 /// Changing `NbWorkers` fails while a multithreaded frame is left
874 /// unfinished, see [`CCtx::wait_for_workers()`].
875 pub fn set_parameter(&mut self, param: CParameter) -> SafeResult {
876 if let CParameter::NbWorkers(_) = param {
877 // zstd would resize the job table the old jobs still use.
878 self.guard_workers()?;
879 }
880 // TODO: Until bindgen properly generates a binding for this, we'll need to do it here.
881
882 #[cfg(feature = "experimental")]
883 use zstd_sys::ZSTD_cParameter::{
884 ZSTD_c_experimentalParam1 as ZSTD_c_rsyncable,
885 ZSTD_c_experimentalParam10 as ZSTD_c_stableOutBuffer,
886 ZSTD_c_experimentalParam11 as ZSTD_c_blockDelimiters,
887 ZSTD_c_experimentalParam12 as ZSTD_c_validateSequences,
888 ZSTD_c_experimentalParam13 as ZSTD_c_useBlockSplitter,
889 ZSTD_c_experimentalParam14 as ZSTD_c_useRowMatchFinder,
890 ZSTD_c_experimentalParam15 as ZSTD_c_deterministicRefPrefix,
891 ZSTD_c_experimentalParam16 as ZSTD_c_prefetchCDictTables,
892 ZSTD_c_experimentalParam17 as ZSTD_c_enableSeqProducerFallback,
893 ZSTD_c_experimentalParam18 as ZSTD_c_maxBlockSize,
894 ZSTD_c_experimentalParam19 as ZSTD_c_searchForExternalRepcodes,
895 ZSTD_c_experimentalParam2 as ZSTD_c_format,
896 ZSTD_c_experimentalParam3 as ZSTD_c_forceMaxWindow,
897 ZSTD_c_experimentalParam4 as ZSTD_c_forceAttachDict,
898 ZSTD_c_experimentalParam5 as ZSTD_c_literalCompressionMode,
899 ZSTD_c_experimentalParam7 as ZSTD_c_srcSizeHint,
900 ZSTD_c_experimentalParam8 as ZSTD_c_enableDedicatedDictSearch,
901 ZSTD_c_experimentalParam9 as ZSTD_c_stableInBuffer,
902 };
903
904 use zstd_sys::ZSTD_cParameter::*;
905 use CParameter::*;
906
907 let (param, value) = match param {
908 #[cfg(feature = "experimental")]
909 RSyncable(rsyncable) => (ZSTD_c_rsyncable, rsyncable as c_int),
910 #[cfg(feature = "experimental")]
911 Format(format) => (ZSTD_c_format, format as c_int),
912 #[cfg(feature = "experimental")]
913 ForceMaxWindow(force) => (ZSTD_c_forceMaxWindow, force as c_int),
914 #[cfg(feature = "experimental")]
915 ForceAttachDict(force) => (ZSTD_c_forceAttachDict, force as c_int),
916 #[cfg(feature = "experimental")]
917 LiteralCompressionMode(mode) => {
918 (ZSTD_c_literalCompressionMode, mode as c_int)
919 }
920 #[cfg(feature = "experimental")]
921 SrcSizeHint(value) => (ZSTD_c_srcSizeHint, value as c_int),
922 #[cfg(feature = "experimental")]
923 EnableDedicatedDictSearch(enable) => {
924 (ZSTD_c_enableDedicatedDictSearch, enable as c_int)
925 }
926 // In stable mode zstd keeps reading from the `InBuffer` of one
927 // call during `flush_stream`/`end_stream`, but the slice is only
928 // borrowed for that one call: the safe API cannot uphold this.
929 #[cfg(feature = "experimental")]
930 StableInBuffer(true) => return Err(PARAMETER_UNSUPPORTED_ERROR),
931 #[cfg(feature = "experimental")]
932 StableInBuffer(false) => (ZSTD_c_stableInBuffer, 0),
933 #[cfg(feature = "experimental")]
934 StableOutBuffer(stable) => {
935 (ZSTD_c_stableOutBuffer, stable as c_int)
936 }
937 #[cfg(feature = "experimental")]
938 BlockDelimiters(value) => (ZSTD_c_blockDelimiters, value as c_int),
939 #[cfg(feature = "experimental")]
940 ValidateSequences(validate) => {
941 (ZSTD_c_validateSequences, validate as c_int)
942 }
943 #[cfg(feature = "experimental")]
944 UseBlockSplitter(split) => {
945 (ZSTD_c_useBlockSplitter, split as c_int)
946 }
947 #[cfg(feature = "experimental")]
948 UseRowMatchFinder(mode) => {
949 (ZSTD_c_useRowMatchFinder, mode as c_int)
950 }
951 #[cfg(feature = "experimental")]
952 DeterministicRefPrefix(deterministic) => {
953 (ZSTD_c_deterministicRefPrefix, deterministic as c_int)
954 }
955 #[cfg(feature = "experimental")]
956 PrefetchCDictTables(prefetch) => {
957 (ZSTD_c_prefetchCDictTables, prefetch as c_int)
958 }
959 #[cfg(feature = "experimental")]
960 EnableSeqProducerFallback(enable) => {
961 (ZSTD_c_enableSeqProducerFallback, enable as c_int)
962 }
963 #[cfg(feature = "experimental")]
964 MaxBlockSize(value) => (ZSTD_c_maxBlockSize, value as c_int),
965 #[cfg(feature = "experimental")]
966 SearchForExternalRepcodes(value) => {
967 (ZSTD_c_searchForExternalRepcodes, value as c_int)
968 }
969 TargetCBlockSize(value) => {
970 (ZSTD_c_targetCBlockSize, value as c_int)
971 }
972 CompressionLevel(level) => (ZSTD_c_compressionLevel, level),
973 WindowLog(value) => (ZSTD_c_windowLog, value as c_int),
974 HashLog(value) => (ZSTD_c_hashLog, value as c_int),
975 ChainLog(value) => (ZSTD_c_chainLog, value as c_int),
976 SearchLog(value) => (ZSTD_c_searchLog, value as c_int),
977 MinMatch(value) => (ZSTD_c_minMatch, value as c_int),
978 TargetLength(value) => (ZSTD_c_targetLength, value as c_int),
979 Strategy(strategy) => (ZSTD_c_strategy, strategy as c_int),
980 EnableLongDistanceMatching(flag) => {
981 (ZSTD_c_enableLongDistanceMatching, flag as c_int)
982 }
983 LdmHashLog(value) => (ZSTD_c_ldmHashLog, value as c_int),
984 LdmMinMatch(value) => (ZSTD_c_ldmMinMatch, value as c_int),
985 LdmBucketSizeLog(value) => {
986 (ZSTD_c_ldmBucketSizeLog, value as c_int)
987 }
988 LdmHashRateLog(value) => (ZSTD_c_ldmHashRateLog, value as c_int),
989 ContentSizeFlag(flag) => (ZSTD_c_contentSizeFlag, flag as c_int),
990 ChecksumFlag(flag) => (ZSTD_c_checksumFlag, flag as c_int),
991 DictIdFlag(flag) => (ZSTD_c_dictIDFlag, flag as c_int),
992
993 NbWorkers(value) => (ZSTD_c_nbWorkers, value as c_int),
994
995 JobSize(value) => (ZSTD_c_jobSize, value as c_int),
996
997 OverlapSizeLog(value) => (ZSTD_c_overlapLog, value as c_int),
998 };
999
1000 // Safety: Just FFI
1001 let res = parse_code(unsafe {
1002 zstd_sys::ZSTD_CCtx_setParameter(self.0.as_ptr(), param, value)
1003 });
1004 if res.is_ok() && matches!(param, ZSTD_c_nbWorkers) {
1005 self.3.requested = value != 0;
1006 self.3.ever |= value != 0;
1007 }
1008 res
1009 }
1010
1011 /// Guarantee that the input size will be this value.
1012 ///
1013 /// If given `None`, assumes the size is unknown.
1014 ///
1015 /// Unless explicitly disabled, this will cause the size to be written in the compressed frame
1016 /// header.
1017 ///
1018 /// If the actual data given to compress has a different size, an error will be returned.
1019 pub fn set_pledged_src_size(
1020 &mut self,
1021 pledged_src_size: Option<u64>,
1022 ) -> SafeResult {
1023 // Safety: Just FFI
1024 parse_code(unsafe {
1025 zstd_sys::ZSTD_CCtx_setPledgedSrcSize(
1026 self.0.as_ptr(),
1027 pledged_src_size.unwrap_or(CONTENTSIZE_UNKNOWN) as c_ulonglong,
1028 )
1029 })
1030 }
1031
1032 /// Fails if the tables of this context may not be the ones its applied
1033 /// parameters describe.
1034 ///
1035 /// Starting a multi-threaded frame overwrites the applied parameters with
1036 /// those of the workers, but leaves the context's own stage and workspace
1037 /// as the last single-threaded frame left them. If that frame was
1038 /// abandoned before its first block, zstd still accepts the calls that
1039 /// work on this context directly, and overruns a workspace that was sized
1040 /// for other parameters. The applied parameters cannot be queried, so
1041 /// refuse these calls as soon as a multi-threaded frame could have been
1042 /// started.
1043 #[cfg(feature = "experimental")]
1044 fn guard_own_tables(&self) -> Result<(), ErrorCode> {
1045 if self.3.ever {
1046 return Err(STAGE_WRONG_ERROR);
1047 }
1048 Ok(())
1049 }
1050
1051 /// Creates a copy of this context.
1052 ///
1053 /// This only works before any data has been compressed. An error will be
1054 /// returned otherwise.
1055 /// A poisoned context cannot be cloned until its session is reset.
1056 /// A context on which `NbWorkers` was ever set to a non-zero value cannot
1057 /// be cloned anymore.
1058 #[cfg(feature = "experimental")]
1059 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1060 #[deprecated(
1061 note = "Context copying is deprecated by zstd; create a new context and configure it instead."
1062 )]
1063 pub fn try_clone(
1064 &self,
1065 pledged_src_size: Option<u64>,
1066 ) -> Result<Self, ErrorCode> {
1067 // An error may leave the C context undefined; do not copy it.
1068 self.2.guard()?;
1069 self.guard_own_tables()?;
1070 // zstd copies the allocator callbacks from `self` into the new
1071 // context, so both must be created the same way; `try_create` always
1072 // picks the same allocator within a build.
1073 let context = Self::try_create().ok_or(MEMORY_ALLOCATION_ERROR)?;
1074
1075 // Safety: Just FFI
1076 parse_code(unsafe {
1077 zstd_sys::ZSTD_copyCCtx(
1078 context.0.as_ptr(),
1079 self.0.as_ptr(),
1080 pledged_src_size.unwrap_or(CONTENTSIZE_UNKNOWN),
1081 )
1082 })?;
1083
1084 Ok(context)
1085 }
1086
1087 /// Wraps the `ZSTD_getBlockSize()` function.
1088 #[cfg(feature = "experimental")]
1089 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1090 #[deprecated(
1091 note = "The block API is deprecated; use the normal compression API instead."
1092 )]
1093 pub fn get_block_size(&self) -> usize {
1094 // Safety: Just FFI
1095 unsafe { zstd_sys::ZSTD_getBlockSize(self.0.as_ptr()) }
1096 }
1097
1098 /// Wraps the `ZSTD_compressBlock()` function.
1099 ///
1100 /// Returns an error once `NbWorkers` was set to a non-zero value on this
1101 /// context.
1102 ///
1103 /// # Safety
1104 ///
1105 /// `src` becomes this context's history window, so it must stay allocated and unmodified until
1106 /// the next call to `compress_block` on this context (or until this context is dropped), as the
1107 /// following block is compressed against it.
1108 #[cfg(feature = "experimental")]
1109 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1110 #[deprecated(
1111 note = "The block API is deprecated; use compress_stream2 instead."
1112 )]
1113 pub unsafe fn compress_block<C: WriteBuf + ?Sized>(
1114 &mut self,
1115 dst: &mut C,
1116 src: &[u8],
1117 ) -> SafeResult {
1118 self.guard_own_tables()?;
1119 // Safety: ZSTD_compressBlock returns the number of bytes written.
1120 unsafe {
1121 dst.write_from(|buffer, capacity| {
1122 parse_code(zstd_sys::ZSTD_compressBlock(
1123 self.0.as_ptr(),
1124 buffer,
1125 capacity,
1126 ptr_void(src),
1127 src.len(),
1128 ))
1129 })
1130 }
1131 }
1132
1133 /// Returns the recommended input buffer size.
1134 ///
1135 /// Using this size may result in minor performance boost.
1136 pub fn in_size() -> usize {
1137 // Safety: Just FFI
1138 unsafe { zstd_sys::ZSTD_CStreamInSize() }
1139 }
1140
1141 /// Returns the recommended output buffer size.
1142 ///
1143 /// Using this may result in minor performance boost.
1144 pub fn out_size() -> usize {
1145 // Safety: Just FFI
1146 unsafe { zstd_sys::ZSTD_CStreamOutSize() }
1147 }
1148
1149 /// Use a shared thread pool for this context.
1150 ///
1151 /// Thread pool must outlive the context.
1152 #[cfg(all(feature = "experimental", feature = "zstdmt"))]
1153 #[cfg_attr(
1154 feature = "doc-cfg",
1155 doc(cfg(all(feature = "experimental", feature = "zstdmt")))
1156 )]
1157 pub fn ref_thread_pool<'b>(&mut self, pool: &'b ThreadPool) -> SafeResult
1158 where
1159 'b: 'a,
1160 {
1161 parse_code(unsafe {
1162 zstd_sys::ZSTD_CCtx_refThreadPool(self.0.as_ptr(), pool.0.as_ptr())
1163 })
1164 }
1165
1166 /// Return to using a private thread pool for this context.
1167 #[cfg(all(feature = "experimental", feature = "zstdmt"))]
1168 #[cfg_attr(
1169 feature = "doc-cfg",
1170 doc(cfg(all(feature = "experimental", feature = "zstdmt")))
1171 )]
1172 pub fn disable_thread_pool(&mut self) -> SafeResult {
1173 parse_code(unsafe {
1174 zstd_sys::ZSTD_CCtx_refThreadPool(
1175 self.0.as_ptr(),
1176 core::ptr::null_mut(),
1177 )
1178 })
1179 }
1180
1181 #[cfg(feature = "experimental")]
1182 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1183 pub fn get_frame_progression(&self) -> FrameProgression {
1184 // Safety: Just FFI
1185 unsafe { zstd_sys::ZSTD_getFrameProgression(self.0.as_ptr()) }
1186 }
1187}
1188
1189impl<'a> Drop for CCtx<'a> {
1190 fn drop(&mut self) {
1191 // Safety: Just FFI
1192 unsafe {
1193 zstd_sys::ZSTD_freeCCtx(self.0.as_ptr());
1194 }
1195 }
1196}
1197
1198// Safety: the context is a plain heap allocation this handle owns; zstd keeps
1199// no thread-local state for it, so it can be moved between threads.
1200unsafe impl Send for CCtx<'_> {}
1201// Safety: every method that mutates the context takes `&mut self`, so a shared
1202// `&CCtx` only ever reads. There is no interior mutability to race on.
1203unsafe impl Sync for CCtx<'_> {}
1204
1205/// Converts a zstd-owned C string to a `str`.
1206///
1207/// # Safety
1208///
1209/// `text` must point to a nul-terminated C string that lives for the rest of
1210/// the program - which is what the zstd functions this is used with return,
1211/// as they hand back pointers to static string literals.
1212unsafe fn c_char_to_str(text: *const c_char) -> &'static str {
1213 // Safety: guaranteed by the caller, see above.
1214 core::ffi::CStr::from_ptr(text)
1215 .to_str()
1216 .expect("bad error message from zstd")
1217}
1218
1219/// Returns the error string associated with an error code.
1220pub fn get_error_name(code: usize) -> &'static str {
1221 unsafe {
1222 // Safety: assumes ZSTD returns a well-formed utf8 string.
1223 let name = zstd_sys::ZSTD_getErrorName(code);
1224 c_char_to_str(name)
1225 }
1226}
1227
1228/// A Decompression Context.
1229///
1230/// The lifetime references the potential dictionary used for this context.
1231///
1232/// If no dictionary was used, it will most likely be `'static`.
1233///
1234/// Same as `DStream`.
1235pub struct DCtx<'a>(
1236 NonNull<zstd_sys::ZSTD_DCtx>,
1237 PhantomData<&'a ()>,
1238 Poison,
1239 DState,
1240);
1241
1242/// What a `DCtx` remembers about its own use.
1243#[derive(Clone, Copy, Debug, Default)]
1244struct DState {
1245 /// `RefMultipleDDicts` was ever enabled, see `try_clone`.
1246 #[cfg(feature = "experimental")]
1247 multiple_ddicts: bool,
1248 /// A streaming (or block) call ran since the session was last reset, so
1249 /// a stream may be open, see `finish_one_shot`.
1250 streamed: bool,
1251}
1252
1253impl Default for DCtx<'_> {
1254 fn default() -> Self {
1255 DCtx::create()
1256 }
1257}
1258
1259impl<'a> DCtx<'a> {
1260 /// Try to create a new decompression context.
1261 ///
1262 /// Returns `None` if the operation failed (for example, not enough memory).
1263 ///
1264 /// With the `with-rust-allocator` feature enabled, the context's internal
1265 /// allocations go through Rust's global allocator instead of the C
1266 /// runtime's `malloc`.
1267 pub fn try_create() -> Option<Self> {
1268 #[cfg(feature = "with-rust-allocator")]
1269 // Safety: the callbacks satisfy zstd's allocation contract and remain
1270 // valid for the lifetime of the context.
1271 let context = unsafe {
1272 zstd_sys::ZSTD_createDCtx_advanced(
1273 crate::rust_allocator::RUST_GLOBAL_ALLOCATOR,
1274 )
1275 };
1276 #[cfg(not(feature = "with-rust-allocator"))]
1277 // Safety: zstd returns a newly owned context or null.
1278 let context = unsafe { zstd_sys::ZSTD_createDCtx() };
1279
1280 Some(Self(
1281 NonNull::new(context)?,
1282 PhantomData,
1283 Poison::default(),
1284 DState::default(),
1285 ))
1286 }
1287
1288 /// Creates a new decoding context.
1289 ///
1290 /// # Panics
1291 ///
1292 /// If the context creation fails.
1293 pub fn create() -> Self {
1294 Self::try_create()
1295 .expect("zstd returned null pointer when creating new context")
1296 }
1297
1298 /// Ends the session of a one-shot decompression, and passes `res` through.
1299 ///
1300 /// zstd.h has these calls imply a reset, but they leave the stream stage
1301 /// alone. A stream they interrupted would then carry on from a state that
1302 /// still points at buffers and dictionaries borrowed for that one call. So
1303 /// reset explicitly: the next stream starts from scratch.
1304 ///
1305 /// Only an open stream can carry on, though: a new one starts by setting
1306 /// everything up again from the context's own dictionary. So when no
1307 /// streaming call ran since the last reset, there is nothing to end, and
1308 /// skipping the reset saves a call per frame - which shows on tiny ones.
1309 ///
1310 /// Only the check is inlined: it runs once per frame.
1311 #[inline]
1312 fn finish_one_shot(&mut self, res: SafeResult) -> SafeResult {
1313 if self.3.streamed || self.2.guard().is_err() {
1314 self.reset_after_one_shot(res)
1315 } else {
1316 res
1317 }
1318 }
1319
1320 #[cold]
1321 #[inline(never)]
1322 fn reset_after_one_shot(&mut self, res: SafeResult) -> SafeResult {
1323 self.reset(ResetDirective::SessionOnly).and(res)
1324 }
1325
1326 /// Fully decompress the given frame.
1327 ///
1328 /// This decompress an entire frame in-memory. If you can have enough memory to store both the
1329 /// input and output buffer, then it may be faster that streaming decompression.
1330 ///
1331 /// Wraps the `ZSTD_decompressDCtx()` function.
1332 pub fn decompress<C: WriteBuf + ?Sized>(
1333 &mut self,
1334 dst: &mut C,
1335 src: &[u8],
1336 ) -> SafeResult {
1337 let res = unsafe {
1338 dst.write_from(|buffer, capacity| {
1339 parse_code(zstd_sys::ZSTD_decompressDCtx(
1340 self.0.as_ptr(),
1341 buffer,
1342 capacity,
1343 ptr_void(src),
1344 src.len(),
1345 ))
1346 })
1347 };
1348 self.finish_one_shot(res)
1349 }
1350
1351 /// Fully decompress the given frame using a dictionary.
1352 ///
1353 /// Dictionary must be identical to the one used during compression.
1354 ///
1355 /// If you plan on using the same dictionary multiple times, it is faster to create a `DDict`
1356 /// first and use `decompress_using_ddict`.
1357 ///
1358 /// Wraps `ZSTD_decompress_usingDict`
1359 pub fn decompress_using_dict<C: WriteBuf + ?Sized>(
1360 &mut self,
1361 dst: &mut C,
1362 src: &[u8],
1363 dict: &[u8],
1364 ) -> SafeResult {
1365 let res = unsafe {
1366 dst.write_from(|buffer, capacity| {
1367 parse_code(zstd_sys::ZSTD_decompress_usingDict(
1368 self.0.as_ptr(),
1369 buffer,
1370 capacity,
1371 ptr_void(src),
1372 src.len(),
1373 ptr_void(dict),
1374 dict.len(),
1375 ))
1376 })
1377 };
1378 self.finish_one_shot(res)
1379 }
1380
1381 /// Fully decompress the given frame using a dictionary.
1382 ///
1383 /// Dictionary must be identical to the one used during compression.
1384 ///
1385 /// Wraps the `ZSTD_decompress_usingDDict()` function.
1386 pub fn decompress_using_ddict<C: WriteBuf + ?Sized>(
1387 &mut self,
1388 dst: &mut C,
1389 src: &[u8],
1390 ddict: &DDict<'_>,
1391 ) -> SafeResult {
1392 let res = unsafe {
1393 dst.write_from(|buffer, capacity| {
1394 parse_code(zstd_sys::ZSTD_decompress_usingDDict(
1395 self.0.as_ptr(),
1396 buffer,
1397 capacity,
1398 ptr_void(src),
1399 src.len(),
1400 ddict.0.as_ptr(),
1401 ))
1402 })
1403 };
1404 self.finish_one_shot(res)
1405 }
1406
1407 /// Initializes an existing `DStream` for decompression.
1408 ///
1409 /// This is equivalent to calling:
1410 /// * `reset(SessionOnly)`
1411 /// * `disable_dictionary()`
1412 ///
1413 /// Wraps the `ZSTD_initCStream()` function.
1414 pub fn init(&mut self) -> SafeResult {
1415 self.2.clear();
1416 let code = unsafe { zstd_sys::ZSTD_initDStream(self.0.as_ptr()) };
1417 self.2.record(parse_code(code))
1418 }
1419
1420 /// Wraps the `ZSTD_initDStream_usingDict()` function.
1421 #[cfg(feature = "experimental")]
1422 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1423 #[deprecated(note = "Use reset and load_dictionary instead.")]
1424 pub fn init_using_dict(&mut self, dict: &[u8]) -> SafeResult {
1425 self.2.clear();
1426 self.2.clear();
1427 let code = unsafe {
1428 zstd_sys::ZSTD_initDStream_usingDict(
1429 self.0.as_ptr(),
1430 ptr_void(dict),
1431 dict.len(),
1432 )
1433 };
1434 parse_code(code)
1435 }
1436
1437 /// Wraps the `ZSTD_initDStream_usingDDict()` function.
1438 ///
1439 /// Dictionary must outlive the context.
1440 ///
1441 /// ```compile_fail,E0515
1442 /// # #![allow(deprecated)]
1443 /// fn decompressor() -> zstd_safe::DCtx<'static> {
1444 /// let ddict = zstd_safe::DDict::create(b"dictionary");
1445 /// let mut dctx = zstd_safe::DCtx::create();
1446 /// dctx.init_using_ddict(&ddict).unwrap();
1447 /// dctx // `ddict` would be dropped while `dctx` still points at it.
1448 /// }
1449 /// ```
1450 #[cfg(feature = "experimental")]
1451 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1452 #[deprecated(note = "Use reset and ref_ddict instead.")]
1453 pub fn init_using_ddict<'b>(&mut self, ddict: &'a DDict<'b>) -> SafeResult
1454 where
1455 'b: 'a,
1456 {
1457 let code = unsafe {
1458 zstd_sys::ZSTD_initDStream_usingDDict(
1459 self.0.as_ptr(),
1460 ddict.0.as_ptr(),
1461 )
1462 };
1463 parse_code(code)
1464 }
1465
1466 /// Resets the state of the context.
1467 ///
1468 /// Depending on the reset mode, it can reset the session, the parameters, or both.
1469 ///
1470 /// Wraps the `ZSTD_DCtx_reset()` function.
1471 pub fn reset(&mut self, reset: ResetDirective) -> SafeResult {
1472 let res = parse_code(unsafe {
1473 zstd_sys::ZSTD_DCtx_reset(self.0.as_ptr(), reset.as_sys())
1474 });
1475 if res.is_ok() && reset.resets_session() {
1476 self.2.clear();
1477 self.3.streamed = false;
1478 }
1479 res
1480 }
1481
1482 /// Loads a dictionary.
1483 ///
1484 /// This will let this context decompress frames that were compressed using this dictionary.
1485 ///
1486 /// The dictionary content will be copied internally and does not need to be kept alive after
1487 /// calling this function.
1488 ///
1489 /// If you need to use the same dictionary for multiple contexts, it may be more efficient to
1490 /// create a `DDict` first, then loads that.
1491 ///
1492 /// The dictionary will apply to all future frames, until a new dictionary is set.
1493 pub fn load_dictionary(&mut self, dict: &[u8]) -> SafeResult {
1494 parse_code(unsafe {
1495 zstd_sys::ZSTD_DCtx_loadDictionary(
1496 self.0.as_ptr(),
1497 ptr_void(dict),
1498 dict.len(),
1499 )
1500 })
1501 }
1502
1503 /// Return to "no-dictionary" mode.
1504 ///
1505 /// This will disable any dictionary/prefix previously registered for future frames.
1506 pub fn disable_dictionary(&mut self) -> SafeResult {
1507 parse_code(unsafe {
1508 zstd_sys::ZSTD_DCtx_loadDictionary(
1509 self.0.as_ptr(),
1510 core::ptr::null(),
1511 0,
1512 )
1513 })
1514 }
1515
1516 /// References a dictionary.
1517 ///
1518 /// This will let this context decompress frames compressed with the same dictionary.
1519 ///
1520 /// It will apply to all frames decompressed by this context (until a new dictionary is set).
1521 ///
1522 /// Wraps the `ZSTD_DCtx_refDDict()` function.
1523 ///
1524 /// Dictionary must outlive the context.
1525 pub fn ref_ddict<'b>(&mut self, ddict: &'a DDict<'b>) -> SafeResult
1526 where
1527 'b: 'a,
1528 {
1529 parse_code(unsafe {
1530 zstd_sys::ZSTD_DCtx_refDDict(self.0.as_ptr(), ddict.0.as_ptr())
1531 })
1532 }
1533
1534 /// Use some prefix as single-use dictionary for the next frame.
1535 ///
1536 /// Just like a dictionary, this only works if compression was done with the same prefix.
1537 ///
1538 /// But unlike a dictionary, this only applies to the next frame.
1539 ///
1540 /// Wraps the `ZSTD_DCtx_refPrefix()` function.
1541 pub fn ref_prefix<'b>(&mut self, prefix: &'b [u8]) -> SafeResult
1542 where
1543 'b: 'a,
1544 {
1545 parse_code(unsafe {
1546 zstd_sys::ZSTD_DCtx_refPrefix(
1547 self.0.as_ptr(),
1548 ptr_void(prefix),
1549 prefix.len(),
1550 )
1551 })
1552 }
1553
1554 /// Sets a decompression parameter.
1555 pub fn set_parameter(&mut self, param: DParameter) -> SafeResult {
1556 #[cfg(feature = "experimental")]
1557 use zstd_sys::ZSTD_dParameter::{
1558 ZSTD_d_experimentalParam1 as ZSTD_d_format,
1559 ZSTD_d_experimentalParam2 as ZSTD_d_stableOutBuffer,
1560 ZSTD_d_experimentalParam3 as ZSTD_d_forceIgnoreChecksum,
1561 ZSTD_d_experimentalParam4 as ZSTD_d_refMultipleDDicts,
1562 };
1563
1564 use zstd_sys::ZSTD_dParameter::*;
1565 use DParameter::*;
1566
1567 let (param, value) = match param {
1568 #[cfg(feature = "experimental")]
1569 Format(format) => (ZSTD_d_format, format as c_int),
1570 #[cfg(feature = "experimental")]
1571 StableOutBuffer(stable) => {
1572 (ZSTD_d_stableOutBuffer, stable as c_int)
1573 }
1574 #[cfg(feature = "experimental")]
1575 ForceIgnoreChecksum(force) => {
1576 (ZSTD_d_forceIgnoreChecksum, force as c_int)
1577 }
1578 #[cfg(feature = "experimental")]
1579 RefMultipleDDicts(value) => {
1580 // From now on `ref_ddict` may allocate the table of
1581 // dictionaries, which zstd keeps until the context is freed.
1582 self.3.multiple_ddicts |= value;
1583 (ZSTD_d_refMultipleDDicts, value as c_int)
1584 }
1585
1586 WindowLogMax(value) => (ZSTD_d_windowLogMax, value as c_int),
1587 };
1588
1589 parse_code(unsafe {
1590 zstd_sys::ZSTD_DCtx_setParameter(self.0.as_ptr(), param, value)
1591 })
1592 }
1593
1594 /// Performs a step of a streaming decompression operation.
1595 ///
1596 /// This will read some data from `input` and/or write some data to `output`.
1597 ///
1598 /// # Returns
1599 ///
1600 /// * `Ok(0)` if the current frame just finished decompressing successfully.
1601 /// * `Ok(hint)` with a hint for the "ideal" amount of input data to provide in the next call.
1602 /// Can be safely ignored.
1603 ///
1604 /// Wraps the `ZSTD_decompressStream()` function.
1605 pub fn decompress_stream<C: WriteBuf + ?Sized>(
1606 &mut self,
1607 output: &mut OutBuffer<'_, C>,
1608 input: &mut InBuffer<'_>,
1609 ) -> SafeResult {
1610 self.2.guard()?;
1611 self.3.streamed = true;
1612 let mut output = output.wrap();
1613 let mut input = input.wrap();
1614 let code = unsafe {
1615 zstd_sys::ZSTD_decompressStream(
1616 self.0.as_ptr(),
1617 ptr_mut(&mut output),
1618 ptr_mut(&mut input),
1619 )
1620 };
1621 let res = output.check(parse_code(code));
1622 self.2.record(input.check(res))
1623 }
1624
1625 /// Wraps the `ZSTD_DStreamInSize()` function.
1626 ///
1627 /// Returns a hint for the recommended size of the input buffer for decompression.
1628 pub fn in_size() -> usize {
1629 unsafe { zstd_sys::ZSTD_DStreamInSize() }
1630 }
1631
1632 /// Wraps the `ZSTD_DStreamOutSize()` function.
1633 ///
1634 /// Returns a hint for the recommended size of the output buffer for decompression.
1635 pub fn out_size() -> usize {
1636 unsafe { zstd_sys::ZSTD_DStreamOutSize() }
1637 }
1638
1639 /// Wraps the `ZSTD_sizeof_DCtx()` function.
1640 pub fn sizeof(&self) -> usize {
1641 unsafe { zstd_sys::ZSTD_sizeof_DCtx(self.0.as_ptr()) }
1642 }
1643
1644 /// Wraps the `ZSTD_decompressBlock()` function.
1645 ///
1646 /// # Safety
1647 ///
1648 /// The bytes written to `dst` become this context's history window, so `dst` must stay
1649 /// allocated and unmodified until the next call to `decompress_block` or `insert_block` on this
1650 /// context (or until this context is dropped), as the following block is decoded against it.
1651 #[cfg(feature = "experimental")]
1652 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1653 #[deprecated(
1654 note = "The block API is deprecated; use decompress_stream instead."
1655 )]
1656 pub unsafe fn decompress_block<C: WriteBuf + ?Sized>(
1657 &mut self,
1658 dst: &mut C,
1659 src: &[u8],
1660 ) -> SafeResult {
1661 self.3.streamed = true;
1662 unsafe {
1663 dst.write_from(|buffer, capacity| {
1664 parse_code(zstd_sys::ZSTD_decompressBlock(
1665 self.0.as_ptr(),
1666 buffer,
1667 capacity,
1668 ptr_void(src),
1669 src.len(),
1670 ))
1671 })
1672 }
1673 }
1674
1675 /// Wraps the `ZSTD_insertBlock()` function.
1676 ///
1677 /// # Safety
1678 ///
1679 /// `block` becomes this context's history window, so it must stay allocated and unmodified
1680 /// until the next call to `decompress_block` or `insert_block` on this context (or until this
1681 /// context is dropped), as the following block is decoded against it.
1682 #[cfg(feature = "experimental")]
1683 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1684 #[deprecated(
1685 note = "The block API is deprecated; use decompress_stream instead."
1686 )]
1687 pub unsafe fn insert_block(&mut self, block: &[u8]) -> usize {
1688 self.3.streamed = true;
1689 unsafe {
1690 zstd_sys::ZSTD_insertBlock(
1691 self.0.as_ptr(),
1692 ptr_void(block),
1693 block.len(),
1694 )
1695 }
1696 }
1697
1698 /// Creates a copy of this context.
1699 ///
1700 /// This only works before any data has been decompressed. An error will be
1701 /// returned otherwise.
1702 /// A poisoned context cannot be cloned until its session is reset.
1703 /// The copy always starts at the beginning of a frame.
1704 ///
1705 /// A dictionary this context merely references (see [`DCtx::ref_ddict`])
1706 /// is referenced by the copy too. One it owns cannot be shared, so an
1707 /// error is also returned while a dictionary from
1708 /// [`DCtx::load_dictionary`] or a prefix from [`DCtx::ref_prefix`] is set,
1709 /// and once [`DParameter::RefMultipleDDicts`] has been enabled.
1710 #[cfg(feature = "experimental")]
1711 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1712 #[deprecated(
1713 note = "Context copying is deprecated by zstd; create a new context and configure it instead."
1714 )]
1715 pub fn try_clone(&self) -> Result<Self, ErrorCode> {
1716 use zstd_sys::ZSTD_dParameter::{
1717 ZSTD_d_experimentalParam2 as ZSTD_d_stableOutBuffer,
1718 ZSTD_d_windowLogMax,
1719 };
1720
1721 // An error may leave the C context undefined; do not copy it.
1722 self.2.guard()?;
1723 // zstd copies the allocator callbacks from `self` into the new
1724 // context, so both must be created the same way; `try_create` always
1725 // picks the same allocator within a build.
1726 let mut context = Self::try_create().ok_or(MEMORY_ALLOCATION_ERROR)?;
1727
1728 // `ZSTD_copyDCtx()` is a plain `memcpy`, so a dictionary `self` owns
1729 // would end up owned, and freed, by both contexts. A loaded dictionary
1730 // or prefix shows up as memory a new context does not use - as do the
1731 // streaming buffers, once data has been decompressed. The table behind
1732 // `RefMultipleDDicts` is not counted there, and outlives the
1733 // parameter, hence the flag.
1734 if self.3.multiple_ddicts || self.sizeof() != context.sizeof() {
1735 return Err(STAGE_WRONG_ERROR);
1736 }
1737
1738 // `ZSTD_copyDCtx` stops where the streaming buffers start and leaves
1739 // the parameters stored past that point (the window limit and the
1740 // output buffer mode) at their defaults. Carry them over explicitly,
1741 // on the fresh context so that it is still in its initial stage; the
1742 // copy below does not touch these fields.
1743 // Only for values that differ: the window limit reads back as a log,
1744 // which rounds zstd's default limit down.
1745 let get = |dctx: &Self, param| {
1746 let mut value: c_int = 0;
1747 // Safety: Just FFI
1748 parse_code(unsafe {
1749 zstd_sys::ZSTD_DCtx_getParameter(
1750 dctx.0.as_ptr(),
1751 param,
1752 &mut value,
1753 )
1754 })
1755 .map(|_| value)
1756 };
1757 for param in [ZSTD_d_windowLogMax, ZSTD_d_stableOutBuffer] {
1758 let value = get(self, param)?;
1759 if value == get(&context, param)? {
1760 continue;
1761 }
1762 // Safety: Just FFI
1763 parse_code(unsafe {
1764 zstd_sys::ZSTD_DCtx_setParameter(
1765 context.0.as_ptr(),
1766 param,
1767 value,
1768 )
1769 })?;
1770 }
1771
1772 // Safety: Just FFI
1773 unsafe {
1774 zstd_sys::ZSTD_copyDCtx(context.0.as_ptr(), self.0.as_ptr())
1775 };
1776
1777 // The position within a frame is copied, but not the state that goes
1778 // with it (a partial header, a legacy context), so start from a frame
1779 // boundary rather than run on what the new context never initialized.
1780 context.reset(ResetDirective::SessionOnly)?;
1781
1782 Ok(context)
1783 }
1784}
1785
1786impl Drop for DCtx<'_> {
1787 fn drop(&mut self) {
1788 unsafe {
1789 zstd_sys::ZSTD_freeDCtx(self.0.as_ptr());
1790 }
1791 }
1792}
1793
1794// Safety: as for `CCtx` - an owned heap allocation with no thread-local state.
1795unsafe impl Send for DCtx<'_> {}
1796// Non thread-safe methods already take `&mut self`, so it's fine to implement Sync here.
1797// Safety: as for `CCtx` - the mutating methods all take `&mut self`.
1798unsafe impl Sync for DCtx<'_> {}
1799
1800/// Compression dictionary.
1801///
1802/// With `with-rust-allocator`, prepared dictionaries retain fixed compression
1803/// parameters rather than adapting them to later input sizes. This can change
1804/// compression ratio, speed, and memory usage compared with the C allocator.
1805pub struct CDict<'a>(NonNull<zstd_sys::ZSTD_CDict>, PhantomData<&'a ()>);
1806
1807impl CDict<'static> {
1808 /// Prepare a dictionary to compress data.
1809 ///
1810 /// This will make it easier for compression contexts to load this dictionary.
1811 ///
1812 /// The dictionary content will be copied internally, and does not need to be kept around.
1813 ///
1814 /// # Panics
1815 ///
1816 /// If loading this dictionary failed.
1817 pub fn create(
1818 dict_buffer: &[u8],
1819 compression_level: CompressionLevel,
1820 ) -> Self {
1821 Self::try_create(dict_buffer, compression_level)
1822 .expect("zstd returned null pointer when creating dict")
1823 }
1824
1825 /// Prepare a dictionary to compress data.
1826 ///
1827 /// This will make it easier for compression contexts to load this dictionary.
1828 ///
1829 /// The dictionary content will be copied internally, and does not need to be kept around.
1830 pub fn try_create(
1831 dict_buffer: &[u8],
1832 compression_level: CompressionLevel,
1833 ) -> Option<Self> {
1834 #[cfg(feature = "with-rust-allocator")]
1835 let ptr = unsafe {
1836 rust_allocator::create_cdict(
1837 dict_buffer,
1838 compression_level,
1839 zstd_sys::ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
1840 )
1841 };
1842 #[cfg(not(feature = "with-rust-allocator"))]
1843 let ptr = unsafe {
1844 zstd_sys::ZSTD_createCDict(
1845 ptr_void(dict_buffer),
1846 dict_buffer.len(),
1847 compression_level,
1848 )
1849 };
1850 Some(CDict(NonNull::new(ptr)?, PhantomData))
1851 }
1852}
1853
1854impl<'a> CDict<'a> {
1855 /// Prepare a dictionary to compress data, referencing `dict_buffer`.
1856 ///
1857 /// # Panics
1858 ///
1859 /// If loading this dictionary failed. See [`CDict::try_create_by_reference`].
1860 #[cfg(feature = "experimental")]
1861 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1862 pub fn create_by_reference(
1863 dict_buffer: &'a [u8],
1864 compression_level: CompressionLevel,
1865 ) -> Self {
1866 Self::try_create_by_reference(dict_buffer, compression_level)
1867 .expect("zstd returned null pointer")
1868 }
1869
1870 /// Prepare a dictionary to compress data, referencing `dict_buffer`.
1871 ///
1872 /// Returns `None` if zstd could not load the dictionary, for instance
1873 /// because it starts with the dictionary magic number but its header is
1874 /// corrupt.
1875 #[cfg(feature = "experimental")]
1876 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
1877 pub fn try_create_by_reference(
1878 dict_buffer: &'a [u8],
1879 compression_level: CompressionLevel,
1880 ) -> Option<Self> {
1881 #[cfg(feature = "with-rust-allocator")]
1882 let ptr = unsafe {
1883 rust_allocator::create_cdict(
1884 dict_buffer,
1885 compression_level,
1886 zstd_sys::ZSTD_dictLoadMethod_e::ZSTD_dlm_byRef,
1887 )
1888 };
1889 #[cfg(not(feature = "with-rust-allocator"))]
1890 let ptr = unsafe {
1891 zstd_sys::ZSTD_createCDict_byReference(
1892 ptr_void(dict_buffer),
1893 dict_buffer.len(),
1894 compression_level,
1895 )
1896 };
1897 Some(CDict(NonNull::new(ptr)?, PhantomData))
1898 }
1899
1900 /// Returns the _current_ memory usage of this dictionary.
1901 ///
1902 /// Note that this may change over time.
1903 pub fn sizeof(&self) -> usize {
1904 unsafe { zstd_sys::ZSTD_sizeof_CDict(self.0.as_ptr()) }
1905 }
1906
1907 /// Returns the dictionary ID for this dict.
1908 ///
1909 /// Returns `None` if this dictionary is empty or invalid.
1910 pub fn get_dict_id(&self) -> Option<NonZeroU32> {
1911 NonZeroU32::new(unsafe {
1912 zstd_sys::ZSTD_getDictID_fromCDict(self.0.as_ptr()) as u32
1913 })
1914 }
1915}
1916
1917/// Wraps the `ZSTD_createCDict()` function.
1918pub fn create_cdict(
1919 dict_buffer: &[u8],
1920 compression_level: CompressionLevel,
1921) -> CDict<'static> {
1922 CDict::create(dict_buffer, compression_level)
1923}
1924
1925impl<'a> Drop for CDict<'a> {
1926 fn drop(&mut self) {
1927 unsafe {
1928 zstd_sys::ZSTD_freeCDict(self.0.as_ptr());
1929 }
1930 }
1931}
1932
1933// Safety: a digested dictionary is immutable once built. zstd.h: "ZSTD_CDict
1934// can be created once and shared by multiple threads concurrently, since its
1935// usage is read-only".
1936unsafe impl<'a> Send for CDict<'a> {}
1937unsafe impl<'a> Sync for CDict<'a> {}
1938
1939/// Wraps the `ZSTD_compress_usingCDict()` function.
1940pub fn compress_using_cdict(
1941 cctx: &mut CCtx<'_>,
1942 dst: &mut [u8],
1943 src: &[u8],
1944 cdict: &CDict<'_>,
1945) -> SafeResult {
1946 cctx.compress_using_cdict(dst, src, cdict)
1947}
1948
1949/// A digested decompression dictionary.
1950pub struct DDict<'a>(NonNull<zstd_sys::ZSTD_DDict>, PhantomData<&'a ()>);
1951
1952impl DDict<'static> {
1953 /// Prepare a dictionary to decompress data.
1954 ///
1955 /// The dictionary content will be copied internally, and does not need to be kept around.
1956 ///
1957 /// # Panics
1958 ///
1959 /// If loading this dictionary failed. See [`DDict::try_create`].
1960 pub fn create(dict_buffer: &[u8]) -> Self {
1961 Self::try_create(dict_buffer)
1962 .expect("zstd returned null pointer when creating dict")
1963 }
1964
1965 /// Prepare a dictionary to decompress data.
1966 ///
1967 /// The dictionary content will be copied internally, and does not need to be kept around.
1968 /// Returns `None` if zstd could not load the dictionary, for instance because it starts
1969 /// with the dictionary magic number but its header is corrupt.
1970 pub fn try_create(dict_buffer: &[u8]) -> Option<Self> {
1971 #[cfg(feature = "with-rust-allocator")]
1972 let ptr = unsafe {
1973 rust_allocator::create_ddict(
1974 dict_buffer,
1975 zstd_sys::ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
1976 )
1977 };
1978 #[cfg(not(feature = "with-rust-allocator"))]
1979 let ptr = unsafe {
1980 zstd_sys::ZSTD_createDDict(
1981 ptr_void(dict_buffer),
1982 dict_buffer.len(),
1983 )
1984 };
1985 Some(DDict(NonNull::new(ptr)?, PhantomData))
1986 }
1987}
1988
1989impl<'a> DDict<'a> {
1990 pub fn sizeof(&self) -> usize {
1991 unsafe { zstd_sys::ZSTD_sizeof_DDict(self.0.as_ptr()) }
1992 }
1993
1994 /// Wraps the `ZSTD_createDDict_byReference()` function.
1995 ///
1996 /// The dictionary will keep referencing `dict_buffer`.
1997 ///
1998 /// # Panics
1999 ///
2000 /// If loading this dictionary failed. See [`DDict::try_create_by_reference`].
2001 #[cfg(feature = "experimental")]
2002 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2003 pub fn create_by_reference(dict_buffer: &'a [u8]) -> Self {
2004 Self::try_create_by_reference(dict_buffer)
2005 .expect("zstd returned null pointer")
2006 }
2007
2008 /// Wraps the `ZSTD_createDDict_byReference()` function.
2009 ///
2010 /// The dictionary will keep referencing `dict_buffer`. Returns `None` if
2011 /// zstd could not load the dictionary, for instance because it starts with
2012 /// the dictionary magic number but its header is corrupt.
2013 #[cfg(feature = "experimental")]
2014 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2015 pub fn try_create_by_reference(dict_buffer: &'a [u8]) -> Option<Self> {
2016 #[cfg(feature = "with-rust-allocator")]
2017 let ptr = unsafe {
2018 rust_allocator::create_ddict(
2019 dict_buffer,
2020 zstd_sys::ZSTD_dictLoadMethod_e::ZSTD_dlm_byRef,
2021 )
2022 };
2023 #[cfg(not(feature = "with-rust-allocator"))]
2024 let ptr = unsafe {
2025 zstd_sys::ZSTD_createDDict_byReference(
2026 ptr_void(dict_buffer),
2027 dict_buffer.len(),
2028 )
2029 };
2030 Some(DDict(NonNull::new(ptr)?, PhantomData))
2031 }
2032
2033 /// Returns the dictionary ID for this dict.
2034 ///
2035 /// Returns `None` if this dictionary is empty or invalid.
2036 pub fn get_dict_id(&self) -> Option<NonZeroU32> {
2037 NonZeroU32::new(unsafe {
2038 zstd_sys::ZSTD_getDictID_fromDDict(self.0.as_ptr()) as u32
2039 })
2040 }
2041}
2042
2043/// Wraps the `ZSTD_createDDict()` function.
2044///
2045/// It copies the dictionary internally, so the resulting `DDict` is `'static`.
2046pub fn create_ddict(dict_buffer: &[u8]) -> DDict<'static> {
2047 DDict::create(dict_buffer)
2048}
2049
2050impl<'a> Drop for DDict<'a> {
2051 fn drop(&mut self) {
2052 unsafe {
2053 zstd_sys::ZSTD_freeDDict(self.0.as_ptr());
2054 }
2055 }
2056}
2057
2058// Safety: like `CDict`, a digested dictionary is only read once built - the
2059// contexts referencing it never write to it.
2060unsafe impl<'a> Send for DDict<'a> {}
2061unsafe impl<'a> Sync for DDict<'a> {}
2062
2063/// A shared thread pool for one or more compression contexts
2064#[cfg(all(feature = "experimental", feature = "zstdmt"))]
2065#[cfg_attr(
2066 feature = "doc-cfg",
2067 doc(cfg(all(feature = "experimental", feature = "zstdmt")))
2068)]
2069pub struct ThreadPool(NonNull<zstd_sys::ZSTD_threadPool>);
2070
2071#[cfg(all(feature = "experimental", feature = "zstdmt"))]
2072#[cfg_attr(
2073 feature = "doc-cfg",
2074 doc(cfg(all(feature = "experimental", feature = "zstdmt")))
2075)]
2076impl ThreadPool {
2077 /// Create a thread pool with the specified number of threads.
2078 ///
2079 /// # Panics
2080 ///
2081 /// If creating the thread pool failed.
2082 pub fn new(num_threads: usize) -> Self {
2083 Self::try_new(num_threads)
2084 .expect("zstd returned null pointer when creating thread pool")
2085 }
2086
2087 /// Create a thread pool with the specified number of threads.
2088 pub fn try_new(num_threads: usize) -> Option<Self> {
2089 Some(Self(NonNull::new(unsafe {
2090 zstd_sys::ZSTD_createThreadPool(num_threads)
2091 })?))
2092 }
2093}
2094
2095#[cfg(all(feature = "experimental", feature = "zstdmt"))]
2096#[cfg_attr(
2097 feature = "doc-cfg",
2098 doc(cfg(all(feature = "experimental", feature = "zstdmt")))
2099)]
2100impl Drop for ThreadPool {
2101 fn drop(&mut self) {
2102 unsafe {
2103 zstd_sys::ZSTD_freeThreadPool(self.0.as_ptr());
2104 }
2105 }
2106}
2107
2108#[cfg(all(feature = "experimental", feature = "zstdmt"))]
2109#[cfg_attr(
2110 feature = "doc-cfg",
2111 doc(cfg(all(feature = "experimental", feature = "zstdmt")))
2112)]
2113// Safety: the pool owns its worker threads and the queue guarding them; none
2114// of that is tied to the thread that created it.
2115unsafe impl Send for ThreadPool {}
2116#[cfg(all(feature = "experimental", feature = "zstdmt"))]
2117#[cfg_attr(
2118 feature = "doc-cfg",
2119 doc(cfg(all(feature = "experimental", feature = "zstdmt")))
2120)]
2121// Safety: sharing is what the pool is for - zstd.h offers these functions to
2122// "share a thread pool among multiple compression contexts" - and its work
2123// queue is guarded by an internal mutex.
2124unsafe impl Sync for ThreadPool {}
2125
2126/// Wraps the `ZSTD_decompress_usingDDict()` function.
2127pub fn decompress_using_ddict(
2128 dctx: &mut DCtx<'_>,
2129 dst: &mut [u8],
2130 src: &[u8],
2131 ddict: &DDict<'_>,
2132) -> SafeResult {
2133 dctx.decompress_using_ddict(dst, src, ddict)
2134}
2135
2136/// Compression stream.
2137///
2138/// Same as `CCtx`.
2139pub type CStream<'a> = CCtx<'a>;
2140
2141// `CStream` is an alias for `CCtx`, and shares its `Send` and `Sync` impls.
2142
2143/// Allocates a new `CStream`.
2144pub fn create_cstream<'a>() -> CStream<'a> {
2145 CCtx::create()
2146}
2147
2148/// Prepares an existing `CStream` for compression at the given level.
2149pub fn init_cstream(
2150 zcs: &mut CStream<'_>,
2151 compression_level: CompressionLevel,
2152) -> SafeResult {
2153 zcs.init(compression_level)
2154}
2155
2156#[derive(Debug)]
2157/// Wrapper around an input buffer.
2158///
2159/// Bytes will be read starting at `src[pos]`.
2160///
2161/// `pos` will be updated after reading.
2162///
2163/// # Invariant
2164///
2165/// `pos <= src.len()`
2166///
2167/// Both fields are public, so this is checked (and panics if it does not hold)
2168/// whenever the buffer is given to zstd.
2169pub struct InBuffer<'a> {
2170 pub src: &'a [u8],
2171 pub pos: usize,
2172}
2173
2174/// Describe a bytes container, like `Vec<u8>`.
2175///
2176/// Represents a contiguous segment of allocated memory, a prefix of which is initialized.
2177///
2178/// It allows starting from an uninitializes chunk of memory and writing to it, progressively
2179/// initializing it. No re-allocation typically occur after the initial creation.
2180///
2181/// The main implementors are:
2182/// * `Vec<u8>` and similar structures. These hold both a length (initialized data) and a capacity
2183/// (allocated memory).
2184///
2185/// Use `Vec::with_capacity` to create an empty `Vec` with non-zero capacity, and the length
2186/// field will be updated to cover the data written to it (as long as it fits in the given
2187/// capacity).
2188/// * `[u8]` and `[u8; N]`. These must start already-initialized, and will not be resized. It will
2189/// be up to the caller to only use the part that was written (as returned by the various writing
2190/// operations).
2191/// * `std::io::Cursor<T: WriteBuf>`. This will ignore data before the cursor's position, and
2192/// append data after that.
2193///
2194/// # Safety
2195///
2196/// An implementation has to keep three promises, because the zstd functions
2197/// this is handed to write straight through the pointer:
2198///
2199/// * `as_mut_ptr()` points at a buffer that is valid to write `capacity()`
2200/// bytes to.
2201/// * `as_slice()` covers only initialized bytes - never any of the capacity
2202/// that has not been written yet.
2203/// * `filled_until(n)` is what marks the first `n` bytes as initialized, so
2204/// after it returns, `as_slice()` must cover them.
2205pub unsafe trait WriteBuf {
2206 /// Returns the valid data part of this container. Should only cover initialized data.
2207 fn as_slice(&self) -> &[u8];
2208
2209 /// Returns the full capacity of this container. May include uninitialized data.
2210 fn capacity(&self) -> usize;
2211
2212 /// Returns a pointer to the start of the data.
2213 fn as_mut_ptr(&mut self) -> *mut u8;
2214
2215 /// Indicates that the first `n` bytes of the container have been written.
2216 ///
2217 /// # Safety
2218 ///
2219 /// The first `n` bytes of this buffer must actually have been initialized.
2220 unsafe fn filled_until(&mut self, n: usize);
2221
2222 /// Call the given closure using the pointer and capacity from `self`.
2223 ///
2224 /// The closure returns a parseable code which, if valid, is how many bytes
2225 /// it wrote to `self`.
2226 ///
2227 /// # Safety
2228 ///
2229 /// The closure must treat its first argument as pointing to potentially
2230 /// uninitialized memory, and must not read from it. It must have written
2231 /// at least `n` bytes contiguously from that pointer, where `n` is the
2232 /// value it returns.
2233 unsafe fn write_from<F>(&mut self, f: F) -> SafeResult
2234 where
2235 F: FnOnce(*mut c_void, usize) -> SafeResult,
2236 {
2237 let capacity = self.capacity();
2238 let res = f(ptr_mut_void(self), capacity);
2239 if let Ok(n) = res {
2240 // Nothing can have been written past the end of the buffer; a
2241 // larger count would mark memory we do not own as initialized.
2242 assert!(n <= capacity, "zstd reported writing past the buffer");
2243 self.filled_until(n);
2244 }
2245 res
2246 }
2247}
2248
2249/// The position of a `Cursor`, as an index into the buffer it wraps.
2250///
2251/// `Cursor` stores the position as a `u64` and lets it be set anywhere, so on a
2252/// target where `usize` is narrower it can name an offset no buffer can hold.
2253/// Saturating leaves such a position out of range, where the bounds checks
2254/// below reject it; casting would wrap it around into a valid-looking offset
2255/// and quietly read or write the wrong part of the buffer.
2256#[cfg(feature = "std")]
2257fn cursor_position<T>(cursor: &std::io::Cursor<T>) -> usize {
2258 use core::convert::TryFrom;
2259
2260 usize::try_from(cursor.position()).unwrap_or(usize::MAX)
2261}
2262
2263#[cfg(feature = "std")]
2264#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "std")))]
2265unsafe impl<T> WriteBuf for std::io::Cursor<T>
2266where
2267 T: WriteBuf,
2268{
2269 fn as_slice(&self) -> &[u8] {
2270 // A cursor past the inner buffer's initialized part has nothing
2271 // initialized after it yet (see `filled_until` below).
2272 self.get_ref()
2273 .as_slice()
2274 .get(cursor_position(self)..)
2275 .unwrap_or(&[])
2276 }
2277
2278 fn capacity(&self) -> usize {
2279 self.get_ref()
2280 .capacity()
2281 .saturating_sub(cursor_position(self))
2282 }
2283
2284 fn as_mut_ptr(&mut self) -> *mut u8 {
2285 let start = cursor_position(self);
2286 assert!(start <= self.get_ref().capacity());
2287 // Safety: start is still in the same memory allocation
2288 unsafe { self.get_mut().as_mut_ptr().add(start) }
2289 }
2290
2291 unsafe fn filled_until(&mut self, n: usize) {
2292 // Here we assume data _before_ self.position() was already initialized.
2293 // Egh it's not actually guaranteed by Cursor? So let's guarantee it ourselves.
2294 // Since the cursor wraps another `WriteBuf`, we know how much data is initialized there.
2295 let position = cursor_position(self);
2296 let capacity = self.get_ref().capacity();
2297 if n == 0 && position > capacity {
2298 // Nothing was written, and nothing could have been: there is
2299 // no room at all after the cursor.
2300 return;
2301 }
2302 // Otherwise the caller wrote `n` bytes starting at `position`, so
2303 // `position` is inside the buffer. Checking it before the zero-fill
2304 // below keeps a position that could not be converted from running off
2305 // the end.
2306 //
2307 // Note that `n == 0` still truncates the data after the cursor, like
2308 // any other `n` does: an empty result must not leave a previous one
2309 // in place.
2310 assert!(position <= capacity);
2311 let initialized = self.get_ref().as_slice().len();
2312 if let Some(uninitialized) = position.checked_sub(initialized) {
2313 // Here, the cursor is further than the known-initialized part.
2314 // Cursor's solution is to pad with zeroes, so let's do the same.
2315 // We'll zero bytes from the end of valid data (as_slice().len()) to the cursor position.
2316
2317 // Safety:
2318 // * We checked `position <= self.get_ref().capacity()`
2319 // * This means that `position` is within (or one past) the nested
2320 // pointer's allocation.
2321 // * Finally, `initialized + uninitialized = position`, so the entire byte
2322 // range here is within the allocation
2323 unsafe {
2324 self.get_mut()
2325 .as_mut_ptr()
2326 .add(initialized)
2327 .write_bytes(0u8, uninitialized)
2328 };
2329 }
2330
2331 let start = position;
2332 assert!(start + n <= self.get_ref().capacity());
2333 self.get_mut().filled_until(start + n);
2334 }
2335}
2336
2337#[cfg(feature = "std")]
2338#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "std")))]
2339unsafe impl WriteBuf for &mut std::vec::Vec<u8> {
2340 fn as_slice(&self) -> &[u8] {
2341 std::vec::Vec::as_slice(self)
2342 }
2343
2344 fn capacity(&self) -> usize {
2345 std::vec::Vec::capacity(self)
2346 }
2347
2348 fn as_mut_ptr(&mut self) -> *mut u8 {
2349 std::vec::Vec::as_mut_ptr(self)
2350 }
2351
2352 unsafe fn filled_until(&mut self, n: usize) {
2353 std::vec::Vec::set_len(self, n)
2354 }
2355}
2356
2357#[cfg(feature = "std")]
2358#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "std")))]
2359unsafe impl WriteBuf for std::vec::Vec<u8> {
2360 fn as_slice(&self) -> &[u8] {
2361 &self[..]
2362 }
2363 fn capacity(&self) -> usize {
2364 self.capacity()
2365 }
2366 fn as_mut_ptr(&mut self) -> *mut u8 {
2367 self.as_mut_ptr()
2368 }
2369 unsafe fn filled_until(&mut self, n: usize) {
2370 self.set_len(n);
2371 }
2372}
2373
2374#[cfg(feature = "arrays")]
2375#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "arrays")))]
2376unsafe impl<const N: usize> WriteBuf for [u8; N] {
2377 fn as_slice(&self) -> &[u8] {
2378 self
2379 }
2380 fn capacity(&self) -> usize {
2381 self.len()
2382 }
2383
2384 fn as_mut_ptr(&mut self) -> *mut u8 {
2385 self[..].as_mut_ptr()
2386 }
2387
2388 unsafe fn filled_until(&mut self, _n: usize) {
2389 // Assume the slice is already initialized
2390 }
2391}
2392
2393unsafe impl WriteBuf for [u8] {
2394 fn as_slice(&self) -> &[u8] {
2395 self
2396 }
2397 fn capacity(&self) -> usize {
2398 self.len()
2399 }
2400
2401 fn as_mut_ptr(&mut self) -> *mut u8 {
2402 self.as_mut_ptr()
2403 }
2404
2405 unsafe fn filled_until(&mut self, _n: usize) {
2406 // Assume the slice is already initialized
2407 }
2408}
2409
2410/*
2411// This is possible, but... why?
2412unsafe impl<'a> WriteBuf for OutBuffer<'a, [u8]> {
2413 fn as_slice(&self) -> &[u8] {
2414 self.dst
2415 }
2416 fn capacity(&self) -> usize {
2417 self.dst.len()
2418 }
2419 fn as_mut_ptr(&mut self) -> *mut u8 {
2420 self.dst.as_mut_ptr()
2421 }
2422 unsafe fn filled_until(&mut self, n: usize) {
2423 self.pos = n;
2424 }
2425}
2426*/
2427
2428#[derive(Debug)]
2429/// Wrapper around an output buffer.
2430///
2431/// `C` is usually either `[u8]` or `Vec<u8>`.
2432///
2433/// Bytes will be written starting at `dst[pos]`.
2434///
2435/// `pos` will be updated after writing.
2436///
2437/// # Invariant
2438///
2439/// `pos <= dst.capacity()`
2440pub struct OutBuffer<'a, C: WriteBuf + ?Sized> {
2441 dst: &'a mut C,
2442 pos: usize,
2443}
2444
2445/// Convenience method to get a mut pointer from a mut ref.
2446fn ptr_mut<B>(ptr_void: &mut B) -> *mut B {
2447 ptr_void as *mut B
2448}
2449
2450/// Interface between a C-level ZSTD_outBuffer and a rust-level `OutBuffer`.
2451///
2452/// Will update the parent buffer from the C buffer on drop.
2453struct OutBufferWrapper<'a, 'b, C: WriteBuf + ?Sized> {
2454 buf: zstd_sys::ZSTD_outBuffer,
2455 parent: &'a mut OutBuffer<'b, C>,
2456}
2457
2458impl<'a, 'b: 'a, C: WriteBuf + ?Sized> Deref for OutBufferWrapper<'a, 'b, C> {
2459 type Target = zstd_sys::ZSTD_outBuffer;
2460
2461 fn deref(&self) -> &Self::Target {
2462 &self.buf
2463 }
2464}
2465
2466impl<'a, 'b: 'a, C: WriteBuf + ?Sized> DerefMut
2467 for OutBufferWrapper<'a, 'b, C>
2468{
2469 fn deref_mut(&mut self) -> &mut Self::Target {
2470 &mut self.buf
2471 }
2472}
2473
2474impl<'a, C: WriteBuf + ?Sized> OutBuffer<'a, C> {
2475 /// Returns a new `OutBuffer` around the given slice.
2476 ///
2477 /// Starts with `pos = 0`.
2478 pub fn around(dst: &'a mut C) -> Self {
2479 OutBuffer { dst, pos: 0 }
2480 }
2481
2482 /// Returns a new `OutBuffer` around the given slice, starting at the given position.
2483 ///
2484 /// If `pos` is past the initialized part of `dst` (for example a `Vec`
2485 /// whose `len()` is smaller than `pos`), the gap is zero-filled, as a
2486 /// `Cursor` would do.
2487 ///
2488 /// # Panics
2489 ///
2490 /// If `pos > dst.capacity()`.
2491 pub fn around_pos(dst: &'a mut C, pos: usize) -> Self {
2492 if pos > dst.capacity() {
2493 panic!("Given position outside of the buffer bounds.");
2494 }
2495
2496 let initialized = dst.as_slice().len();
2497 if pos > initialized {
2498 // Writing to this buffer marks everything before `pos` as
2499 // initialized (see `set_pos`), so it has to be before we start.
2500 // Safety: `initialized..pos` lies within `capacity()`, which
2501 // `WriteBuf` promises is valid to write to.
2502 unsafe {
2503 dst.as_mut_ptr()
2504 .add(initialized)
2505 .write_bytes(0u8, pos - initialized);
2506 dst.filled_until(pos);
2507 }
2508 }
2509
2510 OutBuffer { dst, pos }
2511 }
2512
2513 /// Returns the current cursor position.
2514 ///
2515 /// Guaranteed to be <= self.capacity()
2516 pub fn pos(&self) -> usize {
2517 assert!(self.pos <= self.dst.capacity());
2518 self.pos
2519 }
2520
2521 /// Returns the capacity of the underlying buffer.
2522 pub fn capacity(&self) -> usize {
2523 self.dst.capacity()
2524 }
2525
2526 /// Sets the new cursor position.
2527 ///
2528 /// # Panics
2529 ///
2530 /// If `pos > self.dst.capacity()`.
2531 ///
2532 /// # Safety
2533 ///
2534 /// Data up to `pos` must have actually been written to.
2535 pub unsafe fn set_pos(&mut self, pos: usize) {
2536 if pos > self.dst.capacity() {
2537 panic!("Given position outside of the buffer bounds.");
2538 }
2539
2540 self.dst.filled_until(pos);
2541
2542 self.pos = pos;
2543 }
2544
2545 fn wrap<'b>(&'b mut self) -> OutBufferWrapper<'b, 'a, C> {
2546 OutBufferWrapper {
2547 buf: zstd_sys::ZSTD_outBuffer {
2548 dst: ptr_mut_void(self.dst),
2549 size: self.dst.capacity(),
2550 pos: self.pos,
2551 },
2552 parent: self,
2553 }
2554 }
2555
2556 /// Returns the part of this buffer that was written to.
2557 pub fn as_slice<'b>(&'b self) -> &'a [u8]
2558 where
2559 'b: 'a,
2560 {
2561 let pos = self.pos;
2562 &self.dst.as_slice()[..pos]
2563 }
2564
2565 /// Returns a pointer to the start of this buffer.
2566 pub fn as_mut_ptr(&mut self) -> *mut u8 {
2567 self.dst.as_mut_ptr()
2568 }
2569}
2570
2571impl<'a, 'b, C: WriteBuf + ?Sized> OutBufferWrapper<'a, 'b, C> {
2572 /// Passes `res` through, and forgets the position reported by zstd if it
2573 /// is an error, so the parent keeps the one from before the call.
2574 ///
2575 /// zstd.h makes no promise about `pos` after a failed call, and the legacy
2576 /// decoders report the whole output buffer as written when they fail; only
2577 /// a successful call vouches for the bytes up to `pos` being initialized.
2578 fn check(&mut self, res: SafeResult) -> SafeResult {
2579 if res.is_err() {
2580 self.buf.pos = self.parent.pos;
2581 }
2582 res
2583 }
2584}
2585
2586impl<'a, 'b, C: WriteBuf + ?Sized> Drop for OutBufferWrapper<'a, 'b, C> {
2587 fn drop(&mut self) {
2588 // Safe because `self.buf.pos` either comes from a successful call,
2589 // which guarantees that data until there has been written, or was put
2590 // back to the parent's position by `check()`.
2591 unsafe { self.parent.set_pos(self.buf.pos) };
2592 }
2593}
2594
2595struct InBufferWrapper<'a, 'b> {
2596 buf: zstd_sys::ZSTD_inBuffer,
2597 parent: &'a mut InBuffer<'b>,
2598}
2599
2600impl<'a, 'b: 'a> Deref for InBufferWrapper<'a, 'b> {
2601 type Target = zstd_sys::ZSTD_inBuffer;
2602
2603 fn deref(&self) -> &Self::Target {
2604 &self.buf
2605 }
2606}
2607
2608impl<'a, 'b: 'a> DerefMut for InBufferWrapper<'a, 'b> {
2609 fn deref_mut(&mut self) -> &mut Self::Target {
2610 &mut self.buf
2611 }
2612}
2613
2614impl<'a> InBuffer<'a> {
2615 /// Returns a new `InBuffer` around the given slice.
2616 ///
2617 /// Starts with `pos = 0`.
2618 pub fn around(src: &'a [u8]) -> Self {
2619 InBuffer { src, pos: 0 }
2620 }
2621
2622 /// Returns the current cursor position.
2623 pub fn pos(&self) -> usize {
2624 self.pos
2625 }
2626
2627 /// Sets the new cursor position.
2628 ///
2629 /// # Panics
2630 ///
2631 /// If `pos > self.src.len()`.
2632 pub fn set_pos(&mut self, pos: usize) {
2633 if pos > self.src.len() {
2634 panic!("Given position outside of the buffer bounds.");
2635 }
2636 self.pos = pos;
2637 }
2638
2639 fn wrap<'b>(&'b mut self) -> InBufferWrapper<'b, 'a> {
2640 // The fields are public, so `pos` may not have gone through `set_pos`.
2641 // Not all zstd functions check it before reading from `src + pos`.
2642 if self.pos > self.src.len() {
2643 panic!("Given position outside of the buffer bounds.");
2644 }
2645
2646 InBufferWrapper {
2647 buf: zstd_sys::ZSTD_inBuffer {
2648 src: ptr_void(self.src),
2649 size: self.src.len(),
2650 pos: self.pos,
2651 },
2652 parent: self,
2653 }
2654 }
2655}
2656
2657impl<'a, 'b> InBufferWrapper<'a, 'b> {
2658 /// Passes `res` through, and forgets the position reported by zstd if it
2659 /// is an error, so the parent keeps the one from before the call.
2660 ///
2661 /// See `OutBufferWrapper::check()`: a failed call consumed nothing usable,
2662 /// and the legacy decoders report the whole input as read.
2663 fn check(&mut self, res: SafeResult) -> SafeResult {
2664 if res.is_err() {
2665 self.buf.pos = self.parent.pos;
2666 }
2667 res
2668 }
2669}
2670
2671impl<'a, 'b> Drop for InBufferWrapper<'a, 'b> {
2672 fn drop(&mut self) {
2673 self.parent.set_pos(self.buf.pos);
2674 }
2675}
2676
2677/// A Decompression stream.
2678///
2679/// Same as `DCtx`.
2680pub type DStream<'a> = DCtx<'a>;
2681
2682// Some functions work on a "frame prefix".
2683// TODO: Define `struct FramePrefix(&[u8]);` and move these functions to it?
2684//
2685// Some other functions work on a dictionary (not CDict or DDict).
2686// Same thing?
2687
2688/// Wraps the `ZSTD_findFrameCompressedSize()` function.
2689///
2690/// `src` should contain at least an entire frame.
2691pub fn find_frame_compressed_size(src: &[u8]) -> SafeResult {
2692 let code = unsafe {
2693 zstd_sys::ZSTD_findFrameCompressedSize(ptr_void(src), src.len())
2694 };
2695 parse_code(code)
2696}
2697
2698/// Wraps the `ZSTD_getFrameContentSize()` function.
2699///
2700/// Args:
2701/// * `src`: A prefix of the compressed frame. It should at least include the frame header.
2702///
2703/// Returns:
2704/// * `Err(ContentSizeError)` if `src` is too small of a prefix, or if it appears corrupted.
2705/// * `Ok(None)` if the frame does not include a content size.
2706/// * `Ok(Some(content_size_in_bytes))` otherwise.
2707pub fn get_frame_content_size(
2708 src: &[u8],
2709) -> Result<Option<u64>, ContentSizeError> {
2710 parse_content_size(unsafe {
2711 zstd_sys::ZSTD_getFrameContentSize(ptr_void(src), src.len())
2712 })
2713}
2714
2715/// Wraps the `ZSTD_findDecompressedSize()` function.
2716///
2717/// `src` should be exactly a sequence of ZSTD frames.
2718#[cfg(feature = "experimental")]
2719#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2720pub fn find_decompressed_size(
2721 src: &[u8],
2722) -> Result<Option<u64>, ContentSizeError> {
2723 parse_content_size(unsafe {
2724 zstd_sys::ZSTD_findDecompressedSize(ptr_void(src), src.len())
2725 })
2726}
2727
2728/// Wraps the `ZSTD_isFrame()` function.
2729#[cfg(feature = "experimental")]
2730#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2731pub fn is_frame(buffer: &[u8]) -> bool {
2732 unsafe { zstd_sys::ZSTD_isFrame(ptr_void(buffer), buffer.len()) > 0 }
2733}
2734
2735/// Wraps the `ZSTD_getDictID_fromDict()` function.
2736///
2737/// Returns `None` if the dictionary is not a valid zstd dictionary.
2738pub fn get_dict_id_from_dict(dict: &[u8]) -> Option<NonZeroU32> {
2739 NonZeroU32::new(unsafe {
2740 zstd_sys::ZSTD_getDictID_fromDict(ptr_void(dict), dict.len()) as u32
2741 })
2742}
2743
2744/// Wraps the `ZSTD_getDictID_fromFrame()` function.
2745///
2746/// Returns `None` if the dictionary ID could not be decoded. This may happen if:
2747/// * The frame was not encoded with a dictionary.
2748/// * The frame intentionally did not include dictionary ID.
2749/// * The dictionary was non-conformant.
2750/// * `src` is too small and does not include the frame header.
2751/// * `src` is not a valid zstd frame prefix.
2752pub fn get_dict_id_from_frame(src: &[u8]) -> Option<NonZeroU32> {
2753 NonZeroU32::new(unsafe {
2754 zstd_sys::ZSTD_getDictID_fromFrame(ptr_void(src), src.len()) as u32
2755 })
2756}
2757
2758/// What kind of context reset should be applied.
2759#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2760pub enum ResetDirective {
2761 /// Only the session will be reset.
2762 ///
2763 /// All parameters will be preserved (including the dictionary).
2764 /// But any frame being processed will be dropped.
2765 ///
2766 /// It can be useful to start re-using a context after an error or when an
2767 /// ongoing compression is no longer needed.
2768 SessionOnly,
2769
2770 /// Only reset parameters (including dictionary or referenced prefix).
2771 ///
2772 /// All parameters will be reset to default values.
2773 ///
2774 /// This can only be done between sessions - no compression or decompression must be ongoing.
2775 Parameters,
2776
2777 /// Reset both the session and parameters.
2778 ///
2779 /// The result is similar to a newly created context.
2780 SessionAndParameters,
2781}
2782
2783impl ResetDirective {
2784 /// Does this drop a session in progress?
2785 ///
2786 /// Only a session reset brings a context back from the undefined state an
2787 /// error can leave it in - `Parameters` alone is refused while a session is
2788 /// open.
2789 fn resets_session(self) -> bool {
2790 matches!(
2791 self,
2792 ResetDirective::SessionOnly | ResetDirective::SessionAndParameters
2793 )
2794 }
2795
2796 fn as_sys(self) -> zstd_sys::ZSTD_ResetDirective {
2797 match self {
2798 ResetDirective::SessionOnly => zstd_sys::ZSTD_ResetDirective::ZSTD_reset_session_only,
2799 ResetDirective::Parameters => zstd_sys::ZSTD_ResetDirective::ZSTD_reset_parameters,
2800 ResetDirective::SessionAndParameters => zstd_sys::ZSTD_ResetDirective::ZSTD_reset_session_and_parameters,
2801 }
2802 }
2803}
2804
2805#[cfg(feature = "experimental")]
2806#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2807#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2808#[repr(u32)]
2809pub enum FrameFormat {
2810 /// Regular zstd format.
2811 One = zstd_sys::ZSTD_format_e::ZSTD_f_zstd1 as u32,
2812
2813 /// Skip the 4 bytes identifying the content as zstd-compressed data.
2814 Magicless = zstd_sys::ZSTD_format_e::ZSTD_f_zstd1_magicless as u32,
2815}
2816
2817#[cfg(feature = "experimental")]
2818#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2819#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2820#[repr(u32)]
2821pub enum DictAttachPref {
2822 DefaultAttach =
2823 zstd_sys::ZSTD_dictAttachPref_e::ZSTD_dictDefaultAttach as u32,
2824 ForceAttach = zstd_sys::ZSTD_dictAttachPref_e::ZSTD_dictForceAttach as u32,
2825 ForceCopy = zstd_sys::ZSTD_dictAttachPref_e::ZSTD_dictForceCopy as u32,
2826 ForceLoad = zstd_sys::ZSTD_dictAttachPref_e::ZSTD_dictForceLoad as u32,
2827}
2828
2829#[cfg(feature = "experimental")]
2830#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2831#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2832#[repr(u32)]
2833pub enum ParamSwitch {
2834 Auto = zstd_sys::ZSTD_ParamSwitch_e::ZSTD_ps_auto as u32,
2835 Enable = zstd_sys::ZSTD_ParamSwitch_e::ZSTD_ps_enable as u32,
2836 Disable = zstd_sys::ZSTD_ParamSwitch_e::ZSTD_ps_disable as u32,
2837}
2838
2839/// A compression parameter.
2840#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2841#[non_exhaustive]
2842pub enum CParameter {
2843 #[cfg(feature = "experimental")]
2844 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2845 RSyncable(bool),
2846
2847 #[cfg(feature = "experimental")]
2848 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2849 Format(FrameFormat),
2850
2851 #[cfg(feature = "experimental")]
2852 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2853 ForceMaxWindow(bool),
2854
2855 #[cfg(feature = "experimental")]
2856 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2857 ForceAttachDict(DictAttachPref),
2858
2859 #[cfg(feature = "experimental")]
2860 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2861 LiteralCompressionMode(ParamSwitch),
2862
2863 #[cfg(feature = "experimental")]
2864 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2865 SrcSizeHint(u32),
2866
2867 #[cfg(feature = "experimental")]
2868 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2869 EnableDedicatedDictSearch(bool),
2870
2871 /// Whether zstd may keep referencing the input buffer instead of copying
2872 /// it.
2873 ///
2874 /// This requires the input to stay allocated and unmodified until the
2875 /// frame is complete, which a borrowed `InBuffer` cannot guarantee:
2876 /// `CCtx::set_parameter` refuses to enable it and returns an error.
2877 #[cfg(feature = "experimental")]
2878 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2879 StableInBuffer(bool),
2880
2881 #[cfg(feature = "experimental")]
2882 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2883 StableOutBuffer(bool),
2884
2885 #[cfg(feature = "experimental")]
2886 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2887 BlockDelimiters(bool),
2888
2889 #[cfg(feature = "experimental")]
2890 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2891 ValidateSequences(bool),
2892
2893 #[cfg(feature = "experimental")]
2894 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2895 UseBlockSplitter(ParamSwitch),
2896
2897 #[cfg(feature = "experimental")]
2898 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2899 UseRowMatchFinder(ParamSwitch),
2900
2901 #[cfg(feature = "experimental")]
2902 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2903 DeterministicRefPrefix(bool),
2904
2905 #[cfg(feature = "experimental")]
2906 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2907 PrefetchCDictTables(ParamSwitch),
2908
2909 #[cfg(feature = "experimental")]
2910 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2911 EnableSeqProducerFallback(bool),
2912
2913 #[cfg(feature = "experimental")]
2914 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2915 MaxBlockSize(u32),
2916
2917 #[cfg(feature = "experimental")]
2918 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
2919 SearchForExternalRepcodes(ParamSwitch),
2920
2921 /// Target CBlock size.
2922 ///
2923 /// Tries to make compressed blocks fit in this size (not a guarantee, just a target).
2924 /// Useful to reduce end-to-end latency in low-bandwidth environments.
2925 ///
2926 /// No target when the value is 0.
2927 TargetCBlockSize(u32),
2928
2929 /// Compression level to use.
2930 ///
2931 /// Compression levels are global presets for the other compression parameters.
2932 CompressionLevel(CompressionLevel),
2933
2934 /// Maximum allowed back-reference distance.
2935 ///
2936 /// The actual distance is 2 power "this value".
2937 WindowLog(u32),
2938
2939 HashLog(u32),
2940
2941 ChainLog(u32),
2942
2943 SearchLog(u32),
2944
2945 MinMatch(u32),
2946
2947 TargetLength(u32),
2948
2949 Strategy(Strategy),
2950
2951 EnableLongDistanceMatching(bool),
2952
2953 LdmHashLog(u32),
2954
2955 LdmMinMatch(u32),
2956
2957 LdmBucketSizeLog(u32),
2958
2959 LdmHashRateLog(u32),
2960
2961 ContentSizeFlag(bool),
2962
2963 ChecksumFlag(bool),
2964
2965 DictIdFlag(bool),
2966
2967 /// How many threads will be spawned.
2968 ///
2969 /// With a default value of `0`, `compress_stream*` functions block until they complete.
2970 ///
2971 /// With any other value (including 1, a single compressing thread), these methods directly
2972 /// return, and the actual compression is done in the background (until a flush is requested).
2973 ///
2974 /// Note: this will only work if the `zstdmt` feature is activated.
2975 NbWorkers(u32),
2976
2977 /// Size in bytes of a compression job.
2978 ///
2979 /// Does not have any effect when `NbWorkers` is set to 0.
2980 ///
2981 /// The default value of 0 finds the best job size based on the compression parameters.
2982 ///
2983 /// Note: this will only work if the `zstdmt` feature is activated.
2984 JobSize(u32),
2985
2986 /// Specifies how much overlap must be given to each worker.
2987 ///
2988 /// Possible values:
2989 ///
2990 /// * `0` (default value): automatic overlap based on compression strategy.
2991 /// * `1`: No overlap
2992 /// * `1 < n < 9`: Overlap a fraction of the window size, defined as `1/(2 ^ 9-n)`.
2993 /// * `9`: Full overlap (as long as the window)
2994 /// * `9 < m`: Will return an error.
2995 ///
2996 /// Note: this will only work if the `zstdmt` feature is activated.
2997 OverlapSizeLog(u32),
2998}
2999
3000/// A decompression parameter.
3001#[derive(Copy, Clone, Debug, PartialEq, Eq)]
3002#[non_exhaustive]
3003pub enum DParameter {
3004 WindowLogMax(u32),
3005
3006 #[cfg(feature = "experimental")]
3007 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3008 /// See `FrameFormat`.
3009 Format(FrameFormat),
3010
3011 #[cfg(feature = "experimental")]
3012 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3013 StableOutBuffer(bool),
3014
3015 #[cfg(feature = "experimental")]
3016 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3017 ForceIgnoreChecksum(bool),
3018
3019 #[cfg(feature = "experimental")]
3020 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3021 RefMultipleDDicts(bool),
3022}
3023
3024/// Wraps the `ZDICT_trainFromBuffer()` function.
3025#[cfg(feature = "zdict_builder")]
3026#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "zdict_builder")))]
3027pub fn train_from_buffer<C: WriteBuf + ?Sized>(
3028 dict_buffer: &mut C,
3029 samples_buffer: &[u8],
3030 samples_sizes: &[usize],
3031) -> SafeResult {
3032 // A plain `sum()` wraps around when overflow checks are disabled, and
3033 // would accept sizes reaching far outside of `samples_buffer`.
3034 let total_size = samples_sizes
3035 .iter()
3036 .try_fold(0usize, |total, &size| total.checked_add(size));
3037 assert_eq!(
3038 Some(samples_buffer.len()),
3039 total_size,
3040 "sample sizes don't add up to the samples buffer length"
3041 );
3042
3043 unsafe {
3044 dict_buffer.write_from(|buffer, capacity| {
3045 parse_code(zstd_sys::ZDICT_trainFromBuffer(
3046 buffer,
3047 capacity,
3048 ptr_void(samples_buffer),
3049 samples_sizes.as_ptr(),
3050 samples_sizes.len() as u32,
3051 ))
3052 })
3053 }
3054}
3055
3056/// Wraps the `ZDICT_getDictID()` function.
3057#[cfg(feature = "zdict_builder")]
3058#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "zdict_builder")))]
3059pub fn get_dict_id(dict_buffer: &[u8]) -> Option<NonZeroU32> {
3060 NonZeroU32::new(unsafe {
3061 zstd_sys::ZDICT_getDictID(ptr_void(dict_buffer), dict_buffer.len())
3062 })
3063}
3064
3065/// Wraps the `ZSTD_getBlockSize()` function.
3066#[cfg(feature = "experimental")]
3067#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3068#[deprecated(
3069 note = "The block API is deprecated; use the normal compression API instead."
3070)]
3071pub fn get_block_size(cctx: &CCtx) -> usize {
3072 unsafe { zstd_sys::ZSTD_getBlockSize(cctx.0.as_ptr()) }
3073}
3074
3075/// Wraps the `ZSTD_decompressBound` function
3076#[cfg(feature = "experimental")]
3077#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3078pub fn decompress_bound(data: &[u8]) -> Result<u64, ErrorCode> {
3079 let bound =
3080 unsafe { zstd_sys::ZSTD_decompressBound(ptr_void(data), data.len()) };
3081 if is_error(bound as usize) {
3082 Err(bound as usize)
3083 } else {
3084 Ok(bound)
3085 }
3086}
3087
3088/// Given a buffer of size `src_size`, returns the maximum number of sequences that can ge
3089/// generated.
3090#[cfg(feature = "experimental")]
3091#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3092pub fn sequence_bound(src_size: usize) -> usize {
3093 // Safety: Just FFI.
3094 unsafe { zstd_sys::ZSTD_sequenceBound(src_size) }
3095}
3096
3097/// Returns the minimum extra space when output and input buffer overlap.
3098///
3099/// When using in-place decompression, the output buffer must be at least this much bigger (in
3100/// bytes) than the input buffer. The extra space must be at the front of the output buffer (the
3101/// input buffer must be at the end of the output buffer).
3102#[cfg(feature = "experimental")]
3103#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
3104pub fn decompression_margin(
3105 compressed_data: &[u8],
3106) -> Result<usize, ErrorCode> {
3107 parse_code(unsafe {
3108 zstd_sys::ZSTD_decompressionMargin(
3109 ptr_void(compressed_data),
3110 compressed_data.len(),
3111 )
3112 })
3113}