diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index fd51c0f10..4746b3056 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -88,6 +88,8 @@ size_t ZSTD_rust_nextInputSizeHint(int inBufferMode, size_t inBuffPos); size_t ZSTD_rust_CStreamInSize(void); size_t ZSTD_rust_CStreamOutSize(void); +size_t ZSTD_rust_sizeofLocalDict(int dictBufferPresent, size_t dictSize, + size_t cdictSize); /* Context-free compression-parameter selection and sizing leaves live in * Rust (rust/src/zstd_compress_params.rs). This file retains @@ -528,9 +530,9 @@ static void ZSTD_clearAllDicts(ZSTD_CCtx* cctx) static size_t ZSTD_sizeof_localDict(ZSTD_localDict dict) { - size_t const bufferSize = dict.dictBuffer != NULL ? dict.dictSize : 0; size_t const cdictSize = ZSTD_sizeof_CDict(dict.cdict); - return bufferSize + cdictSize; + return ZSTD_rust_sizeofLocalDict(dict.dictBuffer != NULL, dict.dictSize, + cdictSize); } static void ZSTD_freeCCtxContent(ZSTD_CCtx* cctx) diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index d7fe62e9d..cf7dbfbf8 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -301,6 +301,26 @@ pub extern "C" fn ZSTD_rust_dictTooBig(loaded_dict_size: usize) -> c_int { dict_too_big(loaded_dict_size) as c_int } +#[inline] +fn sizeof_local_dict(dict_buffer_present: c_int, dict_size: usize, cdict_size: usize) -> usize { + let buffer_size = if dict_buffer_present != 0 { + dict_size + } else { + 0 + }; + buffer_size.wrapping_add(cdict_size) +} + +/// Add the C-owned local-dictionary sizes with C `size_t` wrapping semantics. +#[no_mangle] +pub extern "C" fn ZSTD_rust_sizeofLocalDict( + dict_buffer_present: c_int, + dict_size: usize, + cdict_size: usize, +) -> usize { + sizeof_local_dict(dict_buffer_present, dict_size, cdict_size) +} + #[inline] fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) { debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0); @@ -1136,6 +1156,25 @@ mod tests { assert_eq!(rep, [0; ZSTD_REP_NUM]); } + #[test] + fn sizeof_local_dict_ignores_size_without_a_buffer() { + assert_eq!(sizeof_local_dict(0, 37, 11), 11); + assert_eq!(ZSTD_rust_sizeofLocalDict(0, 37, 11), 11); + } + + #[test] + fn sizeof_local_dict_adds_present_buffer_and_cdict_sizes() { + assert_eq!(sizeof_local_dict(1, 37, 11), 48); + assert_eq!(sizeof_local_dict(1, 0, 11), 11); + assert_eq!(ZSTD_rust_sizeofLocalDict(1, 37, 11), 48); + } + + #[test] + fn sizeof_local_dict_wraps_like_c_size_t_addition() { + assert_eq!(sizeof_local_dict(1, usize::MAX, 1), 0); + assert_eq!(sizeof_local_dict(0, usize::MAX, usize::MAX), usize::MAX); + } + #[test] fn public_one_shot_abi_is_c_compatible() { let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =