feat(compress): move CCtx size aggregation to Rust

Keep ZSTD_sizeof_CCtx's NULL handling, workspace accounting, local-dictionary
sizing, and multithreaded context sizing in C, where the private layouts and
ownership remain visible. Pass only the four resulting size_t values to a Rust
ABI helper, which performs the final ordered additions with C-compatible
wrapping semantics. Add focused coverage for zero, ordinary, and overflowing
component sums.

Test Plan:
- `cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression` -- passed before and after formatting
- Same clippy command with `--benches` and `--tests` -- passed before and after formatting
- `cargo +nightly fmt --manifest-path rust/Cargo.toml` -- passed
- `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression sizeof_cctx` -- passed (6 tests)
- `make -B -C lib -j2 lib` -- passed
- `make -C tests test-rust-lib-smoke` -- passed
- `tests/fuzzer -s4560 -t56 -i57 -v` -- passed
- `make -C tests -j2 test-zstream` -- passed (84 named, 6252 and 9519 randomized)
- `git diff --check` and `git diff --cached --check` -- passed
This commit is contained in:
2026-07-18 11:40:15 +02:00
parent efd3d76a4a
commit 0000c6d068
2 changed files with 54 additions and 5 deletions
+11 -5
View File
@@ -90,6 +90,8 @@ 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);
size_t ZSTD_rust_sizeofCCtx(size_t objectSize, size_t workspaceSize,
size_t localDictSize, size_t mtctxSize);
ZSTD_inBuffer ZSTD_rust_inBufferForEndFlush(int inBufferMode,
const void* expectedSrc,
size_t expectedSize,
@@ -582,11 +584,15 @@ static size_t ZSTD_sizeof_mtctx(const ZSTD_CCtx* cctx)
size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx)
{
if (cctx==NULL) return 0; /* support sizeof on NULL */
/* cctx may be in the workspace */
return (cctx->workspace.workspace == cctx ? 0 : sizeof(*cctx))
+ ZSTD_cwksp_sizeof(&cctx->workspace)
+ ZSTD_sizeof_localDict(cctx->localDict)
+ ZSTD_sizeof_mtctx(cctx);
{
/* cctx may be in the workspace */
size_t const objectSize = cctx->workspace.workspace == cctx ? 0 : sizeof(*cctx);
size_t const workspaceSize = ZSTD_cwksp_sizeof(&cctx->workspace);
size_t const localDictSize = ZSTD_sizeof_localDict(cctx->localDict);
size_t const mtctxSize = ZSTD_sizeof_mtctx(cctx);
return ZSTD_rust_sizeofCCtx(objectSize, workspaceSize, localDictSize,
mtctxSize);
}
}
size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs)
+43
View File
@@ -430,6 +430,31 @@ pub extern "C" fn ZSTD_rust_sizeofLocalDict(
sizeof_local_dict(dict_buffer_present, dict_size, cdict_size)
}
#[inline]
fn sizeof_cctx(
object_size: usize,
workspace_size: usize,
local_dict_size: usize,
mtctx_size: usize,
) -> usize {
object_size
.wrapping_add(workspace_size)
.wrapping_add(local_dict_size)
.wrapping_add(mtctx_size)
}
/// Aggregate C-owned context size components with C `size_t` wrapping
/// semantics.
#[no_mangle]
pub extern "C" fn ZSTD_rust_sizeofCCtx(
object_size: usize,
workspace_size: usize,
local_dict_size: usize,
mtctx_size: usize,
) -> usize {
sizeof_cctx(object_size, workspace_size, local_dict_size, mtctx_size)
}
#[inline]
fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) {
debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0);
@@ -1409,6 +1434,24 @@ mod tests {
assert_eq!(sizeof_local_dict(0, usize::MAX, usize::MAX), usize::MAX);
}
#[test]
fn sizeof_cctx_handles_zero_components() {
assert_eq!(sizeof_cctx(0, 0, 0, 0), 0);
assert_eq!(ZSTD_rust_sizeofCCtx(0, 0, 0, 0), 0);
}
#[test]
fn sizeof_cctx_adds_components_in_order() {
assert_eq!(sizeof_cctx(1, 2, 3, 4), 10);
assert_eq!(ZSTD_rust_sizeofCCtx(1, 2, 3, 4), 10);
}
#[test]
fn sizeof_cctx_wraps_like_c_size_t_addition() {
assert_eq!(sizeof_cctx(usize::MAX, 1, 2, 3), 5);
assert_eq!(ZSTD_rust_sizeofCCtx(usize::MAX, 1, 2, 3), 5);
}
#[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 =