Skip to main content

libzstd_rs_sys/lib/decompress/
zstd_ddict.rs

1use libc::{free, malloc, size_t};
2
3use crate::lib::common::error_private::{ERR_isError, Error};
4use crate::lib::decompress::huf_decompress::DTableDesc;
5use crate::lib::decompress::zstd_decompress::ZSTD_loadDEntropy;
6use crate::lib::decompress::{ZSTD_DCtx, ZSTD_entropyDTables_t};
7use crate::lib::zstd::*;
8
9#[repr(u32)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum MultipleDDicts {
12    Single = 0,
13    Multiple = 1,
14}
15
16impl TryFrom<u32> for MultipleDDicts {
17    type Error = ();
18
19    fn try_from(value: u32) -> Result<Self, Self::Error> {
20        match value {
21            0 => Ok(Self::Single),
22            1 => Ok(Self::Multiple),
23            _ => Err(()),
24        }
25    }
26}
27
28#[repr(C)]
29pub struct ZSTD_DDictHashSet {
30    pub ddictPtrTable: *mut *const ZSTD_DDict,
31    pub ddictPtrTableSize: size_t,
32    pub ddictPtrCount: size_t,
33}
34#[repr(C)]
35pub struct ZSTD_DDict {
36    dictBuffer: *mut core::ffi::c_void,
37    dictContent: *const core::ffi::c_void,
38    dictSize: size_t,
39    entropy: ZSTD_entropyDTables_t,
40    dictID: u32,
41    entropyPresent: u32,
42    cMem: ZSTD_customMem,
43}
44pub type ZSTD_dictContentType_e = core::ffi::c_uint;
45pub const ZSTD_dct_fullDict: ZSTD_dictContentType_e = 2;
46pub const ZSTD_dct_rawContent: ZSTD_dictContentType_e = 1;
47pub const ZSTD_dct_auto: ZSTD_dictContentType_e = 0;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50enum DictLoadMethod {
51    ByCopy = 0,
52    ByRef = 1,
53}
54
55pub type ZSTD_dictLoadMethod_e = core::ffi::c_uint;
56pub const ZSTD_dlm_byCopy: ZSTD_dictLoadMethod_e = DictLoadMethod::ByCopy as _;
57pub const ZSTD_dlm_byRef: ZSTD_dictLoadMethod_e = DictLoadMethod::ByRef as _;
58
59pub const ZSTD_MAGIC_DICTIONARY: core::ffi::c_uint = 0xec30a437 as core::ffi::c_uint;
60
61#[inline]
62unsafe extern "C" fn ZSTD_customMalloc(
63    size: size_t,
64    customMem: ZSTD_customMem,
65) -> *mut core::ffi::c_void {
66    if (customMem.customAlloc).is_some() {
67        return (customMem.customAlloc).unwrap_unchecked()(customMem.opaque, size);
68    }
69    malloc(size)
70}
71#[inline]
72unsafe extern "C" fn ZSTD_customFree(ptr: *mut core::ffi::c_void, customMem: ZSTD_customMem) {
73    if !ptr.is_null() {
74        if (customMem.customFree).is_some() {
75            (customMem.customFree).unwrap_unchecked()(customMem.opaque, ptr);
76        } else {
77            free(ptr);
78        }
79    }
80}
81pub unsafe fn ZSTD_DDict_dictContent(ddict: *const ZSTD_DDict) -> *const core::ffi::c_void {
82    (*ddict).dictContent
83}
84pub unsafe fn ZSTD_DDict_dictSize(ddict: *const ZSTD_DDict) -> size_t {
85    (*ddict).dictSize
86}
87pub unsafe fn ZSTD_copyDDictParameters(dctx: *mut ZSTD_DCtx, ddict: *const ZSTD_DDict) {
88    (*dctx).dictID = (*ddict).dictID;
89    (*dctx).prefixStart = (*ddict).dictContent;
90    (*dctx).virtualStart = (*ddict).dictContent;
91    (*dctx).dictEnd =
92        ((*ddict).dictContent as *const u8).add((*ddict).dictSize) as *const core::ffi::c_void;
93    (*dctx).previousDstEnd = (*dctx).dictEnd;
94    if (*ddict).entropyPresent != 0 {
95        (*dctx).litEntropy = 1;
96        (*dctx).fseEntropy = 1;
97        (*dctx).LLTptr = ((*ddict).entropy.LLTable).as_ptr();
98        (*dctx).MLTptr = ((*ddict).entropy.MLTable).as_ptr();
99        (*dctx).OFTptr = ((*ddict).entropy.OFTable).as_ptr();
100        (*dctx).HUFptr = &raw const (*ddict).entropy.hufTable;
101        *((*dctx).entropy.rep).as_mut_ptr().offset(0) = *((*ddict).entropy.rep).as_ptr().offset(0);
102        *((*dctx).entropy.rep).as_mut_ptr().offset(1) = *((*ddict).entropy.rep).as_ptr().offset(1);
103        *((*dctx).entropy.rep).as_mut_ptr().offset(2) = *((*ddict).entropy.rep).as_ptr().offset(2);
104    } else {
105        (*dctx).litEntropy = 0;
106        (*dctx).fseEntropy = 0;
107    };
108}
109unsafe fn ZSTD_loadEntropy_intoDDict(
110    ddict: *mut ZSTD_DDict,
111    dictContentType: ZSTD_dictContentType_e,
112) -> size_t {
113    (*ddict).dictID = 0;
114    (*ddict).entropyPresent = 0;
115
116    if dictContentType == ZSTD_dct_rawContent as ZSTD_dictContentType_e {
117        return 0;
118    }
119
120    let dict = if (*ddict).dictContent.is_null() {
121        &[]
122    } else {
123        core::slice::from_raw_parts((*ddict).dictContent.cast::<u8>(), (*ddict).dictSize)
124    };
125
126    let ([magic, dict_id, ..], _) = dict.as_chunks::<4>() else {
127        if dictContentType == ZSTD_dct_fullDict as ZSTD_dictContentType_e {
128            return Error::dictionary_corrupted.to_error_code();
129        }
130
131        return 0;
132    };
133
134    let magic = u32::from_le_bytes(*magic);
135    if magic != ZSTD_MAGIC_DICTIONARY {
136        if dictContentType == ZSTD_dct_fullDict as ZSTD_dictContentType_e {
137            return Error::dictionary_corrupted.to_error_code();
138        }
139
140        return 0;
141    }
142
143    (*ddict).dictID = u32::from_le_bytes(*dict_id);
144
145    let ret = ZSTD_loadDEntropy(&mut (*ddict).entropy, dict);
146
147    if ERR_isError(ret) {
148        return Error::dictionary_corrupted.to_error_code();
149    }
150
151    (*ddict).entropyPresent = 1;
152
153    0
154}
155
156unsafe fn ZSTD_initDDict_internal(
157    ddict: *mut ZSTD_DDict,
158    dict: *const core::ffi::c_void,
159    mut dictSize: size_t,
160    dictLoadMethod: ZSTD_dictLoadMethod_e,
161    dictContentType: ZSTD_dictContentType_e,
162) -> size_t {
163    if dictLoadMethod == DictLoadMethod::ByRef as ZSTD_dictLoadMethod_e
164        || dict.is_null()
165        || dictSize == 0
166    {
167        (*ddict).dictBuffer = core::ptr::null_mut();
168        (*ddict).dictContent = dict;
169        if dict.is_null() {
170            dictSize = 0;
171        }
172    } else {
173        let internalBuffer = ZSTD_customMalloc(dictSize, (*ddict).cMem);
174        (*ddict).dictBuffer = internalBuffer;
175        (*ddict).dictContent = internalBuffer;
176        if internalBuffer.is_null() {
177            return Error::dictionary_corrupted.to_error_code();
178        }
179        libc::memcpy(internalBuffer, dict, dictSize as libc::size_t);
180    }
181
182    (*ddict).dictSize = dictSize;
183    (*ddict).entropy.hufTable.description = DTableDesc::from_u32(12 * 0x1000001);
184
185    let err_code = ZSTD_loadEntropy_intoDDict(ddict, dictContentType);
186    if ERR_isError(err_code) {
187        return err_code;
188    }
189
190    0
191}
192
193#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_createDDict_advanced))]
194pub unsafe extern "C" fn ZSTD_createDDict_advanced(
195    dict: *const core::ffi::c_void,
196    dictSize: size_t,
197    dictLoadMethod: ZSTD_dictLoadMethod_e,
198    dictContentType: ZSTD_dictContentType_e,
199    customMem: ZSTD_customMem,
200) -> *mut ZSTD_DDict {
201    if customMem.customAlloc.is_none() ^ customMem.customFree.is_none() {
202        return core::ptr::null_mut();
203    }
204
205    let ddict = ZSTD_customMalloc(size_of::<ZSTD_DDict>(), customMem) as *mut ZSTD_DDict;
206
207    if ddict.is_null() {
208        return core::ptr::null_mut();
209    }
210
211    (*ddict).cMem = customMem;
212    let initResult =
213        ZSTD_initDDict_internal(ddict, dict, dictSize, dictLoadMethod, dictContentType);
214
215    if ERR_isError(initResult) {
216        ZSTD_freeDDict(ddict);
217        return core::ptr::null_mut();
218    }
219
220    ddict
221}
222#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_createDDict))]
223pub unsafe extern "C" fn ZSTD_createDDict(
224    dict: *const core::ffi::c_void,
225    dictSize: size_t,
226) -> *mut ZSTD_DDict {
227    let allocator = ZSTD_customMem {
228        customAlloc: None,
229        customFree: None,
230        opaque: core::ptr::null_mut(),
231    };
232    ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, allocator)
233}
234#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_createDDict_byReference))]
235pub unsafe extern "C" fn ZSTD_createDDict_byReference(
236    dictBuffer: *const core::ffi::c_void,
237    dictSize: size_t,
238) -> *mut ZSTD_DDict {
239    let allocator = {
240        ZSTD_customMem {
241            customAlloc: None,
242            customFree: None,
243            opaque: core::ptr::null_mut(),
244        }
245    };
246    ZSTD_createDDict_advanced(
247        dictBuffer,
248        dictSize,
249        ZSTD_dlm_byRef,
250        ZSTD_dct_auto,
251        allocator,
252    )
253}
254#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_initStaticDDict))]
255pub unsafe extern "C" fn ZSTD_initStaticDDict(
256    sBuffer: *mut core::ffi::c_void,
257    sBufferSize: size_t,
258    mut dict: *const core::ffi::c_void,
259    dictSize: size_t,
260    dictLoadMethod: ZSTD_dictLoadMethod_e,
261    dictContentType: ZSTD_dictContentType_e,
262) -> *const ZSTD_DDict {
263    if sBuffer as usize & 0b111 != 0 {
264        return core::ptr::null_mut();
265    }
266
267    if sBufferSize < ZSTD_estimateDDictSize(dictSize, dictLoadMethod) {
268        return core::ptr::null_mut();
269    }
270
271    let ddict = sBuffer as *mut ZSTD_DDict;
272    if dictLoadMethod == DictLoadMethod::ByCopy as ZSTD_dictLoadMethod_e {
273        libc::memcpy(ddict.add(1) as *mut core::ffi::c_void, dict, dictSize);
274        dict = ddict.add(1) as *const core::ffi::c_void;
275    }
276
277    let ret = ZSTD_initDDict_internal(
278        ddict,
279        dict,
280        dictSize,
281        DictLoadMethod::ByRef as _,
282        dictContentType,
283    );
284
285    if ERR_isError(ret) {
286        return core::ptr::null_mut();
287    }
288
289    ddict
290}
291#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_freeDDict))]
292pub unsafe extern "C" fn ZSTD_freeDDict(ddict: *mut ZSTD_DDict) -> size_t {
293    if ddict.is_null() {
294        return 0;
295    }
296    let cMem = (*ddict).cMem;
297    ZSTD_customFree((*ddict).dictBuffer, cMem);
298    ZSTD_customFree(ddict as *mut core::ffi::c_void, cMem);
299    0
300}
301
302#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_estimateDDictSize))]
303pub const extern "C" fn ZSTD_estimateDDictSize(
304    dict_size: size_t,
305    dict_load_method: ZSTD_dictLoadMethod_e,
306) -> size_t {
307    if dict_load_method == ZSTD_dlm_byRef as ZSTD_dictLoadMethod_e {
308        size_of::<ZSTD_DDict>()
309    } else {
310        size_of::<ZSTD_DDict>() + dict_size
311    }
312}
313
314#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_sizeof_DDict))]
315pub unsafe extern "C" fn ZSTD_sizeof_DDict(ddict: *const ZSTD_DDict) -> size_t {
316    if ddict.is_null() {
317        return 0;
318    }
319    (::core::mem::size_of::<ZSTD_DDict>()).wrapping_add(if !((*ddict).dictBuffer).is_null() {
320        (*ddict).dictSize
321    } else {
322        0
323    })
324}
325#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_getDictID_fromDDict))]
326pub unsafe extern "C" fn ZSTD_getDictID_fromDDict(ddict: *const ZSTD_DDict) -> core::ffi::c_uint {
327    if ddict.is_null() {
328        return 0;
329    }
330    (*ddict).dictID
331}
332
333#[cfg(test)]
334mod tests {
335    use super::*;
336
337    #[test]
338    fn test_estimate_ddict_size() {
339        assert_eq!(
340            ZSTD_estimateDDictSize(1234, ZSTD_dlm_byCopy),
341            size_of::<ZSTD_DDict>() + 1234
342        );
343        assert_eq!(
344            ZSTD_estimateDDictSize(1234, ZSTD_dlm_byRef),
345            size_of::<ZSTD_DDict>()
346        );
347    }
348}