feat(compress): route simple context frames through Rust
Dispatch ordinary dictionary-free ZSTD_compress2 calls through the Rust frame compressor while retaining the original C implementation for advanced contexts. Prepare the C workspace first so allocation downsizing and static-context limits remain observable. Mark successful Rust frames so a following one-shot ZSTD_compressStream2(..., ZSTD_e_end) call uses the same output path. Other streaming directives and advanced parameters remain on the C implementation. Test Plan: - RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression zstd_compress::tests - RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression -- -D warnings - cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check - make -B -C tests fuzzer - ./tests/fuzzer -t114 -i1000 --no-big-tests -s1 The basic fuzzer run now reaches the existing Rust one-shot compression-ratio limit at test 113; the new stateful reuse tests pass before that point.
This commit is contained in:
@@ -38,6 +38,15 @@ size_t ZSTD_rust_writeFrameHeader(void* dst, size_t dstCapacity,
|
||||
U32 windowLog, U64 pledgedSrcSize,
|
||||
U32 dictID);
|
||||
size_t ZSTD_rust_resetCCtxForSimpleCompression(void* cctx);
|
||||
size_t ZSTD_rust_prepareCCtxForSimpleCompression(void* cctx,
|
||||
size_t srcSize,
|
||||
int compressionLevel);
|
||||
size_t ZSTD_rust_resetCCtxForSimpleCompressionSession(void* cctx);
|
||||
void ZSTD_rust_markSimpleCompression2Complete(void* cctx);
|
||||
size_t ZSTD_compress2_c(ZSTD_CCtx* cctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize);
|
||||
int ZSTD_rust_simpleCompress2Level(const void* cctx);
|
||||
|
||||
/* Context-free compression-parameter selection and sizing leaves live in
|
||||
* Rust (rust/src/zstd_compress_params.rs). This file retains
|
||||
@@ -1146,6 +1155,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(
|
||||
* Also dumps dictionary */
|
||||
size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)
|
||||
{
|
||||
cctx->rustSimpleCompress2Completed = 0;
|
||||
if ( (reset == ZSTD_reset_session_only)
|
||||
|| (reset == ZSTD_reset_session_and_parameters) ) {
|
||||
cctx->streamStage = zcss_init;
|
||||
@@ -4472,6 +4482,94 @@ size_t ZSTD_rust_resetCCtxForSimpleCompression(void* cctx)
|
||||
return ZSTD_CCtx_reset((ZSTD_CCtx*)cctx, ZSTD_reset_session_and_parameters);
|
||||
}
|
||||
|
||||
size_t ZSTD_rust_prepareCCtxForSimpleCompression(void* opaqueCctx,
|
||||
size_t srcSize,
|
||||
int compressionLevel)
|
||||
{
|
||||
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)opaqueCctx;
|
||||
ZSTD_CCtx_params params;
|
||||
ZSTD_parameters const zstdParams = ZSTD_getParams_internal(
|
||||
compressionLevel, srcSize, 0, ZSTD_cpm_noAttachDict);
|
||||
ZSTD_CCtxParams_init_internal(
|
||||
¶ms, &zstdParams,
|
||||
compressionLevel == 0 ? ZSTD_CLEVEL_DEFAULT : compressionLevel);
|
||||
FORWARD_IF_ERROR(ZSTD_compressBegin_internal(
|
||||
cctx, NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL,
|
||||
¶ms, srcSize, ZSTDb_not_buffered), "");
|
||||
return ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
|
||||
}
|
||||
|
||||
size_t ZSTD_rust_resetCCtxForSimpleCompressionSession(void* cctx)
|
||||
{
|
||||
return ZSTD_CCtx_reset((ZSTD_CCtx*)cctx, ZSTD_reset_session_only);
|
||||
}
|
||||
|
||||
void ZSTD_rust_markSimpleCompression2Complete(void* cctx)
|
||||
{
|
||||
((ZSTD_CCtx*)cctx)->rustSimpleCompress2Completed = 1;
|
||||
}
|
||||
|
||||
/* Return the requested level when the context has only the parameters that
|
||||
* the Rust frame path currently implements. All other contexts continue
|
||||
* through ZSTD_compress2_c(), preserving the full C stateful implementation
|
||||
* while this boundary is migrated incrementally. */
|
||||
int ZSTD_rust_simpleCompress2Level(const void* opaqueCctx)
|
||||
{
|
||||
ZSTD_CCtx const* const cctx = (ZSTD_CCtx const*)opaqueCctx;
|
||||
ZSTD_CCtx_params const* const params = cctx ? &cctx->requestedParams : NULL;
|
||||
if (params == NULL
|
||||
|| params->format != ZSTD_f_zstd1
|
||||
|| params->fParams.contentSizeFlag == 0
|
||||
|| params->fParams.checksumFlag != 0
|
||||
|| params->cParams.windowLog != 0
|
||||
|| params->cParams.chainLog != 0
|
||||
|| params->cParams.hashLog != 0
|
||||
|| params->cParams.searchLog != 0
|
||||
|| params->cParams.minMatch != 0
|
||||
|| params->cParams.targetLength != 0
|
||||
|| params->cParams.strategy != 0
|
||||
|| params->forceWindow != 0
|
||||
|| params->targetCBlockSize != 0
|
||||
|| params->srcSizeHint != 0
|
||||
|| params->attachDictPref != ZSTD_dictDefaultAttach
|
||||
|| params->literalCompressionMode != ZSTD_lcm_auto
|
||||
|| params->nbWorkers != 0
|
||||
|| params->jobSize != 0
|
||||
|| params->overlapLog != 0
|
||||
|| params->rsyncable != 0
|
||||
|| params->ldmParams.enableLdm != ZSTD_ps_auto
|
||||
|| params->ldmParams.hashLog != 0
|
||||
|| (params->ldmParams.bucketSizeLog != 0
|
||||
&& params->ldmParams.bucketSizeLog != 9999)
|
||||
|| params->ldmParams.minMatchLength != 0
|
||||
|| (params->ldmParams.hashRateLog != 0
|
||||
&& params->ldmParams.hashRateLog != 9999)
|
||||
|| params->ldmParams.windowLog != 0
|
||||
|| params->enableDedicatedDictSearch != 0
|
||||
|| params->inBufferMode != ZSTD_bm_buffered
|
||||
|| params->outBufferMode != ZSTD_bm_buffered
|
||||
|| params->blockDelimiters != ZSTD_sf_noBlockDelimiters
|
||||
|| params->validateSequences != 0
|
||||
|| params->postBlockSplitter != ZSTD_ps_auto
|
||||
|| params->preBlockSplitter_level != 0
|
||||
|| params->maxBlockSize != 0
|
||||
|| params->useRowMatchFinder != ZSTD_ps_auto
|
||||
|| params->deterministicRefPrefix != 0
|
||||
|| params->prefetchCDictTables != ZSTD_ps_auto
|
||||
|| params->enableMatchFinderFallback != 0
|
||||
|| params->extSeqProdFunc != NULL
|
||||
|| params->searchForExternalRepcodes != ZSTD_ps_auto
|
||||
|| cctx->pool != NULL
|
||||
|| cctx->seqCollector.collectSequences != 0
|
||||
|| cctx->cdict != NULL
|
||||
|| cctx->prefixDict.dict != NULL
|
||||
|| cctx->localDict.dictBuffer != NULL
|
||||
|| cctx->localDict.cdict != NULL) {
|
||||
return (-2147483647 - 1);
|
||||
}
|
||||
return params->compressionLevel;
|
||||
}
|
||||
|
||||
/* ZSTD_compress() is implemented by rust/src/zstd_compress.rs. */
|
||||
|
||||
|
||||
@@ -5418,6 +5516,30 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
|
||||
assert(cctx != NULL);
|
||||
|
||||
/* transparent initialization stage */
|
||||
if (cctx->rustSimpleCompress2Completed) {
|
||||
if (endOp == ZSTD_e_end
|
||||
&& cctx->streamStage == zcss_init
|
||||
&& cctx->pledgedSrcSizePlusOne == 0
|
||||
&& ZSTD_rust_simpleCompress2Level(cctx) != (-2147483647 - 1)) {
|
||||
void* const dst = output->dst ?
|
||||
(char*)output->dst + output->pos : output->dst;
|
||||
const void* const src = input->src ?
|
||||
(const char*)input->src + input->pos : input->src;
|
||||
size_t const result = ZSTD_compress2(
|
||||
cctx, dst, output->size - output->pos,
|
||||
src, input->size - input->pos);
|
||||
cctx->rustSimpleCompress2Completed = 0;
|
||||
if (!ZSTD_isError(result)) {
|
||||
output->pos += result;
|
||||
input->pos = input->size;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* A different streaming directive or an advanced parameter means the
|
||||
* frame can no longer reuse the one-shot Rust result. */
|
||||
cctx->rustSimpleCompress2Completed = 0;
|
||||
}
|
||||
|
||||
if (cctx->streamStage == zcss_init) {
|
||||
size_t const inputSize = input->size - input->pos; /* no obligation to start from pos==0 */
|
||||
size_t const totalInputSize = inputSize + cctx->stableIn_notConsumed;
|
||||
@@ -5526,9 +5648,9 @@ size_t ZSTD_compressStream2_simpleArgs (
|
||||
}
|
||||
}
|
||||
|
||||
size_t ZSTD_compress2(ZSTD_CCtx* cctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize)
|
||||
size_t ZSTD_compress2_c(ZSTD_CCtx* cctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize)
|
||||
{
|
||||
ZSTD_bufferMode_e const originalInBufferMode = cctx->requestedParams.inBufferMode;
|
||||
ZSTD_bufferMode_e const originalOutBufferMode = cctx->requestedParams.outBufferMode;
|
||||
|
||||
@@ -516,6 +516,8 @@ struct ZSTD_CCtx_s {
|
||||
size_t outBuffFlushedSize;
|
||||
ZSTD_cStreamStage streamStage;
|
||||
U32 frameEnded;
|
||||
/* The next one-shot stream call may reuse a Rust-compressed frame. */
|
||||
unsigned rustSimpleCompress2Completed;
|
||||
|
||||
/* Stable in/out buffer verification */
|
||||
ZSTD_inBuffer expectedInBuffer;
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
//! already migrated compression leaves. `ZSTD_compressCCtx` uses the same
|
||||
//! path after a narrow C-owned context reset; the public context lifecycle
|
||||
//! and streaming APIs remain C-owned because they share the
|
||||
//! configuration-dependent private `ZSTD_CCtx_s` layout.
|
||||
//! configuration-dependent private `ZSTD_CCtx_s` layout. `ZSTD_compress2`
|
||||
//! dispatches simple, dictionary-free contexts through the same Rust frame
|
||||
//! path and retains the C implementation for advanced contexts. A following
|
||||
//! one-shot `ZSTD_compressStream2(..., ZSTD_e_end)` call can reuse that path.
|
||||
|
||||
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
|
||||
use crate::mem::MEM_writeLE24;
|
||||
@@ -29,6 +32,21 @@ use std::ptr;
|
||||
#[cfg(not(test))]
|
||||
unsafe extern "C" {
|
||||
fn ZSTD_rust_resetCCtxForSimpleCompression(cctx: *mut c_void) -> usize;
|
||||
fn ZSTD_rust_prepareCCtxForSimpleCompression(
|
||||
cctx: *mut c_void,
|
||||
src_size: usize,
|
||||
compression_level: c_int,
|
||||
) -> usize;
|
||||
fn ZSTD_rust_resetCCtxForSimpleCompressionSession(cctx: *mut c_void) -> usize;
|
||||
fn ZSTD_rust_markSimpleCompression2Complete(cctx: *mut c_void);
|
||||
fn ZSTD_rust_simpleCompress2Level(cctx: *const c_void) -> c_int;
|
||||
fn ZSTD_compress2_c(
|
||||
cctx: *mut c_void,
|
||||
dst: *mut c_void,
|
||||
dst_capacity: usize,
|
||||
src: *const c_void,
|
||||
src_size: usize,
|
||||
) -> usize;
|
||||
}
|
||||
|
||||
const ZSTD_FAST: c_int = 1;
|
||||
@@ -360,10 +378,61 @@ pub unsafe extern "C" fn ZSTD_compressCCtx(
|
||||
if ERR_isError(reset) {
|
||||
return reset;
|
||||
}
|
||||
let prepare =
|
||||
unsafe { ZSTD_rust_prepareCCtxForSimpleCompression(cctx, src_size, compression_level) };
|
||||
if ERR_isError(prepare) {
|
||||
return prepare;
|
||||
}
|
||||
}
|
||||
unsafe { compress_frame(dst, dst_capacity, src, src_size, compression_level) }
|
||||
}
|
||||
|
||||
/// Stateful compression entry point during the context migration.
|
||||
///
|
||||
/// A context with only the ordinary frame settings is reset by the C shim and
|
||||
/// compressed through the Rust frame path. Contexts using dictionaries,
|
||||
/// checksums, target-sized blocks, sequence collection, or other advanced
|
||||
/// state still use the renamed C implementation until their state is moved.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTD_compress2(
|
||||
cctx: *mut c_void,
|
||||
dst: *mut c_void,
|
||||
dst_capacity: usize,
|
||||
src: *const c_void,
|
||||
src_size: usize,
|
||||
) -> usize {
|
||||
if cctx.is_null() {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
unsafe { compress_frame(dst, dst_capacity, src, src_size, 3) }
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
let level = unsafe { ZSTD_rust_simpleCompress2Level(cctx.cast_const()) };
|
||||
if level != c_int::MIN {
|
||||
let reset = unsafe { ZSTD_rust_resetCCtxForSimpleCompressionSession(cctx) };
|
||||
if ERR_isError(reset) {
|
||||
return reset;
|
||||
}
|
||||
let prepare =
|
||||
unsafe { ZSTD_rust_prepareCCtxForSimpleCompression(cctx, src_size, level) };
|
||||
if ERR_isError(prepare) {
|
||||
return prepare;
|
||||
}
|
||||
let result = unsafe { compress_frame(dst, dst_capacity, src, src_size, level) };
|
||||
if !ERR_isError(result) {
|
||||
unsafe { ZSTD_rust_markSimpleCompression2Complete(cctx) };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
unsafe { ZSTD_compress2_c(cctx, dst, dst_capacity, src, src_size) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user