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