Skip to main content

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