feat(rust): export compression bound from Rust

Move the context-free public ZSTD_compressBound ABI into Rust while leaving
the stateful compressor context in C. The Rust implementation preserves the
size-width-specific input limit, macro arithmetic, and srcSize_wrong error
contract used by C callers.

Test Plan:
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt
- cargo test zstd_compress_api::tests -- --nocapture
- cargo test --target i686-unknown-linux-gnu zstd_compress_api::tests
- make -B -C lib -j2 libzstd.a
- fuzzer -s5346 -i1 --no-big-tests

Refs: public ZSTD_COMPRESSBOUND macro in lib/zstd.h
This commit is contained in:
2026-07-10 22:49:19 +02:00
parent 74140c318f
commit 3e2635f45d
3 changed files with 50 additions and 13 deletions
+2
View File
@@ -22,6 +22,8 @@ pub mod threading;
pub mod xxhash;
pub mod zstd_common;
#[cfg(feature = "compression")]
pub mod zstd_compress_api;
#[cfg(feature = "compression")]
pub mod zstd_compress_literals;
#[cfg(feature = "compression")]
pub mod zstd_compress_sequences;
+45
View File
@@ -0,0 +1,45 @@
//! Public, context-free compression API helpers.
//!
//! The stateful compression context remains in the C translation unit for
//! now. These helpers have no context layout dependency, so they can expose
//! the public ABI directly from Rust.
use crate::errors::{ZstdErrorCode, ERROR};
#[cfg(target_pointer_width = "64")]
const ZSTD_MAX_INPUT_SIZE: usize = 0xFF00_FF00_FF00_FF00;
#[cfg(target_pointer_width = "32")]
const ZSTD_MAX_INPUT_SIZE: usize = 0xFF00_FF00;
const SMALL_INPUT_THRESHOLD: usize = 128 << 10;
/// Returns the maximum one-pass compressed size, matching
/// `ZSTD_COMPRESSBOUND()` and its public error contract.
#[no_mangle]
pub extern "C" fn ZSTD_compressBound(src_size: usize) -> usize {
if src_size >= ZSTD_MAX_INPUT_SIZE {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let margin = if src_size < SMALL_INPUT_THRESHOLD {
(SMALL_INPUT_THRESHOLD - src_size) >> 11
} else {
0
};
src_size.wrapping_add(src_size >> 8).wrapping_add(margin)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::ERR_isError;
#[test]
fn compress_bound_matches_the_public_macro_edges() {
assert_eq!(ZSTD_compressBound(0), 64);
assert_eq!(ZSTD_compressBound(1), 64);
assert_eq!(ZSTD_compressBound(SMALL_INPUT_THRESHOLD - 1), 131_582);
assert_eq!(ZSTD_compressBound(SMALL_INPUT_THRESHOLD), 131_584);
assert!(ERR_isError(ZSTD_compressBound(ZSTD_MAX_INPUT_SIZE)));
}
}