Files
zstd-rs/rust/src/zstd_compress.rs
T
ddidderr 5d91f86a34 feat(compress): split static CCtx construction callbacks
Move static CCtx construction sequencing out of the monolithic C leaf. Rust
now validates the existing public workspace contract, sequences workspace
creation, CCtx and auxiliary reservations, publication, and BMI2 setup, and
short-circuits callback-reported failures. C callbacks retain the private
cwksp and CCtx layouts plus the native workspace-size and CPU-feature rules.

Add matching ABI layout assertions and focused callback-order, reservation-
failure, workspace-capacity, and NULL-path unit coverage. Static-CDict and
heap-CCtx paths remain unchanged.

Test Plan:
- `rustfmt --edition 2021 --check rust/src/zstd_compress.rs` -- passed
- GCC and Clang `-fsyntax-only` checks on `zstd_compress.c` -- passed
- GCC syntax checks with `ZSTD_DISABLE_ASM=1` and
  `ZSTD_ADDRESS_SANITIZER=1` -- passed
- `git diff --check` -- passed
- Cargo, make, native suites, and fuzzers not run per request
2026-07-20 16:00:30 +02:00

20403 lines
704 KiB
Rust

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::too_many_arguments)]
//! First high-level compression slice.
//!
//! The layout-independent one-shot entry point in this module drives the
//! already migrated compression leaves. `ZSTD_compressCCtx` uses the same
//! path after a narrow C-owned context reset; public context allocation now
//! runs through a Rust-owned boundary while C retains configuration-dependent
//! private `ZSTD_CCtx_s` initialization. `ZSTD_compress2` and the complete-input simple
//! `ZSTD_compressStream2(..., ZSTD_e_end)` path dispatch through Rust while
//! retaining the C implementation for advanced and partial-stream cases.
use crate::common::MINMATCH;
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
use crate::mem::MEM_writeLE32;
#[cfg(test)]
use crate::xxhash::XXH64_reset;
use crate::xxhash::{XXH64_digest, XXH64_state_t, XXH64_update};
#[cfg(not(test))]
use crate::zstd_compress_api::ZSTD_compressBound;
use crate::zstd_compress_block_split::ZSTD_rust_deriveBlockSplits;
use crate::zstd_compress_frame::{
write_raw_block, ZSTD_rust_noCompressBlock, ZSTD_rust_rleCompressBlock,
ZSTD_rust_writeBlockHeader, ZSTD_rust_writeEpilogue, ZSTD_rust_writeFrameHeader,
ZSTD_writeLastEmptyBlock,
};
use crate::zstd_compress_literals::min_gain;
use crate::zstd_compress_params::{
ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters, ZSTD_resolveMaxBlockSize,
ZSTD_rustMatchStateSizing, ZSTD_rust_params_adjustCParams, ZSTD_rust_params_allocateChainTable,
ZSTD_rust_params_checkCParams, ZSTD_rust_params_defaultCLevel,
ZSTD_rust_params_estimateMatchStateSize, ZSTD_rust_params_getParamsInternal,
ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_rowMatchFinderUsed,
ZSTD_rust_params_selectBlockCompressor, ZSTD_rust_params_selectCParams,
ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE, ZSTD_RUST_PS_ENABLE,
};
use crate::zstd_compress_params_api::{
ZSTD_CCtxParams_setParameter, ZSTD_CCtx_params, ZSTD_customMem, ZSTD_rust_isUpdateAuthorized,
};
use crate::zstd_compress_sequences::SeqDef;
use crate::zstd_compress_stats::{
update_rep, SeqCollector, SeqStore_t, ZSTD_Sequence, ZSTD_SequencePosition,
ZSTD_compressedBlockState_t, ZSTD_entropyCTablesMetadata_t, ZSTD_entropyCTables_t,
ZSTD_rust_confirmRepcodesAndEntropyTables, ZSTD_rust_convertSequencesNoRepcodes,
ZSTD_rust_copyBlockSequences, ZSTD_rust_countSeqStoreLiteralsBytes,
ZSTD_rust_countSeqStoreMatchBytes, ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize,
ZSTD_rust_entropyCompressSeqStore, ZSTD_rust_entropyCompressSeqStore_internal,
ZSTD_rust_fastSequenceLengthSum, ZSTD_rust_finalizeOffBase, ZSTD_rust_get1BlockSummary,
ZSTD_rust_isRLE, ZSTD_rust_maybeRLE, ZSTD_rust_postProcessSequenceProducerResult,
ZSTD_rust_resetCompressedBlockState, ZSTD_rust_resetSeqStore,
ZSTD_rust_seqStore_resolveOffCodes, ZSTD_rust_storeLastLiterals,
ZSTD_rust_transferSequencesNoDelim, ZSTD_rust_transferSequencesWBlockDelim,
ZSTD_rust_validateSeqStore, ZSTD_LLT_LITERAL_LENGTH, ZSTD_LLT_MATCH_LENGTH,
};
use crate::zstd_compress_superblock::ZSTD_rust_compressSuperBlock;
use std::ffi::c_void;
use std::mem::{offset_of, size_of, MaybeUninit};
use std::os::raw::{c_int, c_longlong, c_uint};
use std::ptr;
#[cfg(not(test))]
unsafe extern "C" {
fn ZSTD_createCCtx() -> *mut c_void;
fn ZSTD_freeCCtx(cctx: *mut c_void) -> usize;
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, src_size: usize) -> c_int;
fn ZSTD_compress_usingDict(
cctx: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
compression_level: c_int,
) -> usize;
fn ZSTD_rust_simpleCompressStream2Level(cctx: *const c_void, src_size: usize) -> 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;
fn ZSTD_compressStream2_c(
cctx: *mut c_void,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
end_op: c_int,
) -> usize;
}
const ZSTD_FAST: c_int = 1;
const ZSTD_DFAST: c_int = 2;
const ZSTD_BTLAZY2: c_int = 6;
const ZSTD_REP_NUM: usize = 3;
const ZSTD_BM_BUFFERED: c_int = 0;
const ZSTD_BM_STABLE: c_int = 1;
const ZSTD_SF_NO_BLOCK_DELIMITERS: c_int = 0;
const ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS: c_int = 1;
const ZSTD_BLOCKSIZE_MAX: usize = 1 << 17;
const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX;
const ZSTD_TARGET_CBLOCK_BSS_COMPRESS: c_int = 0;
const ZSTD_BLOCK_HEADER_SIZE: usize = 3;
const MIN_CBLOCK_SIZE: usize = 2;
const MIN_COMPRESSIBLE_BLOCK_SIZE: usize = MIN_CBLOCK_SIZE + ZSTD_BLOCK_HEADER_SIZE + 1 + 1;
const ZSTD_ROWSIZE: usize = 16;
const ZSTD_CSTREAM_STAGE_INIT: c_int = 0;
/// Scalar stream state used to select the complete-input fast path. The C
/// context remains opaque; its private fields are projected before Rust
/// evaluates the eligibility predicate.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rust_simpleCompressStream2Projection {
stream_stage: c_int,
pledged_src_size_plus_one: u64,
rust_simple_compress2_completed: c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_simpleCompressStream2Projection, stream_stage) == 0);
assert!(
offset_of!(
ZSTD_rust_simpleCompressStream2Projection,
pledged_src_size_plus_one
) == if size_of::<usize>() == 8 { 8 } else { 4 }
);
assert!(
offset_of!(
ZSTD_rust_simpleCompressStream2Projection,
rust_simple_compress2_completed
) == if size_of::<usize>() == 8 { 16 } else { 12 }
);
assert!(
size_of::<ZSTD_rust_simpleCompressStream2Projection>()
== if size_of::<usize>() == 8 { 24 } else { 16 }
);
};
pub type ZSTD_rust_simpleCompress2LevelFn = unsafe extern "C" fn(*const c_void, usize) -> c_int;
const ZSTD_WINDOW_START_INDEX: u32 = 2;
const ZSTD_DUBT_UNSORTED_MARK: u32 = 1;
const ZSTD_INDEXOVERFLOW_MARGIN: usize = 16usize << 20;
const ZSTD_SHORT_CACHE_TAG_BITS: u32 = 8;
const ZSTD_CURRENT_MAX: usize = if size_of::<usize>() == 8 {
3500usize << 20
} else {
2000usize << 20
};
const ZSTD_CHUNKSIZE_MAX: usize = u32::MAX as usize - ZSTD_CURRENT_MAX;
const ZSTD_E_END: c_int = 2;
const ZSTD_E_CONTINUE: c_int = 0;
const ZSTD_E_FLUSH: c_int = 1;
const ZSTD_C_WINDOW_LOG: c_int = 101;
const ZSTD_C_HASH_LOG: c_int = 102;
const ZSTD_C_CHAIN_LOG: c_int = 103;
const ZSTD_C_SEARCH_LOG: c_int = 104;
const ZSTD_C_MIN_MATCH: c_int = 105;
const ZSTD_C_TARGET_LENGTH: c_int = 106;
const ZSTD_C_STRATEGY: c_int = 107;
const ZSTD_C_FORMAT: c_int = 10;
const ZSTD_C_COMPRESSION_LEVEL: c_int = 100;
const ZSTD_C_TARGET_C_BLOCK_SIZE: c_int = 130;
const ZSTD_C_ENABLE_LDM: c_int = 160;
const ZSTD_C_LDM_HASH_LOG: c_int = 161;
const ZSTD_C_LDM_MIN_MATCH: c_int = 162;
const ZSTD_C_LDM_BUCKET_SIZE_LOG: c_int = 163;
const ZSTD_C_LDM_HASH_RATE_LOG: c_int = 164;
const ZSTD_C_CONTENT_SIZE_FLAG: c_int = 200;
const ZSTD_C_CHECKSUM_FLAG: c_int = 201;
const ZSTD_C_DICT_ID_FLAG: c_int = 202;
const ZSTD_C_NB_WORKERS: c_int = 400;
const ZSTD_C_JOB_SIZE: c_int = 401;
const ZSTD_C_OVERLAP_LOG: c_int = 402;
const ZSTD_C_RSYNCABLE: c_int = 500;
const ZSTD_C_FORCE_MAX_WINDOW: c_int = 1000;
const ZSTD_C_FORCE_ATTACH_DICT: c_int = 1001;
const ZSTD_C_LITERAL_COMPRESSION_MODE: c_int = 1002;
const ZSTD_C_SRC_SIZE_HINT: c_int = 1004;
const ZSTD_C_ENABLE_DEDICATED_DICT_SEARCH: c_int = 1005;
const ZSTD_C_STABLE_IN_BUFFER: c_int = 1006;
const ZSTD_C_STABLE_OUT_BUFFER: c_int = 1007;
const ZSTD_C_BLOCK_DELIMITERS: c_int = 1008;
const ZSTD_C_VALIDATE_SEQUENCES: c_int = 1009;
const ZSTD_C_SPLIT_AFTER_SEQUENCES: c_int = 1010;
const ZSTD_C_USE_ROW_MATCH_FINDER: c_int = 1011;
const ZSTD_C_DETERMINISTIC_REF_PREFIX: c_int = 1012;
const ZSTD_C_PREFETCH_CDICT_TABLES: c_int = 1013;
const ZSTD_C_ENABLE_SEQ_PRODUCER_FALLBACK: c_int = 1014;
const ZSTD_C_MAX_BLOCK_SIZE: c_int = 1015;
const ZSTD_C_REPCODE_RESOLUTION: c_int = 1016;
const ZSTD_C_BLOCK_SPLITTER_LEVEL: c_int = 1017;
const ZSTD_RESET_SESSION_ONLY: c_int = 1;
const ZSTD_RESET_PARAMETERS: c_int = 2;
const ZSTD_RESET_SESSION_AND_PARAMETERS: c_int = 3;
const ZSTD_CSTREAM_STAGE_LOAD: c_int = 1;
const ZSTD_CSTREAM_STAGE_FLUSH: c_int = 2;
const ZSTD_BSS_COMPRESS: c_int = 0;
const ZSTD_BSS_NO_COMPRESS: c_int = 1;
const FSE_REPEAT_CHECK: c_int = 1;
const FSE_REPEAT_VALID: c_int = 2;
type FrameChunkPrepareOverflowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
type FrameChunkPrepareWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, c_uint);
type FrameChunkCompressFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize;
type BuildSeqStoreSkipFn = unsafe extern "C" fn(*mut c_void, usize);
type BuildSeqStorePrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
type BuildSeqStoreCompressFn =
unsafe extern "C" fn(*mut c_void, *mut SeqStore_t, *mut u32, *const c_void, usize) -> usize;
type BuildSeqStoreTryExternalProducerFn =
unsafe extern "C" fn(*mut c_void, *const c_void, usize, *mut c_int, *mut c_int) -> usize;
type BuildSeqStoreClearLdmFn = unsafe extern "C" fn(*mut c_void);
type BlockCompressorFn =
unsafe extern "C" fn(*mut c_void, *mut SeqStore_t, *mut u32, *const c_void, usize) -> usize;
const ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH: usize = 10;
const ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH: usize = 3;
const ZSTD_RUST_DICT_MODE_COUNT: c_int = 4;
const ZSTD_RUST_BLOCK_COMPRESSOR_MAX_STRATEGY: c_int =
ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH as c_int - 1;
/// C supplies the configuration-dependent compressor tables and their private
/// match-state leaves. Rust owns the strategy/row-mode index and dictionary
/// mode dispatch into those tables.
#[repr(C)]
pub struct ZSTD_rust_selectBlockCompressorState {
block_compressors: *const Option<BlockCompressorFn>,
row_block_compressors: *const Option<BlockCompressorFn>,
selected_compressor: *mut Option<BlockCompressorFn>,
}
const _: () = {
assert!(size_of::<BlockCompressorFn>() == size_of::<usize>());
assert!(size_of::<Option<BlockCompressorFn>>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_selectBlockCompressorState, block_compressors) == 0);
assert!(
offset_of!(ZSTD_rust_selectBlockCompressorState, row_block_compressors)
== size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_selectBlockCompressorState, selected_compressor)
== 2 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_selectBlockCompressorState>() == 3 * size_of::<usize>());
};
/// Select one C-owned match-state compressor while keeping the table policy
/// and dispatch-index arithmetic in Rust.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_selectBlockCompressor(
state: *const ZSTD_rust_selectBlockCompressorState,
strategy: c_int,
use_row_match_finder: c_int,
dict_mode: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.block_compressors.is_null()
|| state.row_block_compressors.is_null()
|| state.selected_compressor.is_null()
|| !(ZSTD_FAST..=ZSTD_RUST_BLOCK_COMPRESSOR_MAX_STRATEGY).contains(&strategy)
|| !(0..ZSTD_RUST_DICT_MODE_COUNT).contains(&dict_mode)
{
return ERROR(ZstdErrorCode::Generic);
}
unsafe { *state.selected_compressor = None };
let selected_index = ZSTD_rust_params_selectBlockCompressor(strategy, use_row_match_finder);
let (table, table_width, table_index) = if selected_index < 3 {
(
state.row_block_compressors,
ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH,
selected_index,
)
} else {
(
state.block_compressors,
ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH,
selected_index - 3,
)
};
if table_index < 0 {
return ERROR(ZstdErrorCode::Generic);
}
let callback = unsafe { *table.add(dict_mode as usize * table_width + table_index as usize) };
let Some(callback) = callback else {
return ERROR(ZstdErrorCode::Generic);
};
unsafe { *state.selected_compressor = Some(callback) };
0
}
/// Explicit projection for the sequence-store builder.
///
/// Rust owns the threshold/reset/repcode/literal-store orchestration. The
/// callbacks retain the private matchfinder, LDM, and external-producer
/// fallback operations in C without passing `ZSTD_CCtx` across the ABI.
#[repr(C)]
pub struct ZSTD_rust_buildSeqStoreState {
seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
callback_context: *mut c_void,
min_match: c_uint,
validate_seq_store: c_int,
skip_small_block: BuildSeqStoreSkipFn,
prepare_match_state: BuildSeqStorePrepareFn,
has_external_sequences: c_int,
ldm_enabled: c_int,
has_external_sequence_producer: c_int,
enable_match_finder_fallback: c_int,
compress_external_sequences: BuildSeqStoreCompressFn,
compress_ldm: BuildSeqStoreCompressFn,
try_external_sequence_producer: BuildSeqStoreTryExternalProducerFn,
compress_internal: BuildSeqStoreCompressFn,
clear_ldm_seq_store: BuildSeqStoreClearLdmFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_buildSeqStoreState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_buildSeqStoreState, prev_c_block) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_buildSeqStoreState, next_c_block) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_buildSeqStoreState, callback_context) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_buildSeqStoreState, min_match) == 4 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, validate_seq_store)
== 4 * size_of::<usize>() + size_of::<c_uint>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, skip_small_block)
== 4 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, prepare_match_state)
== 5 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, has_external_sequences)
== 6 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, ldm_enabled)
== 6 * size_of::<usize>() + size_of::<c_uint>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, has_external_sequence_producer)
== 6 * size_of::<usize>() + size_of::<c_uint>() + 3 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, enable_match_finder_fallback)
== 6 * size_of::<usize>() + size_of::<c_uint>() + 4 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, compress_external_sequences)
== 6 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, compress_ldm)
== 7 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, try_external_sequence_producer)
== size_of::<[usize; 8]>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, compress_internal)
== 9 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_buildSeqStoreState, clear_ldm_seq_store)
== 10 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_buildSeqStoreState>()
== 11 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
);
};
type ExternalSequenceProducerFn = unsafe extern "C" fn(
*mut c_void,
*mut ZSTD_Sequence,
usize,
*const c_void,
usize,
*const c_void,
usize,
c_int,
usize,
) -> usize;
type ExternalSequenceTransferFn = unsafe extern "C" fn(
*mut c_void,
*mut ZSTD_SequencePosition,
*const ZSTD_Sequence,
usize,
*const c_void,
usize,
) -> usize;
/// Projection for the successful block-level external sequence-producer path.
///
/// Rust owns producer invocation, result post-processing, length validation,
/// and transfer ordering. The transfer callback retains the private CCtx
/// match-state projection; C owns fallback selection after this leaf returns.
#[repr(C)]
pub struct ZSTD_rust_externalSequenceProducerState {
callback_context: *mut c_void,
producer_state: *mut c_void,
producer: Option<ExternalSequenceProducerFn>,
ext_seq_buf: *mut ZSTD_Sequence,
ext_seq_buf_capacity: *const usize,
src: *const c_void,
src_size: *const usize,
compression_level: *const c_int,
window_size: *const usize,
transfer: Option<ExternalSequenceTransferFn>,
external_seq_count: *mut usize,
seq_store_complete: *mut c_int,
allow_fallback: *mut c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_externalSequenceProducerState, callback_context) == 0);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, producer_state) == size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, producer) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, ext_seq_buf) == 3 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_externalSequenceProducerState,
ext_seq_buf_capacity
) == 4 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_externalSequenceProducerState, src) == 5 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, src_size) == 6 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, compression_level)
== 7 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, window_size) == size_of::<[usize; 8]>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, transfer) == 9 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, external_seq_count)
== 10 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, seq_store_complete)
== 11 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_externalSequenceProducerState, allow_fallback)
== 12 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_externalSequenceProducerState>() == 13 * size_of::<usize>());
};
/// Try an external sequence producer; C retains only the fallback decision.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_tryExternalSequenceProducer(
state: *const ZSTD_rust_externalSequenceProducerState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(producer) = state.producer else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(transfer) = state.transfer else {
return ERROR(ZstdErrorCode::Generic);
};
if state.ext_seq_buf.is_null()
|| state.ext_seq_buf_capacity.is_null()
|| state.src.is_null()
|| state.src_size.is_null()
|| state.compression_level.is_null()
|| state.window_size.is_null()
|| state.external_seq_count.is_null()
|| state.seq_store_complete.is_null()
|| state.allow_fallback.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
*state.seq_store_complete = 0;
*state.allow_fallback = 0;
}
let ext_seq_buf_capacity = unsafe { *state.ext_seq_buf_capacity };
let src_size = unsafe { *state.src_size };
let nb_external_seqs = unsafe {
producer(
state.producer_state,
state.ext_seq_buf,
ext_seq_buf_capacity,
state.src,
src_size,
ptr::null(),
0,
*state.compression_level,
*state.window_size,
)
};
unsafe { *state.external_seq_count = nb_external_seqs };
let nb_post_processed_seqs = unsafe {
ZSTD_rust_postProcessSequenceProducerResult(
state.ext_seq_buf,
nb_external_seqs,
ext_seq_buf_capacity,
src_size,
)
};
if ERR_isError(nb_post_processed_seqs) {
unsafe { *state.allow_fallback = 1 };
return nb_post_processed_seqs;
}
let seq_len_sum =
unsafe { ZSTD_rust_fastSequenceLengthSum(state.ext_seq_buf, nb_post_processed_seqs) };
if seq_len_sum > src_size {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
let mut seq_pos = ZSTD_SequencePosition::default();
let transfer_result = unsafe {
transfer(
state.callback_context,
&mut seq_pos,
state.ext_seq_buf,
nb_post_processed_seqs,
state.src,
src_size,
)
};
if ERR_isError(transfer_result) {
return transfer_result;
}
unsafe { *state.seq_store_complete = 1 };
0
}
/// Reset the externally referenced raw-sequence store without exposing its C
/// representation to Rust.
#[repr(C)]
pub struct ZSTD_rust_externalSequenceStoreState {
seq: *mut *mut c_void,
pos: *mut usize,
pos_in_sequence: *mut usize,
size: *mut usize,
capacity: *mut usize,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_externalSequenceStoreState, seq) == 0);
assert!(offset_of!(ZSTD_rust_externalSequenceStoreState, pos) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_externalSequenceStoreState, pos_in_sequence) == 2 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_externalSequenceStoreState, size) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_externalSequenceStoreState, capacity) == 4 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_externalSequenceStoreState>() == 5 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_referenceExternalSequences(
state: *const ZSTD_rust_externalSequenceStoreState,
seq: *mut c_void,
nb_seq: usize,
) {
let Some(state) = state.as_ref() else {
return;
};
if state.seq.is_null()
|| state.pos.is_null()
|| state.pos_in_sequence.is_null()
|| state.size.is_null()
|| state.capacity.is_null()
{
return;
}
unsafe {
*state.seq = seq;
*state.size = nb_seq;
*state.capacity = nb_seq;
*state.pos = 0;
*state.pos_in_sequence = 0;
}
}
/// C-owned operations used by Rust's frame-chunk block-preparation policy.
///
/// The callback context and private window/workspace layout remain in C; Rust
/// controls the order in which these operations run.
#[repr(C)]
pub struct ZSTD_rust_frameChunkPrepareState {
callback_context: *mut c_void,
max_dist: c_uint,
correct_overflow: FrameChunkPrepareOverflowFn,
check_dict_validity: FrameChunkPrepareWindowFn,
enforce_max_dist: FrameChunkPrepareWindowFn,
clamp_state: *const ZSTD_rust_frameChunkClampState,
}
#[repr(C)]
pub struct ZSTD_rust_frameChunkClampState {
next_to_update: *mut c_uint,
low_limit: *const c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_frameChunkClampState, next_to_update) == 0);
assert!(offset_of!(ZSTD_rust_frameChunkClampState, low_limit) == size_of::<usize>());
assert!(size_of::<ZSTD_rust_frameChunkClampState>() == 2 * size_of::<usize>());
};
const _: () = {
assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, max_dist) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_frameChunkPrepareState, correct_overflow) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkPrepareState, check_dict_validity) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkPrepareState, enforce_max_dist) == 4 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, clamp_state) == 5 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_frameChunkPrepareState>() == 6 * size_of::<usize>());
};
/// Projected inputs shared by the frame-chunk internal block path and the
/// deprecated block adapter. Sequence-store construction and the private
/// compressed-block storage remain behind these two C-owned projections.
#[repr(C)]
pub struct ZSTD_rust_compressContinueBlockState {
build_seq_store_state: *const ZSTD_rust_buildSeqStoreState,
block_internal_state: *const ZSTD_rust_blockInternalState,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressContinueBlockState, build_seq_store_state) == 0);
assert!(
offset_of!(ZSTD_rust_compressContinueBlockState, block_internal_state)
== size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressContinueBlockState>() == 2 * size_of::<usize>());
};
/// Explicit projection of the state used by the block-split estimator.
///
/// C retains the private block-split context and owns projection assembly;
/// Rust owns the estimator implementation and receives only its required
/// sequence stores, entropy tables, parameters, and workspace.
#[repr(C)]
pub struct ZSTD_rust_deriveBlockSplitsState {
original_seq_store: *const SeqStore_t,
full_seq_store_chunk: *mut SeqStore_t,
first_half_seq_store: *mut SeqStore_t,
second_half_seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
entropy_metadata: *mut ZSTD_entropyCTablesMetadata_t,
workspace: *mut c_void,
workspace_size: usize,
strategy: c_int,
disable_literal_compression: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, original_seq_store) == 0);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, full_seq_store_chunk) == size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, first_half_seq_store)
== 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, second_half_seq_store)
== 3 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, prev_c_block) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, next_c_block) == 5 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, entropy_metadata) == 6 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, workspace) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, workspace_size) == usize::BITS as usize);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, strategy)
== usize::BITS as usize + size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_deriveBlockSplitsState,
disable_literal_compression
) == usize::BITS as usize + size_of::<usize>() + size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_deriveBlockSplitsState>()
== usize::BITS as usize + size_of::<usize>() + 2 * size_of::<c_int>()
);
};
/// Explicit projection of the state used by `ZSTD_compress_frameChunk`.
///
/// The Rust side owns the per-frame block loop and its savings/dispatch
/// policy. The callback context is opaque to Rust and is only returned to
/// C-owned callbacks, which retain private window, workspace, and CCtx
/// layout-sensitive operations.
#[repr(C)]
pub struct ZSTD_rust_frameChunkState {
callback_context: *mut c_void,
tmp_workspace: *mut c_void,
checksum_state: *mut XXH64_state_t,
is_first_block: *mut c_int,
stage: *mut c_int,
tmp_wksp_size: usize,
block_size_max: usize,
savings: c_longlong,
pre_block_splitter_level: c_int,
strategy: c_int,
use_target_c_block_size: c_int,
block_splitter_enabled: c_int,
checksum_flag: c_int,
ending_stage: c_int,
prepare_state: *const ZSTD_rust_frameChunkPrepareState,
compress_target: Option<FrameChunkCompressFn>,
compress_split: Option<FrameChunkCompressFn>,
compress_internal: Option<FrameChunkCompressFn>,
compress_internal_state: *const ZSTD_rust_compressContinueBlockState,
compress_target_state: *const ZSTD_rust_targetCBlockSizeState,
compress_split_state: *const ZSTD_rust_splitBlockState,
derive_block_splits_state: *const ZSTD_rust_deriveBlockSplitsState,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_frameChunkState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_frameChunkState, tmp_workspace) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_frameChunkState, checksum_state) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_frameChunkState, is_first_block) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_frameChunkState, stage) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_frameChunkState, tmp_wksp_size) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_frameChunkState, block_size_max) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_frameChunkState, savings) == 7 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_frameChunkState, pre_block_splitter_level)
== 7 * size_of::<usize>() + size_of::<c_longlong>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, strategy)
== 7 * size_of::<usize>() + size_of::<c_longlong>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, use_target_c_block_size)
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, block_splitter_enabled)
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 3 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, checksum_flag)
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 4 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, ending_stage)
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 5 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, prepare_state)
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, compress_internal_state)
== 11 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, compress_target_state)
== 12 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, compress_split_state)
== 13 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, derive_block_splits_state)
== 14 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_frameChunkState>()
== 15 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
};
/// Rust implementation of `ZSTD_compress_frameChunk`.
///
/// C still owns the private match-state/window operations and invokes the
/// selected block body through callbacks. Rust owns preparation ordering, the
/// block-size heuristic call, output framing, savings accounting, checksum
/// sequencing, and frame-state update.
#[inline]
unsafe fn prepare_frame_chunk_block(
state: &ZSTD_rust_frameChunkPrepareState,
src: *const c_void,
block_size: usize,
) {
unsafe { (state.correct_overflow)(state.callback_context, src, block_size) };
unsafe { (state.check_dict_validity)(state.callback_context, src, block_size, state.max_dist) };
unsafe { (state.enforce_max_dist)(state.callback_context, src, block_size, state.max_dist) };
if state.clamp_state.is_null() {
return;
}
let clamp_state = unsafe { &*state.clamp_state };
if clamp_state.next_to_update.is_null() || clamp_state.low_limit.is_null() {
return;
}
unsafe {
/* Ensure hash/chain table insertion resumes no sooner than lowlimit. */
if *clamp_state.next_to_update < *clamp_state.low_limit {
*clamp_state.next_to_update = *clamp_state.low_limit;
}
}
}
#[allow(clippy::too_many_arguments)]
unsafe fn compress_frame_chunk_body_with(
state: &ZSTD_rust_frameChunkState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
last_frame_chunk: c_uint,
) -> usize {
if state.is_first_block.is_null() || state.stage.is_null() || state.prepare_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if state.checksum_flag != 0 && src_size != 0 {
if state.checksum_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
let _ = XXH64_update(state.checksum_state, src, src_size);
}
}
let mut remaining = src_size;
let mut ip = src.cast::<u8>();
let mut op = dst.cast::<u8>();
let mut remaining_capacity = dst_capacity;
let mut savings = state.savings;
let mut compressed_size_total = 0usize;
while remaining != 0 {
let block_size = unsafe {
crate::zstd_compress_frame::ZSTD_rust_optimalBlockSize(
ip.cast(),
remaining,
state.block_size_max,
state.pre_block_splitter_level,
state.strategy,
savings,
state.tmp_workspace,
state.tmp_wksp_size,
)
};
if block_size == 0 || block_size > remaining {
return ERROR(ZstdErrorCode::Generic);
}
let last_block = last_frame_chunk & u32::from(block_size == remaining);
/* Keep the original early capacity guard: even a raw block needs the
* minimum block header plus the minimum compressible payload budget. */
if remaining_capacity < ZSTD_BLOCK_HEADER_SIZE + MIN_CBLOCK_SIZE + 1 {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
unsafe { prepare_frame_chunk_block(&*state.prepare_state, ip.cast(), block_size) };
let c_size = if state.use_target_c_block_size != 0 {
if !state.compress_target_state.is_null() {
let block_state = if state.compress_internal_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
} else {
unsafe { &*state.compress_internal_state }
};
if block_state.build_seq_store_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let bss = unsafe {
ZSTD_rust_buildSeqStore(
block_state.build_seq_store_state,
ip.cast(),
block_size,
)
};
if ERR_isError(bss) {
return bss;
}
let mut target_state = unsafe { ptr::read(state.compress_target_state) };
target_state.is_first_block = unsafe { *state.is_first_block };
unsafe {
ZSTD_rust_compressBlockTargetCBlockSizeAfterBuild(
&target_state,
op.cast(),
remaining_capacity,
ip.cast(),
block_size,
bss as c_int,
last_block,
)
}
} else {
let Some(compress_target) = state.compress_target else {
return ERROR(ZstdErrorCode::Generic);
};
unsafe {
compress_target(
state.callback_context,
op.cast(),
remaining_capacity,
ip.cast(),
block_size,
last_block,
)
}
}
} else if state.block_splitter_enabled != 0 {
if !state.compress_split_state.is_null() {
let block_state = if state.compress_internal_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
} else {
unsafe { &*state.compress_internal_state }
};
let split_state = unsafe { &*state.compress_split_state };
if block_state.build_seq_store_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let bss = unsafe {
ZSTD_rust_buildSeqStore(
block_state.build_seq_store_state,
ip.cast(),
block_size,
)
};
if ERR_isError(bss) {
return bss;
}
let num_splits = if bss == ZSTD_BSS_COMPRESS as usize {
if split_state.seq_store.is_null() || split_state.partitions.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let sequence_store = unsafe { &*split_state.seq_store };
if sequence_store.sequencesStart.is_null() || sequence_store.sequences.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let sequence_count = unsafe {
sequence_store
.sequences
.offset_from(sequence_store.sequencesStart)
};
if sequence_count < 0 {
return ERROR(ZstdErrorCode::Generic);
}
if state.derive_block_splits_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let derive_state = unsafe { &*state.derive_block_splits_state };
if derive_state.original_seq_store.is_null()
|| derive_state.full_seq_store_chunk.is_null()
|| derive_state.first_half_seq_store.is_null()
|| derive_state.second_half_seq_store.is_null()
|| derive_state.prev_c_block.is_null()
|| derive_state.next_c_block.is_null()
|| derive_state.entropy_metadata.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *derive_state.prev_c_block };
let next_c_block = unsafe { *derive_state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let result = unsafe {
ZSTD_rust_deriveBlockSplits(
split_state.partitions.cast_mut(),
sequence_count as u32,
derive_state.original_seq_store,
derive_state.full_seq_store_chunk,
derive_state.first_half_seq_store,
derive_state.second_half_seq_store,
ptr::addr_of!((*prev_c_block).entropy),
ptr::addr_of_mut!((*next_c_block).entropy),
derive_state.strategy,
derive_state.disable_literal_compression,
derive_state.entropy_metadata,
derive_state.workspace,
derive_state.workspace_size,
)
};
if ERR_isError(result) {
return result;
}
result
} else {
0
};
let mut split_state = unsafe { ptr::read(state.compress_split_state) };
split_state.is_first_block = unsafe { *state.is_first_block };
unsafe {
ZSTD_rust_compressBlockSplitAfterBuild(
&split_state,
op.cast(),
remaining_capacity,
ip.cast(),
block_size,
last_block,
num_splits,
bss as c_int,
)
}
} else {
let Some(compress_split) = state.compress_split else {
return ERROR(ZstdErrorCode::Generic);
};
unsafe {
compress_split(
state.callback_context,
op.cast(),
remaining_capacity,
ip.cast(),
block_size,
last_block,
)
}
}
} else {
let compressed_size = if !state.compress_internal_state.is_null() {
unsafe {
ZSTD_rust_compressFrameChunkInternal(
state.compress_internal_state,
state.is_first_block,
op.add(ZSTD_BLOCK_HEADER_SIZE).cast(),
remaining_capacity - ZSTD_BLOCK_HEADER_SIZE,
ip.cast(),
block_size,
last_block,
)
}
} else {
let Some(compress_internal) = state.compress_internal else {
return ERROR(ZstdErrorCode::Generic);
};
unsafe {
compress_internal(
state.callback_context,
op.add(ZSTD_BLOCK_HEADER_SIZE).cast(),
remaining_capacity - ZSTD_BLOCK_HEADER_SIZE,
ip.cast(),
block_size,
last_block,
)
}
};
if ERR_isError(compressed_size) {
return compressed_size;
}
if compressed_size == 0 {
unsafe {
ZSTD_rust_noCompressBlock(
op.cast(),
remaining_capacity,
ip.cast(),
block_size,
last_block,
)
}
} else {
unsafe {
ZSTD_rust_writeBlockHeader(op.cast(), compressed_size, block_size, last_block)
};
compressed_size + ZSTD_BLOCK_HEADER_SIZE
}
};
if ERR_isError(c_size) {
return c_size;
}
savings =
savings.wrapping_add((block_size as c_longlong).wrapping_sub(c_size as c_longlong));
unsafe {
ip = ip.add(block_size);
op = op.add(c_size);
}
remaining -= block_size;
debug_assert!(c_size <= remaining_capacity);
remaining_capacity = remaining_capacity.wrapping_sub(c_size);
compressed_size_total = compressed_size_total.wrapping_add(c_size);
unsafe { *state.is_first_block = 0 };
}
if last_frame_chunk != 0 && compressed_size_total != 0 {
unsafe { *state.stage = state.ending_stage };
}
compressed_size_total
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressFrameChunk(
state: *const ZSTD_rust_frameChunkState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
last_frame_chunk: c_uint,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_frame_chunk_body_with(&*state, dst, dst_capacity, src, src_size, last_frame_chunk)
}
}
type CompressContinueBlockFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize;
/// Pointer projection for one C-owned `ZSTD_window_t` and the match-state
/// fields which follow its update. Each field points directly at the C
/// storage so later callbacks observe the updated window immediately.
#[repr(C)]
#[derive(Default)]
struct ZSTD_rust_compressContinueWindowProjection {
next_src: *mut *const c_void,
base: *mut *const c_void,
dict_base: *mut *const c_void,
dict_limit: *mut c_uint,
low_limit: *mut c_uint,
force_non_contiguous: *mut c_int,
next_to_update: *mut c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressContinueWindowProjection, next_src) == 0);
assert!(offset_of!(ZSTD_rust_compressContinueWindowProjection, base) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, dict_base) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, dict_limit)
== 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, low_limit) == 4 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressContinueWindowProjection,
force_non_contiguous
) == 5 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, next_to_update)
== 6 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressContinueWindowProjection>() == 7 * size_of::<usize>());
};
/// Explicit projection for the high-level continue and deprecated block APIs.
///
/// Rust owns stage transitions, frame-header sequencing, input progression,
/// and dispatch between the already-migrated frame-chunk/block bodies. The
/// frame-header parameters and frame-chunk state are projected explicitly.
/// The opaque callback context remains in C for overflow-correction callbacks
/// and context-sensitive block operations; window advancement is projected
/// directly into Rust.
#[repr(C)]
pub struct ZSTD_rust_compressContinueState {
callback_context: *mut c_void,
window_state: *const ZSTD_rust_compressContinueWindowProjection,
ldm_window_state: *const ZSTD_rust_compressContinueWindowProjection,
overflow_state: *const ZSTD_rust_overflowCorrectState,
frame_chunk_state: *const ZSTD_rust_frameChunkState,
compress_block: CompressContinueBlockFn,
stage: *mut c_int,
consumed_src_size: *mut u64,
produced_c_size: *mut u64,
pledged_src_size_plus_one: u64,
block_size_max: usize,
check_block_size: c_int,
no_dict_id_flag: c_int,
checksum_flag: c_int,
content_size_flag: c_int,
format: c_int,
window_log: c_uint,
dict_id: c_uint,
ldm_enabled: c_int,
}
const COMPRESS_CONTINUE_SCALARS_OFFSET: usize =
size_of::<[usize; 9]>() + size_of::<u64>() + size_of::<usize>();
const _: () = {
assert!(offset_of!(ZSTD_rust_compressContinueState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressContinueState, window_state) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, ldm_window_state) == 2 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressContinueState, overflow_state) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, frame_chunk_state) == 4 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressContinueState, compress_block) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressContinueState, stage) == 6 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, consumed_src_size) == 7 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, produced_c_size)
== 8 * (usize::BITS as usize / 8)
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, pledged_src_size_plus_one)
== size_of::<[usize; 9]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, block_size_max)
== size_of::<[usize; 9]>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, check_block_size)
== COMPRESS_CONTINUE_SCALARS_OFFSET
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, no_dict_id_flag)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, checksum_flag)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 2]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, content_size_flag)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 3]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, format)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 4]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, window_log)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 5]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, dict_id)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 5]>() + size_of::<c_uint>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, ldm_enabled)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 5]>() + 2 * size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_compressContinueState>()
== if size_of::<usize>() == 8 { 120 } else { 80 }
);
};
const ZSTD_COMPRESSION_STAGE_CREATED: c_int = 0;
const ZSTD_COMPRESSION_STAGE_INIT: c_int = 1;
const ZSTD_COMPRESSION_STAGE_ONGOING: c_int = 2;
#[cfg(test)]
const ZSTD_COMPRESSION_STAGE_ENDING: c_int = 3;
type GenerateSequencesGetParameterFn =
unsafe extern "C" fn(*mut c_void, c_int, *mut c_int) -> usize;
type GenerateSequencesAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
type GenerateSequencesFreeFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
type GenerateSequencesSetCollectorFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_Sequence, usize) -> usize;
type GenerateSequencesCompress2Fn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize) -> usize;
type GenerateSequencesGetCountFn = unsafe extern "C" fn(*mut c_void) -> usize;
/// C-owned context projection for `ZSTD_generateSequences()`.
///
/// Rust owns the public policy and temporary-buffer lifetime. C callbacks keep
/// the private `ZSTD_CCtx` and `SeqCollector` layouts out of this module.
#[repr(C)]
pub struct ZSTD_rust_generateSequencesState {
callback_context: *mut c_void,
get_parameter: GenerateSequencesGetParameterFn,
allocate: GenerateSequencesAllocateFn,
free: GenerateSequencesFreeFn,
set_collector: GenerateSequencesSetCollectorFn,
compress2: GenerateSequencesCompress2Fn,
get_count: GenerateSequencesGetCountFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_generateSequencesState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_generateSequencesState, get_parameter) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_generateSequencesState, allocate) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_generateSequencesState, free) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_generateSequencesState, set_collector) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_generateSequencesState, compress2) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_generateSequencesState, get_count) == 6 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_generateSequencesState>() == 7 * size_of::<usize>());
};
/// Own the validation, allocation, collection setup, compression call, and
/// cleanup ordering for the public sequence-generation API.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_generateSequences(
state: *const ZSTD_rust_generateSequencesState,
out_seqs: *mut ZSTD_Sequence,
out_seqs_size: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let dst_capacity = crate::zstd_compress_api::ZSTD_compressBound(src_size);
let mut parameter = 0;
let result = unsafe {
(state.get_parameter)(
state.callback_context,
ZSTD_C_TARGET_C_BLOCK_SIZE,
&mut parameter,
)
};
if ERR_isError(result) {
return result;
}
if parameter != 0 {
return ERROR(ZstdErrorCode::ParameterUnsupported);
}
let result =
unsafe { (state.get_parameter)(state.callback_context, ZSTD_C_NB_WORKERS, &mut parameter) };
if ERR_isError(result) {
return result;
}
if parameter != 0 {
return ERROR(ZstdErrorCode::ParameterUnsupported);
}
let dst = unsafe { (state.allocate)(state.callback_context, dst_capacity) };
if dst.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe { (state.set_collector)(state.callback_context, out_seqs, out_seqs_size) };
if ERR_isError(result) {
unsafe { (state.free)(state.callback_context, dst) };
return result;
}
let result =
unsafe { (state.compress2)(state.callback_context, dst, dst_capacity, src, src_size) };
unsafe { (state.free)(state.callback_context, dst) };
if ERR_isError(result) {
return result;
}
let sequence_count = unsafe { (state.get_count)(state.callback_context) };
debug_assert!(sequence_count <= crate::zstd_compress_api::ZSTD_sequenceBound(src_size));
sequence_count
}
#[inline]
unsafe fn compress_continue_block_body_with(
state: &ZSTD_rust_compressContinueBlockState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
frame: c_uint,
is_first_block: *const c_int,
) -> usize {
if state.build_seq_store_state.is_null() || state.block_internal_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let bss = unsafe { ZSTD_rust_buildSeqStore(state.build_seq_store_state, src, src_size) };
if ERR_isError(bss) {
return bss;
}
if !is_first_block.is_null() {
let mut block_state = unsafe { ptr::read(state.block_internal_state) };
block_state.is_first_block = unsafe { *is_first_block };
return unsafe {
ZSTD_rust_compressBlockInternalAfterBuild(
&block_state,
dst,
dst_capacity,
src,
src_size,
frame,
bss as c_int,
)
};
}
unsafe {
ZSTD_rust_compressBlockInternalAfterBuild(
state.block_internal_state,
dst,
dst_capacity,
src,
src_size,
frame,
bss as c_int,
)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressContinueBlock(
context: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
_last_frame_chunk: c_uint,
) -> usize {
if context.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*context.cast::<ZSTD_rust_compressContinueBlockState>() };
unsafe {
compress_continue_block_body_with(state, dst, dst_capacity, src, src_size, 0, ptr::null())
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressFrameChunkInternal(
state: *const ZSTD_rust_compressContinueBlockState,
is_first_block: *const c_int,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
_last_block: c_uint,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_continue_block_body_with(
&*state,
dst,
dst_capacity,
src,
src_size,
1,
is_first_block,
)
}
}
unsafe fn compress_continue_update_window(
projection: *const ZSTD_rust_compressContinueWindowProjection,
src: *const c_void,
src_size: usize,
force_non_contiguous: c_int,
update_match_state: bool,
) -> Result<(), usize> {
if projection.is_null() {
return Err(ERROR(ZstdErrorCode::Generic));
}
let projection = unsafe { &*projection };
if projection.next_src.is_null()
|| projection.base.is_null()
|| projection.dict_base.is_null()
|| projection.dict_limit.is_null()
|| projection.low_limit.is_null()
{
return Err(ERROR(ZstdErrorCode::Generic));
}
if update_match_state
&& (projection.force_non_contiguous.is_null() || projection.next_to_update.is_null())
{
return Err(ERROR(ZstdErrorCode::Generic));
}
let mut window_state = ZSTD_rust_windowUpdateState {
nextSrc: unsafe { *projection.next_src },
base: unsafe { *projection.base },
dictBase: unsafe { *projection.dict_base },
dictLimit: unsafe { *projection.dict_limit },
lowLimit: unsafe { *projection.low_limit },
};
let contiguous =
unsafe { ZSTD_rust_windowUpdate(&mut window_state, src, src_size, force_non_contiguous) };
unsafe {
*projection.next_src = window_state.nextSrc;
*projection.base = window_state.base;
*projection.dict_base = window_state.dictBase;
*projection.dict_limit = window_state.dictLimit;
*projection.low_limit = window_state.lowLimit;
}
if update_match_state && contiguous == 0 {
unsafe {
*projection.force_non_contiguous = 0;
*projection.next_to_update = window_state.dictLimit;
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
unsafe fn compress_continue_body_with(
state: &ZSTD_rust_compressContinueState,
mut dst: *mut c_void,
mut dst_capacity: usize,
src: *const c_void,
src_size: usize,
frame: c_uint,
last_frame_chunk: c_uint,
) -> usize {
if state.stage.is_null() || state.consumed_src_size.is_null() || state.produced_c_size.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
/* The deprecated block entry point performed this check before entering
* the old internal routine. Keep that ordering in the Rust orchestration. */
if state.check_block_size != 0 && src_size > state.block_size_max {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if unsafe { *state.stage } == ZSTD_COMPRESSION_STAGE_CREATED {
return ERROR(ZstdErrorCode::StageWrong);
}
let mut frame_header_size = 0usize;
if frame != 0 && unsafe { *state.stage } == ZSTD_COMPRESSION_STAGE_INIT {
frame_header_size = unsafe {
ZSTD_rust_writeFrameHeader(
dst,
dst_capacity,
state.no_dict_id_flag,
state.checksum_flag,
state.content_size_flag,
state.format,
state.window_log,
state.pledged_src_size_plus_one.wrapping_sub(1),
state.dict_id,
)
};
if ERR_isError(frame_header_size) {
return frame_header_size;
}
if frame_header_size > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
unsafe {
*state.stage = ZSTD_COMPRESSION_STAGE_ONGOING;
}
dst = unsafe { dst.cast::<u8>().add(frame_header_size).cast() };
dst_capacity -= frame_header_size;
}
if src_size == 0 {
return frame_header_size;
}
if state.window_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let match_window = unsafe { &*state.window_state };
if match_window.force_non_contiguous.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let force_non_contiguous = unsafe { *match_window.force_non_contiguous };
if let Err(result) = unsafe {
compress_continue_update_window(
state.window_state,
src,
src_size,
force_non_contiguous,
true,
)
} {
return result;
}
if state.ldm_enabled != 0 {
if let Err(result) = unsafe {
compress_continue_update_window(state.ldm_window_state, src, src_size, 0, false)
} {
return result;
}
}
if frame == 0 {
if state.overflow_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let src_end = src.cast::<u8>().wrapping_add(src_size).cast();
unsafe { ZSTD_rust_overflowCorrectIfNeeded(state.overflow_state, src, src_end) };
}
let compressed_size = if frame != 0 {
if state.frame_chunk_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
ZSTD_rust_compressFrameChunk(
state.frame_chunk_state,
dst,
dst_capacity,
src,
src_size,
last_frame_chunk,
)
}
} else {
unsafe {
(state.compress_block)(state.callback_context, dst, dst_capacity, src, src_size, 0)
}
};
if ERR_isError(compressed_size) {
return compressed_size;
}
let source_size_wrong = update_frame_progression(
unsafe { &mut *state.consumed_src_size },
unsafe { &mut *state.produced_c_size },
state.pledged_src_size_plus_one,
src_size,
compressed_size,
frame_header_size,
);
if state.pledged_src_size_plus_one != 0 && source_size_wrong {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
compressed_size.wrapping_add(frame_header_size)
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressContinue(
state: *const ZSTD_rust_compressContinueState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
frame: c_uint,
last_frame_chunk: c_uint,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_continue_body_with(
&*state,
dst,
dst_capacity,
src,
src_size,
frame,
last_frame_chunk,
)
}
}
type CompressEndTraceFn = unsafe extern "C" fn(*mut c_void, usize);
/// Explicit projection for the public end-of-frame orchestration.
///
/// Rust owns callback ordering, output offset/capacity accounting, and
/// pledged-size validation. The continue state is projected explicitly and
/// the opaque callback context retains only the private trace operation in C;
/// Rust serializes the epilogue from explicit frame and checksum projections.
#[repr(C)]
pub struct ZSTD_rust_compressEndState {
callback_context: *mut c_void,
compress_continue_state: *const ZSTD_rust_compressContinueState,
trace: CompressEndTraceFn,
consumed_src_size: *const u64,
pledged_src_size_plus_one: u64,
content_size_flag: c_int,
stage: *mut c_int,
no_dict_id_flag: c_int,
checksum_flag: c_int,
format: c_int,
window_log: c_uint,
checksum_state: *const XXH64_state_t,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressEndState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressEndState, compress_continue_state) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressEndState, trace) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressEndState, consumed_src_size) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressEndState, pledged_src_size_plus_one) == 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, content_size_flag)
== 4 * size_of::<usize>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, stage) == 5 * size_of::<usize>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, no_dict_id_flag)
== 6 * size_of::<usize>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, checksum_flag)
== 6 * size_of::<usize>() + size_of::<u64>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, format)
== 6 * size_of::<usize>() + size_of::<u64>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, window_log)
== 6 * size_of::<usize>() + size_of::<u64>() + 3 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, checksum_state)
== 6 * size_of::<usize>()
+ size_of::<u64>()
+ 3 * size_of::<c_int>()
+ size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_compressEndState>() == if size_of::<usize>() == 8 { 80 } else { 48 }
);
};
unsafe fn compress_end_body_with(
state: &ZSTD_rust_compressEndState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.compress_continue_state.is_null()
|| state.consumed_src_size.is_null()
|| state.stage.is_null()
|| state.checksum_state.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let c_size = unsafe {
ZSTD_rust_compressContinue(
state.compress_continue_state,
dst,
dst_capacity,
src,
src_size,
1,
1,
)
};
if ERR_isError(c_size) {
return c_size;
}
let checksum = if state.checksum_flag != 0 {
unsafe { XXH64_digest(state.checksum_state) as u32 }
} else {
0
};
let end_result = unsafe {
ZSTD_rust_writeEpilogue(
dst.cast::<u8>().add(c_size).cast(),
dst_capacity.wrapping_sub(c_size),
state.stage,
state.no_dict_id_flag,
state.checksum_flag,
state.content_size_flag,
state.format,
state.window_log,
checksum,
)
};
if ERR_isError(end_result) {
return end_result;
}
debug_assert!(!(state.content_size_flag != 0 && state.pledged_src_size_plus_one == 0));
if state.pledged_src_size_plus_one != 0
&& state.pledged_src_size_plus_one != unsafe { (*state.consumed_src_size).wrapping_add(1) }
{
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
unsafe { (state.trace)(state.callback_context, end_result) };
c_size.wrapping_add(end_result)
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressEnd(
state: *const ZSTD_rust_compressEndState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { compress_end_body_with(&*state, dst, dst_capacity, src, src_size) }
}
type Compress2ResetFn = unsafe extern "C" fn(*mut c_void) -> usize;
type Compress2SetBufferModesFn = unsafe extern "C" fn(*mut c_void, c_int, c_int);
type Compress2StreamEndFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
usize,
*mut usize,
*const c_void,
usize,
*mut usize,
) -> usize;
/// Explicit projection for the C fallback behind `ZSTD_compress2`.
///
/// Rust owns the reset/mode-switch/stream-call ordering and result policy.
/// The opaque callback context remains in C, where callbacks retain access to
/// the private `ZSTD_CCtx` layout and the simple-arguments stream adapter.
#[repr(C)]
pub struct ZSTD_rust_compress2State {
callback_context: *mut c_void,
reset_session: Compress2ResetFn,
set_buffer_modes: Compress2SetBufferModesFn,
compress_stream_end: Compress2StreamEndFn,
original_in_buffer_mode: c_int,
original_out_buffer_mode: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compress2State, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compress2State, reset_session) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compress2State, set_buffer_modes) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compress2State, compress_stream_end) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compress2State, original_in_buffer_mode) == 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compress2State, original_out_buffer_mode)
== 4 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_compress2State>() == 4 * size_of::<usize>() + 2 * size_of::<c_int>()
);
};
unsafe fn compress2_body_with(
state: &ZSTD_rust_compress2State,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
let reset_result = unsafe { (state.reset_session)(state.callback_context) };
unsafe {
(state.set_buffer_modes)(state.callback_context, ZSTD_BM_STABLE, ZSTD_BM_STABLE);
}
let mut output_pos = 0;
let mut input_pos = 0;
let result = if ERR_isError(reset_result) {
reset_result
} else {
unsafe {
(state.compress_stream_end)(
state.callback_context,
dst,
dst_capacity,
&mut output_pos,
src,
src_size,
&mut input_pos,
)
}
};
unsafe {
(state.set_buffer_modes)(
state.callback_context,
state.original_in_buffer_mode,
state.original_out_buffer_mode,
);
}
if ERR_isError(result) {
return result;
}
if result != 0 {
debug_assert_eq!(output_pos, dst_capacity);
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
debug_assert_eq!(input_pos, src_size);
output_pos
}
/// Drive the `ZSTD_compress2_c()` fallback without crossing the C context
/// layout.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compress2(
state: *const ZSTD_rust_compress2State,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { compress2_body_with(&*state, dst, dst_capacity, src, src_size) }
}
/// Explicit projection for `ZSTD_CCtx_setParametersUsingCCtxParams`.
///
/// Rust owns the stage and dictionary policy while the C-owned parameter
/// objects cross the ABI as pointers to the shared layout mirror.
#[repr(C)]
pub struct ZSTD_rust_setParametersUsingCCtxParamsState {
requested_params: *mut ZSTD_CCtx_params,
source_params: *const ZSTD_CCtx_params,
stream_stage: c_int,
cdict: *const c_void,
}
const _: () = {
assert!(
offset_of!(
ZSTD_rust_setParametersUsingCCtxParamsState,
requested_params
) == 0
);
assert!(
offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, source_params)
== size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, stream_stage)
== 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, cdict) == 3 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_setParametersUsingCCtxParamsState>() == 4 * size_of::<usize>());
};
/// Apply stored parameters through the C-owned context projection.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_setParametersUsingCCtxParams(
state: *const ZSTD_rust_setParametersUsingCCtxParamsState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.stream_stage != ZSTD_CSTREAM_STAGE_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
if !state.cdict.is_null() {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe {
*state.requested_params = *state.source_params;
}
0
}
/// Explicit projection for `ZSTD_CCtx_setParameter`.
///
/// Rust owns the stage authorization and accepted-parameter policy while the
/// C-owned context exposes only the fields whose side effects remain private.
#[repr(C)]
pub struct ZSTD_rust_setParameterState {
requested_params: *mut ZSTD_CCtx_params,
stream_stage: c_int,
c_params_changed: *mut c_int,
static_size: usize,
rust_simple_compress2_max_block_size_set: *mut c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_setParameterState, requested_params) == 0);
assert!(offset_of!(ZSTD_rust_setParameterState, stream_stage) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_setParameterState, c_params_changed) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_setParameterState, static_size) == 3 * size_of::<usize>());
assert!(
offset_of!(
ZSTD_rust_setParameterState,
rust_simple_compress2_max_block_size_set
) == 4 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_setParameterState>() == 5 * size_of::<usize>());
};
#[inline]
fn set_parameter_is_supported(param: c_int) -> bool {
matches!(
param,
ZSTD_C_COMPRESSION_LEVEL
| ZSTD_C_WINDOW_LOG
| ZSTD_C_HASH_LOG
| ZSTD_C_CHAIN_LOG
| ZSTD_C_SEARCH_LOG
| ZSTD_C_MIN_MATCH
| ZSTD_C_TARGET_LENGTH
| ZSTD_C_STRATEGY
| ZSTD_C_LDM_HASH_RATE_LOG
| ZSTD_C_FORMAT
| ZSTD_C_CONTENT_SIZE_FLAG
| ZSTD_C_CHECKSUM_FLAG
| ZSTD_C_DICT_ID_FLAG
| ZSTD_C_FORCE_MAX_WINDOW
| ZSTD_C_FORCE_ATTACH_DICT
| ZSTD_C_LITERAL_COMPRESSION_MODE
| ZSTD_C_JOB_SIZE
| ZSTD_C_OVERLAP_LOG
| ZSTD_C_RSYNCABLE
| ZSTD_C_ENABLE_DEDICATED_DICT_SEARCH
| ZSTD_C_ENABLE_LDM
| ZSTD_C_LDM_HASH_LOG
| ZSTD_C_LDM_MIN_MATCH
| ZSTD_C_LDM_BUCKET_SIZE_LOG
| ZSTD_C_TARGET_C_BLOCK_SIZE
| ZSTD_C_SRC_SIZE_HINT
| ZSTD_C_STABLE_IN_BUFFER
| ZSTD_C_STABLE_OUT_BUFFER
| ZSTD_C_BLOCK_DELIMITERS
| ZSTD_C_VALIDATE_SEQUENCES
| ZSTD_C_SPLIT_AFTER_SEQUENCES
| ZSTD_C_BLOCK_SPLITTER_LEVEL
| ZSTD_C_USE_ROW_MATCH_FINDER
| ZSTD_C_DETERMINISTIC_REF_PREFIX
| ZSTD_C_PREFETCH_CDICT_TABLES
| ZSTD_C_ENABLE_SEQ_PRODUCER_FALLBACK
| ZSTD_C_MAX_BLOCK_SIZE
| ZSTD_C_REPCODE_RESOLUTION
)
}
/// Apply one context parameter through the C-owned parameter object.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_setParameter(
state: *const ZSTD_rust_setParameterState,
param: c_int,
value: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.stream_stage != ZSTD_CSTREAM_STAGE_INIT {
if ZSTD_rust_isUpdateAuthorized(param) == 0 {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe {
*state.c_params_changed = 1;
}
}
if param == ZSTD_C_NB_WORKERS {
if value != 0 && state.static_size != 0 {
return ERROR(ZstdErrorCode::ParameterUnsupported);
}
} else if !set_parameter_is_supported(param) {
return ERROR(ZstdErrorCode::ParameterUnsupported);
}
let result = unsafe { ZSTD_CCtxParams_setParameter(state.requested_params, param, value) };
if !ERR_isError(result) && param == ZSTD_C_MAX_BLOCK_SIZE {
unsafe {
*state.rust_simple_compress2_max_block_size_set = 1;
}
}
result
}
/// Explicit projection for `ZSTD_CCtx_refThreadPool`.
///
/// Rust owns the init-stage check and assignment policy while the pool slot
/// itself remains storage owned by the C context. The pool value is opaque to
/// Rust because its private `POOL_ctx_s` layout is not part of this bridge.
#[repr(C)]
pub struct ZSTD_rust_refThreadPoolState {
pool: *mut *mut c_void,
requested_pool: *mut c_void,
stream_stage: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_refThreadPoolState, pool) == 0);
assert!(offset_of!(ZSTD_rust_refThreadPoolState, requested_pool) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_refThreadPoolState, stream_stage) == 2 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_refThreadPoolState>() == 3 * size_of::<usize>());
};
/// Attach an opaque thread pool through the C-owned context slot.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_refThreadPool(
state: *const ZSTD_rust_refThreadPoolState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.stream_stage != ZSTD_CSTREAM_STAGE_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe {
*state.pool = state.requested_pool;
}
0
}
type InitCCtxCallbackFn = unsafe extern "C" fn(*mut c_void);
type InitCCtxSetCustomMemFn = unsafe extern "C" fn(*mut c_void, *const c_void);
type InitCCtxResetFn = unsafe extern "C" fn(*mut c_void) -> usize;
/// Explicit projection for private heap-CCtx initialization.
///
/// Rust owns the initialization ordering while C retains the private context
/// layout, allocator publication, CPU-feature query, and reset operation
/// behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_initCCtxState {
callback_context: *mut c_void,
custom_mem: *const c_void,
zero: InitCCtxCallbackFn,
set_custom_mem: InitCCtxSetCustomMemFn,
set_bmi2: InitCCtxCallbackFn,
reset: InitCCtxResetFn,
}
const _: () = {
assert!(size_of::<InitCCtxCallbackFn>() == size_of::<usize>());
assert!(size_of::<InitCCtxSetCustomMemFn>() == size_of::<usize>());
assert!(size_of::<InitCCtxResetFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCCtxState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initCCtxState, custom_mem) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCCtxState, zero) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCCtxState, set_custom_mem) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCCtxState, set_bmi2) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCCtxState, reset) == 5 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_initCCtxState>() == 6 * size_of::<usize>());
};
/// Initialize a heap-owned C compression context in the original order.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCCtx(state: *const ZSTD_rust_initCCtxState) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callback_context.is_null() || state.custom_mem.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
(state.zero)(state.callback_context);
(state.set_custom_mem)(state.callback_context, state.custom_mem);
(state.set_bmi2)(state.callback_context);
let result = (state.reset)(state.callback_context);
if ERR_isError(result) {
return result;
}
}
0
}
type CreateCCtxValidateCustomMemFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type CreateCCtxAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
type CreateCCtxInitFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
/// Explicit projection for the public `ZSTD_createCCtx_advanced` wrapper.
///
/// Rust owns custom-memory validation, allocation failure, and
/// allocate-before-init ordering. C retains private `ZSTD_CCtx` initialization
/// behind the final callback.
#[repr(C)]
pub struct ZSTD_rust_createCCtxState {
callback_context: *mut c_void,
cctx_size: usize,
validate_custom_mem: CreateCCtxValidateCustomMemFn,
allocate: CreateCCtxAllocateFn,
init: CreateCCtxInitFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_createCCtxState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_createCCtxState, cctx_size) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_createCCtxState, validate_custom_mem) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_createCCtxState, allocate) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_createCCtxState, init) == 4 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_createCCtxState>() == size_of::<[usize; 5]>());
};
/// Allocate and initialize a C-owned compression context.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_createCCtx(
state: *const ZSTD_rust_createCCtxState,
) -> *mut c_void {
if state.is_null() {
return ptr::null_mut();
}
let state = unsafe { &*state };
if state.callback_context.is_null()
|| unsafe { (state.validate_custom_mem)(state.callback_context) == 0 }
{
return ptr::null_mut();
}
let cctx = unsafe { (state.allocate)(state.callback_context, state.cctx_size) };
if cctx.is_null() {
return ptr::null_mut();
}
unsafe { (state.init)(state.callback_context, cctx) };
cctx
}
type CreateCDictAdvancedWrapperInitParamsFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
*const ZSTD_compressionParameters,
*const c_void,
usize,
c_int,
);
type CreateCDictAdvancedWrapperCreateFn = unsafe extern "C" fn(
*mut c_void,
*const c_void,
usize,
c_int,
c_int,
*const c_void,
*const c_void,
) -> *mut c_void;
/// Explicit projection for the public heap-CDict wrapper.
///
/// Rust owns the wrapper's preparation-to-construction ordering and forwards
/// the dictionary scalars. C retains the private `ZSTD_CCtx_params` setup and
/// the advanced-CDict implementation behind callbacks; allocation and
/// workspace ownership stay in the existing advanced2 path.
#[repr(C)]
pub struct ZSTD_rust_createCDictAdvancedWrapperState {
callback_context: *mut c_void,
cctx_params: *mut c_void,
c_params: *const ZSTD_compressionParameters,
custom_mem: *const c_void,
init_params: CreateCDictAdvancedWrapperInitParamsFn,
create: CreateCDictAdvancedWrapperCreateFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_createCDictAdvancedWrapperState, callback_context) == 0);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedWrapperState, cctx_params) == size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedWrapperState, c_params) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedWrapperState, custom_mem) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedWrapperState, init_params)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedWrapperState, create) == 5 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_createCDictAdvancedWrapperState>() == size_of::<[usize; 6]>());
};
/// Prepare a heap-CDict parameter object, then enter the existing advanced
/// construction path in the original callback order.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_createCDictAdvancedWrapper(
state: *const ZSTD_rust_createCDictAdvancedWrapperState,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
) -> *mut c_void {
if state.is_null() {
return ptr::null_mut();
}
let state = unsafe { &*state };
if state.cctx_params.is_null() || state.c_params.is_null() || state.custom_mem.is_null() {
return ptr::null_mut();
}
unsafe {
(state.init_params)(
state.callback_context,
state.cctx_params,
state.c_params,
state.custom_mem,
dict_size,
dict_content_type,
);
(state.create)(
state.callback_context,
dict,
dict_size,
dict_load_method,
dict_content_type,
state.cctx_params,
state.custom_mem,
)
}
}
type InitStaticCCtxCreateWorkspaceFn = unsafe extern "C" fn(*mut c_void);
type InitStaticCCtxReserveObjectFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
type InitStaticCCtxZeroFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
type InitStaticCCtxMoveWorkspaceFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
type InitStaticCCtxCheckAvailableFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type InitStaticCCtxReserveBlockStateFn = unsafe extern "C" fn(*mut c_void, c_int) -> *mut c_void;
type InitStaticCCtxReserveTmpWorkspaceFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
type InitStaticCCtxSetBmi2Fn = unsafe extern "C" fn(*mut c_void);
const ZSTD_STATIC_WORKSPACE_ALIGNMENT: usize = 8;
const INIT_STATIC_CCTX_PREV_CBLOCK: c_int = 0;
const INIT_STATIC_CCTX_NEXT_CBLOCK: c_int = 1;
/// Explicit projection for the public `ZSTD_initStaticCCtx` wrapper.
///
/// Rust owns workspace pointer/alignment validation, minimum-size validation,
/// construction ordering, and callback failure policy. C retains the private
/// static-workspace and CCtx layout operations behind narrow callbacks.
#[repr(C)]
pub struct ZSTD_rust_initStaticCCtxState {
callback_context: *mut c_void,
workspace: *mut c_void,
workspace_size: usize,
cctx_size: usize,
create_workspace: InitStaticCCtxCreateWorkspaceFn,
reserve_object: InitStaticCCtxReserveObjectFn,
zero: InitStaticCCtxZeroFn,
move_workspace: InitStaticCCtxMoveWorkspaceFn,
check_available: InitStaticCCtxCheckAvailableFn,
reserve_block_state: InitStaticCCtxReserveBlockStateFn,
reserve_tmp_workspace: InitStaticCCtxReserveTmpWorkspaceFn,
set_bmi2: InitStaticCCtxSetBmi2Fn,
}
const _: () = {
assert!(size_of::<InitStaticCCtxCreateWorkspaceFn>() == size_of::<usize>());
assert!(size_of::<InitStaticCCtxReserveObjectFn>() == size_of::<usize>());
assert!(size_of::<InitStaticCCtxZeroFn>() == size_of::<usize>());
assert!(size_of::<InitStaticCCtxMoveWorkspaceFn>() == size_of::<usize>());
assert!(size_of::<InitStaticCCtxCheckAvailableFn>() == size_of::<usize>());
assert!(size_of::<InitStaticCCtxReserveBlockStateFn>() == size_of::<usize>());
assert!(size_of::<InitStaticCCtxReserveTmpWorkspaceFn>() == size_of::<usize>());
assert!(size_of::<InitStaticCCtxSetBmi2Fn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace_size) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, cctx_size) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, create_workspace) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, reserve_object) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, zero) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, move_workspace) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, check_available) == 8 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_initStaticCCtxState, reserve_block_state) == 9 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_initStaticCCtxState, reserve_tmp_workspace) == 10 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, set_bmi2) == 11 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_initStaticCCtxState>() == size_of::<[usize; 12]>());
};
/// Validate and sequence static CCtx construction before entering C leaves.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initStaticCCtx(
state: *const ZSTD_rust_initStaticCCtxState,
) -> *mut c_void {
if state.is_null() {
return ptr::null_mut();
}
let state = unsafe { &*state };
if state.callback_context.is_null()
|| state.workspace.is_null()
|| (state.workspace as usize) & (ZSTD_STATIC_WORKSPACE_ALIGNMENT - 1) != 0
|| state.workspace_size <= state.cctx_size
{
return ptr::null_mut();
}
unsafe {
(state.create_workspace)(state.callback_context);
let cctx = (state.reserve_object)(state.callback_context);
if cctx.is_null() {
return ptr::null_mut();
}
(state.zero)(state.callback_context, cctx);
(state.move_workspace)(state.callback_context, cctx);
if (state.check_available)(state.callback_context) == 0 {
return ptr::null_mut();
}
if (state.reserve_block_state)(state.callback_context, INIT_STATIC_CCTX_PREV_CBLOCK)
.is_null()
{
return ptr::null_mut();
}
if (state.reserve_block_state)(state.callback_context, INIT_STATIC_CCTX_NEXT_CBLOCK)
.is_null()
{
return ptr::null_mut();
}
if (state.reserve_tmp_workspace)(state.callback_context).is_null() {
return ptr::null_mut();
}
(state.set_bmi2)(state.callback_context);
cctx
}
}
type FreeCCtxContentFn = unsafe extern "C" fn(*mut c_void);
type FreeCCtxObjectFn = unsafe extern "C" fn(*mut c_void);
/// Explicit projection for the public `ZSTD_freeCCtx` wrapper.
///
/// Rust owns the null/static/embedded-object policy and callback ordering.
/// C retains the private content teardown and custom allocator callbacks.
#[repr(C)]
pub struct ZSTD_rust_freeCCtxState {
callback_context: *mut c_void,
static_size: usize,
cctx_in_workspace: c_int,
free_content: FreeCCtxContentFn,
free_object: FreeCCtxObjectFn,
}
const _: () = {
assert!(size_of::<FreeCCtxContentFn>() == size_of::<usize>());
assert!(size_of::<FreeCCtxObjectFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_freeCCtxState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_freeCCtxState, static_size) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_freeCCtxState, cctx_in_workspace) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_freeCCtxState, free_content) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_freeCCtxState, free_object) == 4 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_freeCCtxState>() == 5 * size_of::<usize>());
};
/// Free the C-owned context content and, when applicable, its outer object.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_freeCCtx(state: *const ZSTD_rust_freeCCtxState) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callback_context.is_null() {
return 0;
}
if state.static_size != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe {
(state.free_content)(state.callback_context);
if state.cctx_in_workspace == 0 {
(state.free_object)(state.callback_context);
}
}
0
}
type ClearAllDictsCallbackFn = unsafe extern "C" fn(*mut c_void);
/// Compose the dictionary teardown operations while leaving the private
/// ZSTD_CCtx fields behind C callbacks.
#[repr(C)]
pub struct ZSTD_rust_clearAllDictsState {
callback_context: *mut c_void,
free_local_dict_buffer: ClearAllDictsCallbackFn,
free_local_cdict: ClearAllDictsCallbackFn,
clear_local_dict: ClearAllDictsCallbackFn,
clear_prefix_dict: ClearAllDictsCallbackFn,
clear_cdict: ClearAllDictsCallbackFn,
}
const _: () = {
assert!(size_of::<ClearAllDictsCallbackFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_clearAllDictsState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_clearAllDictsState, free_local_dict_buffer) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_clearAllDictsState, free_local_cdict) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_clearAllDictsState, clear_local_dict) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_clearAllDictsState, clear_prefix_dict) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_clearAllDictsState, clear_cdict) == 5 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_clearAllDictsState>() == 6 * size_of::<usize>());
};
/// Preserve the original local-dictionary, prefix-dictionary, and attached
/// CDict teardown order while keeping their private storage C-owned.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_clearAllDicts(state: *const ZSTD_rust_clearAllDictsState) {
if state.is_null() {
return;
}
let state = unsafe { &*state };
if state.callback_context.is_null() {
return;
}
unsafe {
(state.free_local_dict_buffer)(state.callback_context);
(state.free_local_cdict)(state.callback_context);
(state.clear_local_dict)(state.callback_context);
(state.clear_prefix_dict)(state.callback_context);
(state.clear_cdict)(state.callback_context);
}
}
type ResetCCtxClearAllDictsFn = unsafe extern "C" fn(*mut c_void);
type ResetCCtxResetParamsFn = unsafe extern "C" fn(*mut c_void) -> usize;
/// Explicit projection for the public `ZSTD_CCtx_reset` wrapper.
///
/// Rust owns the reset-directive policy and ordering. The callback context
/// remains in C, where the dictionary and parameter reset operations retain
/// access to the private `ZSTD_CCtx` layout.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxState {
callback_context: *mut c_void,
rust_simple_compress2_completed: *mut c_uint,
stream_stage: *mut c_int,
pledged_src_size_plus_one: *mut u64,
rust_simple_compress2_max_block_size_set: *mut c_uint,
clear_all_dicts: ResetCCtxClearAllDictsFn,
reset_params: ResetCCtxResetParamsFn,
}
const _: () = {
assert!(size_of::<ResetCCtxClearAllDictsFn>() == size_of::<usize>());
assert!(size_of::<ResetCCtxResetParamsFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxState, callback_context) == 0);
assert!(
offset_of!(ZSTD_rust_resetCCtxState, rust_simple_compress2_completed) == size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_resetCCtxState, stream_stage) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxState, pledged_src_size_plus_one) == 3 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_resetCCtxState,
rust_simple_compress2_max_block_size_set
) == 4 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_resetCCtxState, clear_all_dicts) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxState, reset_params) == 6 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_resetCCtxState>() == 7 * size_of::<usize>());
};
/// Drive the public `ZSTD_CCtx_reset` policy through C-owned callbacks.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtx(
state: *const ZSTD_rust_resetCCtxState,
reset: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
unsafe {
*state.rust_simple_compress2_completed = 0;
}
if reset == ZSTD_RESET_SESSION_ONLY || reset == ZSTD_RESET_SESSION_AND_PARAMETERS {
unsafe {
*state.stream_stage = ZSTD_CSTREAM_STAGE_INIT;
*state.pledged_src_size_plus_one = 0;
}
}
if reset == ZSTD_RESET_PARAMETERS || reset == ZSTD_RESET_SESSION_AND_PARAMETERS {
if unsafe { *state.stream_stage } != ZSTD_CSTREAM_STAGE_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe { (state.clear_all_dicts)(state.callback_context) };
unsafe {
*state.rust_simple_compress2_max_block_size_set = 0;
}
return unsafe { (state.reset_params)(state.callback_context) };
}
0
}
type CopyCCtxInternalFn = unsafe extern "C" fn(
*mut c_void,
*const c_void,
*const ZSTD_frameParameters,
u64,
c_int,
) -> usize;
/// Explicit projection for the public `ZSTD_copyCCtx` wrapper.
///
/// Rust owns zero-to-unknown pledged-size normalization and frame-parameter
/// construction. C retains the private table and workspace copy operation.
#[repr(C)]
pub struct ZSTD_rust_copyCCtxState {
callback_context: *mut c_void,
src_cctx: *const c_void,
f_params: *const ZSTD_frameParameters,
pledged_src_size: *const u64,
zbuff: *const c_int,
copy_internal: Option<CopyCCtxInternalFn>,
}
const _: () = {
assert!(size_of::<CopyCCtxInternalFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyCCtxState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_copyCCtxState, src_cctx) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyCCtxState, f_params) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyCCtxState, pledged_src_size) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyCCtxState, zbuff) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyCCtxState, copy_internal) == 5 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_copyCCtxState>() == size_of::<[usize; 6]>());
};
/// Normalize the public copy request before invoking the private C operation.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_copyCCtx(state: *const ZSTD_rust_copyCCtxState) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(copy_internal) = state.copy_internal else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.src_cctx.is_null()
|| state.f_params.is_null()
|| state.pledged_src_size.is_null()
|| state.zbuff.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let pledged_src_size = unsafe { *state.pledged_src_size };
let pledged_src_size = if pledged_src_size == 0 {
ZSTD_CONTENTSIZE_UNKNOWN
} else {
pledged_src_size
};
let mut f_params = unsafe { *state.f_params };
f_params.contentSizeFlag = c_int::from(pledged_src_size != ZSTD_CONTENTSIZE_UNKNOWN);
unsafe {
copy_internal(
state.callback_context,
state.src_cctx,
&f_params,
pledged_src_size,
*state.zbuff,
)
}
}
type CopyCCtxResetFn = unsafe extern "C" fn(
*mut c_void,
*const c_void,
*const ZSTD_frameParameters,
u64,
c_int,
) -> usize;
type CopyCCtxMarkTablesFn = unsafe extern "C" fn(*mut c_void);
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ZSTD_rust_copyWindowState {
next_src: *const c_void,
base: *const c_void,
dict_base: *const c_void,
dict_limit: c_uint,
low_limit: c_uint,
nb_overflow_corrections: c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_copyWindowState, next_src) == 0);
assert!(offset_of!(ZSTD_rust_copyWindowState, base) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyWindowState, dict_base) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyWindowState, dict_limit) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_copyWindowState, low_limit)
== 3 * size_of::<usize>() + size_of::<c_uint>()
);
assert!(
offset_of!(ZSTD_rust_copyWindowState, nb_overflow_corrections)
== 3 * size_of::<usize>() + 2 * size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_copyWindowState>()
== (3 * size_of::<usize>() + 3 * size_of::<c_uint>()).div_ceil(size_of::<usize>())
* size_of::<usize>()
);
};
const _: () = {
assert!(size_of::<ZSTD_customMem>() == 3 * size_of::<usize>());
};
/// Projection for the private `ZSTD_copyCCtx_internal` operation.
///
/// Rust owns the stage/error branch and the order of the reset, workspace,
/// table, window, dictionary, and block-state operations. C retains access to
/// the private `ZSTD_CCtx` layout behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_copyCCtxInternalState {
callback_context: *mut c_void,
src_cctx: *const c_void,
f_params: *const ZSTD_frameParameters,
pledged_src_size: u64,
source_stage: *const c_int,
destination_custom_mem: *mut c_void,
source_custom_mem: *const c_void,
reset: Option<CopyCCtxResetFn>,
mark_tables_dirty: Option<CopyCCtxMarkTablesFn>,
mark_tables_clean: Option<CopyCCtxMarkTablesFn>,
destination_hash_table: *mut *mut c_uint,
source_hash_table: *const c_uint,
source_hash_log: *const c_uint,
destination_chain_table: *mut *mut c_uint,
source_chain_table: *const c_uint,
source_chain_log: *const c_uint,
destination_hash_table3: *mut *mut c_uint,
source_hash_table3: *const c_uint,
source_hash_log3: *const c_uint,
source_strategy: *const c_int,
source_use_row_match_finder: *const c_int,
destination_window: *mut c_void,
source_window: *const c_void,
destination_next_to_update: *mut c_uint,
source_next_to_update: *const c_uint,
destination_loaded_dict_end: *mut c_uint,
source_loaded_dict_end: *const c_uint,
destination_dict_id: *mut c_uint,
source_dict_id: *const c_uint,
destination_dict_content_size: *mut usize,
source_dict_content_size: *const usize,
destination_block_state: *mut *mut ZSTD_compressedBlockState_t,
source_block_state: *const ZSTD_compressedBlockState_t,
zbuff: c_int,
}
const _: () = {
assert!(size_of::<CopyCCtxResetFn>() == size_of::<usize>());
assert!(size_of::<CopyCCtxMarkTablesFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyCCtxInternalState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_copyCCtxInternalState, src_cctx) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyCCtxInternalState, f_params) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_copyCCtxInternalState, pledged_src_size) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_copyCCtxInternalState, source_stage)
== 3 * size_of::<usize>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_copyCCtxInternalState, zbuff)
== 3 * size_of::<usize>() + size_of::<u64>() + 29 * size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_copyCCtxInternalState>()
== (offset_of!(ZSTD_rust_copyCCtxInternalState, zbuff) + size_of::<c_int>())
.div_ceil(size_of::<usize>())
* size_of::<usize>()
);
};
/// Run the private context-copy operation through C-owned layout callbacks
/// and copy the compressed-block state directly in Rust.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_copyCCtxInternal(
state: *const ZSTD_rust_copyCCtxInternalState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let (Some(reset), Some(mark_tables_dirty), Some(mark_tables_clean)) = (
state.reset,
state.mark_tables_dirty,
state.mark_tables_clean,
) else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.src_cctx.is_null()
|| state.f_params.is_null()
|| state.source_stage.is_null()
|| state.destination_custom_mem.is_null()
|| state.source_custom_mem.is_null()
|| state.destination_hash_table.is_null()
|| state.destination_chain_table.is_null()
|| state.destination_hash_table3.is_null()
|| state.source_hash_log.is_null()
|| state.source_chain_log.is_null()
|| state.source_hash_log3.is_null()
|| state.source_strategy.is_null()
|| state.source_use_row_match_finder.is_null()
|| state.destination_window.is_null()
|| state.source_window.is_null()
|| state.destination_next_to_update.is_null()
|| state.source_next_to_update.is_null()
|| state.destination_loaded_dict_end.is_null()
|| state.source_loaded_dict_end.is_null()
|| state.destination_dict_id.is_null()
|| state.source_dict_id.is_null()
|| state.destination_dict_content_size.is_null()
|| state.source_dict_content_size.is_null()
|| state.destination_block_state.is_null()
|| state.source_block_state.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { *state.source_stage } != ZSTD_COMPRESSION_STAGE_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
let source_hash_log = unsafe { *state.source_hash_log };
let source_chain_log = unsafe { *state.source_chain_log };
let source_hash_log3 = unsafe { *state.source_hash_log3 };
let source_strategy = unsafe { *state.source_strategy };
let source_use_row_match_finder = unsafe { *state.source_use_row_match_finder };
let Some(hash_table_size) = 1usize.checked_shl(source_hash_log) else {
return ERROR(ZstdErrorCode::Generic);
};
let chain_table_size = if ZSTD_rust_params_allocateChainTable(
source_strategy,
source_use_row_match_finder,
0,
) != 0
{
let Some(size) = 1usize.checked_shl(source_chain_log) else {
return ERROR(ZstdErrorCode::Generic);
};
size
} else {
0
};
let hash_table3_size = if source_hash_log3 != 0 {
let Some(size) = 1usize.checked_shl(source_hash_log3) else {
return ERROR(ZstdErrorCode::Generic);
};
size
} else {
0
};
unsafe {
ptr::copy(
state.source_custom_mem.cast::<ZSTD_customMem>(),
state.destination_custom_mem.cast::<ZSTD_customMem>(),
1,
);
let reset_error = reset(
state.callback_context,
state.src_cctx,
state.f_params,
state.pledged_src_size,
state.zbuff,
);
if ERR_isError(reset_error) {
return reset_error;
}
let destination_hash_table = *state.destination_hash_table;
let destination_chain_table = *state.destination_chain_table;
let destination_hash_table3 = *state.destination_hash_table3;
if (hash_table_size != 0
&& (destination_hash_table.is_null() || state.source_hash_table.is_null()))
|| (chain_table_size != 0
&& (destination_chain_table.is_null() || state.source_chain_table.is_null()))
|| (hash_table3_size != 0
&& (destination_hash_table3.is_null() || state.source_hash_table3.is_null()))
{
return ERROR(ZstdErrorCode::Generic);
}
mark_tables_dirty(state.callback_context);
if hash_table_size != 0 {
ptr::copy_nonoverlapping(
state.source_hash_table,
destination_hash_table,
hash_table_size,
);
}
if chain_table_size != 0 {
ptr::copy_nonoverlapping(
state.source_chain_table,
destination_chain_table,
chain_table_size,
);
}
if hash_table3_size != 0 {
ptr::copy_nonoverlapping(
state.source_hash_table3,
destination_hash_table3,
hash_table3_size,
);
}
mark_tables_clean(state.callback_context);
ptr::copy(
state.source_window.cast::<ZSTD_rust_copyWindowState>(),
state.destination_window.cast::<ZSTD_rust_copyWindowState>(),
1,
);
*state.destination_next_to_update = *state.source_next_to_update;
*state.destination_loaded_dict_end = *state.source_loaded_dict_end;
*state.destination_dict_id = *state.source_dict_id;
*state.destination_dict_content_size = *state.source_dict_content_size;
let destination_block_state = *state.destination_block_state;
if destination_block_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
ptr::copy_nonoverlapping(state.source_block_state, destination_block_state, 1);
}
0
}
type CompressAdvancedInitParamsFn = unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters);
type CompressAdvancedInternalFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
usize,
*const c_void,
usize,
*const c_void,
usize,
) -> usize;
/// Explicit projection for the public `ZSTD_compress_advanced` wrapper.
///
/// Rust owns parameter validation and the init-then-compress order. C retains
/// the private `simpleApiParams` initialization and advanced compression call.
#[repr(C)]
pub struct ZSTD_rust_compressAdvancedState {
callback_context: *mut c_void,
init_params: Option<CompressAdvancedInitParamsFn>,
compress_internal: Option<CompressAdvancedInternalFn>,
}
const _: () = {
assert!(size_of::<CompressAdvancedInitParamsFn>() == size_of::<usize>());
assert!(size_of::<CompressAdvancedInternalFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressAdvancedState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressAdvancedState, init_params) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressAdvancedState, compress_internal) == 2 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressAdvancedState>() == size_of::<[usize; 3]>());
};
/// Validate parameters and invoke the C-owned advanced compression operation.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressAdvanced(
state: *const ZSTD_rust_compressAdvancedState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
params: *const ZSTD_parameters,
) -> usize {
if state.is_null() || params.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(init_params) = state.init_params else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(compress_internal) = state.compress_internal else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let params = unsafe { &*params };
let check_result = ZSTD_rust_params_checkCParams(params.cParams);
if ERR_isError(check_result) {
return check_result;
}
unsafe { init_params(state.callback_context, params) };
unsafe {
compress_internal(
state.callback_context,
dst,
dst_capacity,
src,
src_size,
dict,
dict_size,
)
}
}
type CompressBeginAdvancedInitParamsFn = unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters);
type CompressBeginAdvancedBeginFn =
unsafe extern "C" fn(*mut c_void, *const c_void, usize, *const c_void, u64) -> usize;
/// Explicit projection for the public `ZSTD_compressBegin_advanced` wrapper.
///
/// Rust owns public parameter validation and init-then-begin ordering. C
/// retains private `ZSTD_CCtx_params` initialization and the final begin
/// operation behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressBeginAdvancedState {
cctx: *mut c_void,
cctx_params: *mut c_void,
init_params: CompressBeginAdvancedInitParamsFn,
begin: CompressBeginAdvancedBeginFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressBeginAdvancedState, cctx) == 0);
assert!(offset_of!(ZSTD_rust_compressBeginAdvancedState, cctx_params) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressBeginAdvancedState, init_params) == 2 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressBeginAdvancedState, begin) == 3 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_compressBeginAdvancedState>() == size_of::<[usize; 4]>());
};
/// Validate parameters and begin a frame through the C-owned operation.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBeginAdvanced(
state: *const ZSTD_rust_compressBeginAdvancedState,
dict: *const c_void,
dict_size: usize,
params: *const ZSTD_parameters,
pledged_src_size: u64,
) -> usize {
if state.is_null() || params.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.cctx.is_null() || state.cctx_params.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let params = unsafe { &*params };
let check_result = ZSTD_rust_params_checkCParams(params.cParams);
if ERR_isError(check_result) {
return check_result;
}
unsafe {
(state.init_params)(state.cctx_params, params);
(state.begin)(
state.cctx,
dict,
dict_size,
state.cctx_params,
pledged_src_size,
)
}
}
type CompressUsingDictInitParamsFn =
unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters, c_int);
/// Explicit projection for the public `ZSTD_compress_usingDict` wrapper.
///
/// Rust owns compression-parameter selection, dictionary-presence handling,
/// and the default-level normalization. C retains the private
/// `ZSTD_CCtx_params` initialization and advanced compression operation.
#[repr(C)]
pub struct ZSTD_rust_compressUsingDictState {
callback_context: *mut c_void,
exclusion_mask: *const c_uint,
init_params: Option<CompressUsingDictInitParamsFn>,
compress_internal: Option<CompressAdvancedInternalFn>,
}
const _: () = {
assert!(size_of::<CompressUsingDictInitParamsFn>() == size_of::<usize>());
assert!(size_of::<CompressAdvancedInternalFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressUsingDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressUsingDictState, exclusion_mask) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressUsingDictState, init_params) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressUsingDictState, compress_internal) == 3 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressUsingDictState>() == size_of::<[usize; 4]>());
};
/// Select parameters and invoke the C-owned dictionary compression operation.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressUsingDict(
state: *const ZSTD_rust_compressUsingDictState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
compression_level: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(init_params) = state.init_params else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(compress_internal) = state.compress_internal else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null() || state.exclusion_mask.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let dict_size_for_params = if dict.is_null() { 0 } else { dict_size };
let params = ZSTD_rust_params_getParamsInternal(
compression_level,
src_size as u64,
dict_size_for_params,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
unsafe { *state.exclusion_mask },
);
let init_level = if compression_level == 0 {
ZSTD_rust_params_defaultCLevel()
} else {
compression_level
};
unsafe { init_params(state.callback_context, &params, init_level) };
unsafe {
compress_internal(
state.callback_context,
dst,
dst_capacity,
src,
src_size,
dict,
dict_size,
)
}
}
type ResetCStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize;
type ResetCStreamSetPledgedSrcSizeFn = unsafe extern "C" fn(*mut c_void, u64) -> usize;
/// Explicit projection for the public `ZSTD_resetCStream` wrapper.
///
/// Rust owns the zero-to-unknown conversion, callback ordering, and error
/// propagation. The opaque callback context stays in C, where the callbacks
/// retain access to the private `ZSTD_CCtx` layout.
#[repr(C)]
pub struct ZSTD_rust_resetCStreamState {
callback_context: *mut c_void,
reset_session: ResetCStreamResetFn,
set_pledged_src_size: ResetCStreamSetPledgedSrcSizeFn,
}
const _: () = {
assert!(size_of::<ResetCStreamResetFn>() == size_of::<usize>());
assert!(size_of::<ResetCStreamSetPledgedSrcSizeFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCStreamState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_resetCStreamState, reset_session) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCStreamState, set_pledged_src_size) == 2 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_resetCStreamState>() == 3 * size_of::<usize>());
};
/// Drive the public `ZSTD_resetCStream` policy through C-owned callbacks.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCStream(
state: *const ZSTD_rust_resetCStreamState,
pss: u64,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let pledged_src_size = if pss == 0 {
ZSTD_CONTENTSIZE_UNKNOWN
} else {
pss
};
let reset_result = unsafe { (state.reset_session)(state.callback_context) };
if ERR_isError(reset_result) {
return reset_result;
}
let pledged_size_result =
unsafe { (state.set_pledged_src_size)(state.callback_context, pledged_src_size) };
if ERR_isError(pledged_size_result) {
return pledged_size_result;
}
0
}
type InitCStreamUsingCDictAdvancedResetFn = unsafe extern "C" fn(*mut c_void) -> usize;
type InitCStreamUsingCDictAdvancedSetPledgedSrcSizeFn =
unsafe extern "C" fn(*mut c_void, u64) -> usize;
type InitCStreamUsingCDictAdvancedSetFrameParamsFn =
unsafe extern "C" fn(*mut c_void, c_uint, c_uint, c_uint);
type InitCStreamUsingCDictAdvancedRefCDictFn =
unsafe extern "C" fn(*mut c_void, *const c_void) -> usize;
/// Explicit projection for `ZSTD_initCStream_usingCDict_advanced`.
///
/// Rust owns the public wrapper's callback order and error propagation while
/// C retains the private context and dictionary/parameter mutations.
#[repr(C)]
pub struct ZSTD_rust_initCStreamUsingCDictAdvancedState {
callback_context: *mut c_void,
reset_session: InitCStreamUsingCDictAdvancedResetFn,
set_pledged_src_size: InitCStreamUsingCDictAdvancedSetPledgedSrcSizeFn,
set_frame_params: InitCStreamUsingCDictAdvancedSetFrameParamsFn,
ref_cdict: InitCStreamUsingCDictAdvancedRefCDictFn,
}
const _: () = {
assert!(size_of::<InitCStreamUsingCDictAdvancedResetFn>() == size_of::<usize>());
assert!(size_of::<InitCStreamUsingCDictAdvancedSetPledgedSrcSizeFn>() == size_of::<usize>());
assert!(size_of::<InitCStreamUsingCDictAdvancedSetFrameParamsFn>() == size_of::<usize>());
assert!(size_of::<InitCStreamUsingCDictAdvancedRefCDictFn>() == size_of::<usize>());
assert!(
offset_of!(
ZSTD_rust_initCStreamUsingCDictAdvancedState,
callback_context
) == 0
);
assert!(
offset_of!(ZSTD_rust_initCStreamUsingCDictAdvancedState, reset_session)
== size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_initCStreamUsingCDictAdvancedState,
set_pledged_src_size
) == 2 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_initCStreamUsingCDictAdvancedState,
set_frame_params
) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_initCStreamUsingCDictAdvancedState, ref_cdict)
== 4 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_initCStreamUsingCDictAdvancedState>() == 5 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCStreamUsingCDictAdvanced(
state: *const ZSTD_rust_initCStreamUsingCDictAdvancedState,
pledged_src_size: u64,
content_size_flag: c_uint,
checksum_flag: c_uint,
no_dict_id_flag: c_uint,
cdict: *const c_void,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let result = unsafe { (state.reset_session)(state.callback_context) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_pledged_src_size)(state.callback_context, pledged_src_size) };
if ERR_isError(result) {
return result;
}
unsafe {
(state.set_frame_params)(
state.callback_context,
content_size_flag,
checksum_flag,
no_dict_id_flag,
)
};
let result = unsafe { (state.ref_cdict)(state.callback_context, cdict) };
if ERR_isError(result) {
return result;
}
0
}
/// Explicit projection for `ZSTD_initCStream_usingCDict`.
#[repr(C)]
pub struct ZSTD_rust_initCStreamUsingCDictState {
callback_context: *mut c_void,
reset_session: InitCStreamUsingCDictAdvancedResetFn,
ref_cdict: InitCStreamUsingCDictAdvancedRefCDictFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_initCStreamUsingCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initCStreamUsingCDictState, reset_session) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCStreamUsingCDictState, ref_cdict) == 2 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_initCStreamUsingCDictState>() == 3 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCStreamUsingCDict(
state: *const ZSTD_rust_initCStreamUsingCDictState,
cdict: *const c_void,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let result = unsafe { (state.reset_session)(state.callback_context) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.ref_cdict)(state.callback_context, cdict) };
if ERR_isError(result) {
return result;
}
0
}
type InitCStreamSrcSizeSetLevelFn = unsafe extern "C" fn(*mut c_void, c_int) -> usize;
/// Explicit projection for `ZSTD_initCStream_srcSize`.
#[repr(C)]
pub struct ZSTD_rust_initCStreamSrcSizeState {
callback_context: *mut c_void,
reset_session: InitCStreamUsingCDictAdvancedResetFn,
ref_cdict: InitCStreamUsingCDictAdvancedRefCDictFn,
set_level: InitCStreamSrcSizeSetLevelFn,
set_pledged_src_size: InitCStreamUsingCDictAdvancedSetPledgedSrcSizeFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, reset_session) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, ref_cdict) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, set_level) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_initCStreamSrcSizeState, set_pledged_src_size)
== 4 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_initCStreamSrcSizeState>() == 5 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCStreamSrcSize(
state: *const ZSTD_rust_initCStreamSrcSizeState,
pss: u64,
compression_level: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let pledged_src_size = if pss == 0 {
ZSTD_CONTENTSIZE_UNKNOWN
} else {
pss
};
let result = unsafe { (state.reset_session)(state.callback_context) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.ref_cdict)(state.callback_context, ptr::null()) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_level)(state.callback_context, compression_level) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_pledged_src_size)(state.callback_context, pledged_src_size) };
if ERR_isError(result) {
return result;
}
0
}
/// Explicit projection for `ZSTD_initCStream`.
#[repr(C)]
pub struct ZSTD_rust_initCStreamState {
callback_context: *mut c_void,
reset_session: InitCStreamUsingCDictAdvancedResetFn,
ref_cdict: InitCStreamUsingCDictAdvancedRefCDictFn,
set_level: InitCStreamSrcSizeSetLevelFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_initCStreamState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initCStreamState, reset_session) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCStreamState, ref_cdict) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCStreamState, set_level) == 3 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_initCStreamState>() == 4 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCStream(
state: *const ZSTD_rust_initCStreamState,
compression_level: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let result = unsafe { (state.reset_session)(state.callback_context) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.ref_cdict)(state.callback_context, ptr::null()) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_level)(state.callback_context, compression_level) };
if ERR_isError(result) {
return result;
}
0
}
type InitCStreamUsingDictLoadDictionaryFn =
unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> usize;
/// Explicit projection for `ZSTD_initCStream_usingDict`.
#[repr(C)]
pub struct ZSTD_rust_initCStreamUsingDictState {
callback_context: *mut c_void,
reset_session: InitCStreamUsingCDictAdvancedResetFn,
set_level: InitCStreamSrcSizeSetLevelFn,
load_dictionary: InitCStreamUsingDictLoadDictionaryFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_initCStreamUsingDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initCStreamUsingDictState, reset_session) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCStreamUsingDictState, set_level) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_initCStreamUsingDictState, load_dictionary) == 3 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_initCStreamUsingDictState>() == 4 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCStreamUsingDict(
state: *const ZSTD_rust_initCStreamUsingDictState,
dict: *const c_void,
dict_size: usize,
compression_level: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let result = unsafe { (state.reset_session)(state.callback_context) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_level)(state.callback_context, compression_level) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.load_dictionary)(state.callback_context, dict, dict_size) };
if ERR_isError(result) {
return result;
}
0
}
type InitCStreamAdvancedCheckCParamsFn =
unsafe extern "C" fn(*mut c_void, ZSTD_compressionParameters) -> usize;
type InitCStreamAdvancedSetZstdParamsFn = unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters);
/// Explicit projection for `ZSTD_initCStream_advanced`.
#[repr(C)]
pub struct ZSTD_rust_initCStreamAdvancedState {
callback_context: *mut c_void,
reset_session: InitCStreamUsingCDictAdvancedResetFn,
set_pledged_src_size: InitCStreamUsingCDictAdvancedSetPledgedSrcSizeFn,
check_c_params: InitCStreamAdvancedCheckCParamsFn,
set_zstd_params: InitCStreamAdvancedSetZstdParamsFn,
load_dictionary: InitCStreamUsingDictLoadDictionaryFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_initCStreamAdvancedState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initCStreamAdvancedState, reset_session) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_initCStreamAdvancedState, set_pledged_src_size)
== 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_initCStreamAdvancedState, check_c_params) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_initCStreamAdvancedState, set_zstd_params) == 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_initCStreamAdvancedState, load_dictionary) == 5 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_initCStreamAdvancedState>() == 6 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCStreamAdvanced(
state: *const ZSTD_rust_initCStreamAdvancedState,
params: *const ZSTD_parameters,
pss: u64,
dict: *const c_void,
dict_size: usize,
) -> usize {
if state.is_null() || params.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let params = unsafe { &*params };
let pledged_src_size = if pss == 0 && params.fParams.contentSizeFlag == 0 {
ZSTD_CONTENTSIZE_UNKNOWN
} else {
pss
};
let result = unsafe { (state.reset_session)(state.callback_context) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_pledged_src_size)(state.callback_context, pledged_src_size) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.check_c_params)(state.callback_context, params.cParams) };
if ERR_isError(result) {
return result;
}
unsafe { (state.set_zstd_params)(state.callback_context, params) };
let result = unsafe { (state.load_dictionary)(state.callback_context, dict, dict_size) };
if ERR_isError(result) {
return result;
}
0
}
type SetCParamsCheckCParamsFn =
unsafe extern "C" fn(*mut c_void, ZSTD_compressionParameters) -> usize;
type SetCParamsSetParameterFn = unsafe extern "C" fn(*mut c_void, c_int, c_int) -> usize;
/// Explicit projection for `ZSTD_CCtx_setCParams`.
#[repr(C)]
pub struct ZSTD_rust_setCParamsState {
callback_context: *mut c_void,
check_c_params: SetCParamsCheckCParamsFn,
set_parameter: SetCParamsSetParameterFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_setCParamsState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_setCParamsState, check_c_params) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_setCParamsState, set_parameter) == 2 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_setCParamsState>() == 3 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_setCParams(
state: *const ZSTD_rust_setCParamsState,
cparams: ZSTD_compressionParameters,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let result = unsafe { (state.check_c_params)(state.callback_context, cparams) };
if ERR_isError(result) {
return result;
}
let parameters = [
(ZSTD_C_WINDOW_LOG, cparams.windowLog as c_int),
(ZSTD_C_CHAIN_LOG, cparams.chainLog as c_int),
(ZSTD_C_HASH_LOG, cparams.hashLog as c_int),
(ZSTD_C_SEARCH_LOG, cparams.searchLog as c_int),
(ZSTD_C_MIN_MATCH, cparams.minMatch as c_int),
(ZSTD_C_TARGET_LENGTH, cparams.targetLength as c_int),
(ZSTD_C_STRATEGY, cparams.strategy),
];
for (param, value) in parameters {
let result = unsafe { (state.set_parameter)(state.callback_context, param, value) };
if ERR_isError(result) {
return result;
}
}
0
}
type SetFParamsSetParameterFn = unsafe extern "C" fn(*mut c_void, c_int, c_int) -> usize;
/// Explicit projection for `ZSTD_CCtx_setFParams`.
#[repr(C)]
pub struct ZSTD_rust_setFParamsState {
callback_context: *mut c_void,
set_parameter: SetFParamsSetParameterFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_setFParamsState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_setFParamsState, set_parameter) == size_of::<usize>());
assert!(size_of::<ZSTD_rust_setFParamsState>() == 2 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_setFParams(
state: *const ZSTD_rust_setFParamsState,
fparams: ZSTD_frameParameters,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let parameters = [
(
ZSTD_C_CONTENT_SIZE_FLAG,
(fparams.contentSizeFlag != 0) as c_int,
),
(ZSTD_C_CHECKSUM_FLAG, (fparams.checksumFlag != 0) as c_int),
(ZSTD_C_DICT_ID_FLAG, (fparams.noDictIDFlag == 0) as c_int),
];
for (param, value) in parameters {
let result = unsafe { (state.set_parameter)(state.callback_context, param, value) };
if ERR_isError(result) {
return result;
}
}
0
}
type SetParamsCheckCParamsFn =
unsafe extern "C" fn(*mut c_void, ZSTD_compressionParameters) -> usize;
type SetParamsSetFParamsFn = unsafe extern "C" fn(*mut c_void, ZSTD_frameParameters) -> usize;
type SetParamsSetCParamsFn = unsafe extern "C" fn(*mut c_void, ZSTD_compressionParameters) -> usize;
/// Explicit projection for `ZSTD_CCtx_setParams`.
#[repr(C)]
pub struct ZSTD_rust_setParamsState {
callback_context: *mut c_void,
check_c_params: SetParamsCheckCParamsFn,
set_f_params: SetParamsSetFParamsFn,
set_c_params: SetParamsSetCParamsFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_setParamsState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_setParamsState, check_c_params) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_setParamsState, set_f_params) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_setParamsState, set_c_params) == 3 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_setParamsState>() == 4 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_setParams(
state: *const ZSTD_rust_setParamsState,
params: ZSTD_parameters,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let result = unsafe { (state.check_c_params)(state.callback_context, params.cParams) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_f_params)(state.callback_context, params.fParams) };
if ERR_isError(result) {
return result;
}
let result = unsafe { (state.set_c_params)(state.callback_context, params.cParams) };
if ERR_isError(result) {
return result;
}
0
}
type CompressStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize;
/// Explicit projection of the single-threaded `ZSTD_compressStream_generic`
/// state machine.
///
/// Rust owns buffering, output-drain policy, directive handling, and stream
/// stage transitions. Continue/end state projections retain the migrated
/// frame construction path; the opaque callback context remains only for
/// session reset.
#[repr(C)]
pub struct ZSTD_rust_compressStreamState {
callback_context: *mut c_void,
in_buffer_mode: c_int,
out_buffer_mode: c_int,
stream_stage: *mut c_int,
block_size_max: usize,
stable_in_not_consumed: *mut usize,
in_buff: *mut c_void,
in_buff_size: usize,
in_to_compress: *mut usize,
in_buff_pos: *mut usize,
in_buff_target: *mut usize,
out_buff: *mut c_void,
out_buff_size: usize,
out_buff_content_size: *mut usize,
out_buff_flushed_size: *mut usize,
frame_ended: *mut c_uint,
compress_continue_state: *const ZSTD_rust_compressContinueState,
compress_end_state: *const ZSTD_rust_compressEndState,
reset_session: CompressStreamResetFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStreamState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressStreamState, in_buffer_mode) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressStreamState, out_buffer_mode)
== size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, stream_stage)
== size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, block_size_max)
== 2 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, stable_in_not_consumed)
== 3 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, in_buff)
== 4 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, in_buff_size)
== 5 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, in_to_compress)
== 5 * size_of::<usize>() + 2 * size_of::<c_int>() + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, in_buff_pos)
== 6 * size_of::<usize>() + 2 * size_of::<c_int>() + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, in_buff_target)
== 7 * size_of::<usize>() + 2 * size_of::<c_int>() + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, out_buff)
== 9 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, out_buff_size)
== 9 * size_of::<usize>() + 2 * size_of::<c_int>() + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, out_buff_content_size)
== 9 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, out_buff_flushed_size)
== 10 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, frame_ended)
== 11 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, compress_continue_state)
== 12 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, compress_end_state)
== 13 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, reset_session)
== 14 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_compressStreamState>()
== 15 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
};
const ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER: c_int = 0;
const ZSTD_RUST_INIT_RESOLVE_LDM: c_int = 1;
const ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER: c_int = 2;
const ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES: c_int = 3;
const ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE: c_int = 4;
const ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH: c_int = 5;
type CompressStreamInitGetLocalDictFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitLocalDictState);
type CompressStreamInitCreateLocalDictFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitLocalDictState) -> usize;
type CompressStreamInitPublishLocalDictFn =
unsafe extern "C" fn(*mut c_void, *const ZSTD_rust_compressStreamInitLocalDictState);
type CompressStreamInitRefreshCDictFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitDictionaryState);
type CompressStreamInitClearPrefixFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitAssertDictionariesFn = unsafe extern "C" fn(*mut c_void, *const c_void);
type CompressStreamInitSetLevelFn = unsafe extern "C" fn(*mut c_void, c_int);
type CompressStreamInitDebugFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitGetPledgedFn = unsafe extern "C" fn(*mut c_void) -> u64;
type CompressStreamInitSetPledgedFn = unsafe extern "C" fn(*mut c_void, usize);
type CompressStreamInitGetCParamModeFn =
unsafe extern "C" fn(*mut c_void, *const c_void, u64) -> c_int;
type CompressStreamInitBuildCParamsFn = unsafe extern "C" fn(*mut c_void, u64, usize, c_int);
type CompressStreamInitResolveParamsFn = unsafe extern "C" fn(*mut c_void, c_int);
type CompressStreamInitGetNbWorkersFn = unsafe extern "C" fn(*mut c_void) -> c_uint;
type CompressStreamInitSetNbWorkersFn = unsafe extern "C" fn(*mut c_void, c_uint);
type CompressStreamInitHasExtSeqProdFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type CompressStreamInitTraceFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitGetMTContextFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
type CompressStreamInitCreateMTContextFn = unsafe extern "C" fn(*mut c_void, c_uint) -> usize;
type CompressStreamInitMTFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
*const ZSTD_rust_compressStreamInitDictionaryState,
*mut c_void,
u64,
) -> usize;
type CompressStreamInitCommitMTFn = unsafe extern "C" fn(
*mut c_void,
*const ZSTD_rust_compressStreamInitDictionaryState,
*mut c_void,
);
type CompressStreamInitCheckCParamsFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitBeginFn = unsafe extern "C" fn(
*mut c_void,
*const ZSTD_rust_compressStreamInitDictionaryState,
*mut c_void,
u64,
) -> usize;
type CompressStreamInitAssertOrdinaryFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitGetBufferModeFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type CompressStreamInitGetBlockSizeFn = unsafe extern "C" fn(*mut c_void) -> usize;
type CompressStreamInitCommitOrdinaryFn = unsafe extern "C" fn(*mut c_void, usize);
/// Private local-dictionary projection used by transparent stream initialization.
///
/// Rust owns the no-dictionary, already-initialized, create, and publish order;
/// C retains the private dictionary storage and allocator-sensitive callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressStreamInitLocalDictState {
dict: *const c_void,
dict_buffer: *mut c_void,
dict_size: usize,
dict_content_type: c_int,
local_cdict: *mut c_void,
cdict: *const c_void,
prefix_dict: *const c_void,
created_cdict: *mut c_void,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict) == 0);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict_buffer) == size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict_size) == 2 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitLocalDictState,
dict_content_type
) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, local_cdict)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, cdict) == 5 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, prefix_dict)
== 6 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, created_cdict)
== 7 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressStreamInitLocalDictState>() == size_of::<[usize; 8]>());
};
/// Scalar dictionary snapshot used by transparent stream initialization.
///
/// The prefix is populated by C before local-dictionary initialization, which
/// preserves the original single-use snapshot order. C refreshes only the
/// CDict fields after local-dictionary initialization because that operation
/// may create the local CDict.
#[repr(C)]
pub struct ZSTD_rust_compressStreamInitDictionaryState {
prefix_dict: *const c_void,
prefix_dict_size: usize,
prefix_dict_content_type: c_int,
cdict: *const c_void,
cdict_is_local: c_int,
cdict_compression_level: c_int,
cdict_dict_content_size: usize,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStreamInitDictionaryState, prefix_dict) == 0);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
prefix_dict_size
) == size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
prefix_dict_content_type
) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitDictionaryState, cdict) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitDictionaryState, cdict_is_local)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
cdict_compression_level
) == 4 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
cdict_dict_content_size
) == 4 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_compressStreamInitDictionaryState>()
== if size_of::<usize>() == 8 { 48 } else { 28 }
);
};
/// Projection for `ZSTD_CCtx_init_compressStream2()`.
///
/// Rust owns the ordering and branch policy. The C callback slots retain
/// private parameter/context layouts, local dictionary storage, allocators,
/// trace setup, MT construction/init, and codec/reset operations.
#[repr(C)]
pub struct ZSTD_rust_compressStreamInitState {
callback_context: *mut c_void,
params: *mut c_void,
dictionaries: *mut ZSTD_rust_compressStreamInitDictionaryState,
end_op: c_int,
in_size: usize,
multithreaded: c_int,
mt_job_size_min: usize,
get_local_dict: CompressStreamInitGetLocalDictFn,
create_local_dict: CompressStreamInitCreateLocalDictFn,
publish_local_dict: CompressStreamInitPublishLocalDictFn,
refresh_cdict: CompressStreamInitRefreshCDictFn,
clear_prefix: CompressStreamInitClearPrefixFn,
assert_dictionaries: CompressStreamInitAssertDictionariesFn,
set_compression_level: CompressStreamInitSetLevelFn,
debug_init: CompressStreamInitDebugFn,
get_pledged_src_size_plus_one: CompressStreamInitGetPledgedFn,
set_pledged_src_size: CompressStreamInitSetPledgedFn,
get_cparam_mode: CompressStreamInitGetCParamModeFn,
build_cparams: CompressStreamInitBuildCParamsFn,
resolve_params: CompressStreamInitResolveParamsFn,
get_nb_workers: CompressStreamInitGetNbWorkersFn,
set_nb_workers: CompressStreamInitSetNbWorkersFn,
has_ext_seq_prod: CompressStreamInitHasExtSeqProdFn,
trace_begin: CompressStreamInitTraceFn,
get_mt_context: CompressStreamInitGetMTContextFn,
create_mt_context: CompressStreamInitCreateMTContextFn,
init_mt: CompressStreamInitMTFn,
commit_mt: CompressStreamInitCommitMTFn,
check_cparams: CompressStreamInitCheckCParamsFn,
compress_begin: CompressStreamInitBeginFn,
assert_ordinary: CompressStreamInitAssertOrdinaryFn,
get_buffer_mode: CompressStreamInitGetBufferModeFn,
get_block_size: CompressStreamInitGetBlockSizeFn,
commit_ordinary: CompressStreamInitCommitOrdinaryFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStreamInitState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressStreamInitState, params) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressStreamInitState, dictionaries) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressStreamInitState, end_op) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressStreamInitState, in_size) == 4 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressStreamInitState, multithreaded)
== 4 * size_of::<usize>() + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitState, mt_job_size_min)
== 5 * size_of::<usize>() + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_compressStreamInitState>()
== if size_of::<usize>() == 8 { 272 } else { 144 }
);
};
#[inline]
unsafe fn compress_stream_init_local_dict_with(state: &ZSTD_rust_compressStreamInitState) -> usize {
let mut local_dict = ZSTD_rust_compressStreamInitLocalDictState {
dict: ptr::null(),
dict_buffer: ptr::null_mut(),
dict_size: 0,
dict_content_type: 0,
local_cdict: ptr::null_mut(),
cdict: ptr::null(),
prefix_dict: ptr::null(),
created_cdict: ptr::null_mut(),
};
unsafe {
(state.get_local_dict)(state.callback_context, &mut local_dict);
}
if local_dict.dict.is_null() {
debug_assert!(local_dict.dict_buffer.is_null());
debug_assert!(local_dict.local_cdict.is_null());
debug_assert_eq!(local_dict.dict_size, 0);
return 0;
}
if !local_dict.local_cdict.is_null() {
debug_assert_eq!(local_dict.cdict, local_dict.local_cdict.cast_const(),);
return 0;
}
debug_assert!(local_dict.dict_size > 0);
debug_assert!(local_dict.cdict.is_null());
debug_assert!(local_dict.prefix_dict.is_null());
let result = unsafe { (state.create_local_dict)(state.callback_context, &mut local_dict) };
if ERR_isError(result) {
return result;
}
debug_assert!(!local_dict.created_cdict.is_null());
unsafe {
(state.publish_local_dict)(state.callback_context, &local_dict);
}
0
}
#[inline]
unsafe fn compress_stream_init_body_with(state: &ZSTD_rust_compressStreamInitState) -> usize {
if state.params.is_null() || state.dictionaries.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let result = unsafe { compress_stream_init_local_dict_with(state) };
if ERR_isError(result) {
return result;
}
unsafe { (state.refresh_cdict)(state.callback_context, state.dictionaries) };
let dictionaries = unsafe { &*state.dictionaries };
unsafe { (state.clear_prefix)(state.callback_context) };
unsafe { (state.assert_dictionaries)(state.callback_context, dictionaries.prefix_dict) };
if !dictionaries.cdict.is_null() && dictionaries.cdict_is_local == 0 {
unsafe {
(state.set_compression_level)(state.params, dictionaries.cdict_compression_level)
};
}
unsafe { (state.debug_init)(state.callback_context) };
if state.end_op == ZSTD_E_END {
unsafe { (state.set_pledged_src_size)(state.callback_context, state.in_size) };
}
let pledged_src_size_plus_one =
unsafe { (state.get_pledged_src_size_plus_one)(state.callback_context) };
let pledged_src_size = pledged_src_size_plus_one.wrapping_sub(1);
let dict_size = if !dictionaries.prefix_dict.is_null() {
dictionaries.prefix_dict_size
} else if !dictionaries.cdict.is_null() {
dictionaries.cdict_dict_content_size
} else {
0
};
let mode =
unsafe { (state.get_cparam_mode)(state.params, dictionaries.cdict, pledged_src_size) };
unsafe {
(state.build_cparams)(state.params, pledged_src_size, dict_size, mode);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_LDM);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH);
}
if state.multithreaded != 0 {
let has_ext_seq_prod = unsafe { (state.has_ext_seq_prod)(state.params) };
let mut nb_workers = unsafe { (state.get_nb_workers)(state.params) };
if has_ext_seq_prod != 0 && nb_workers >= 1 {
return ERROR(ZstdErrorCode::ParameterCombinationUnsupported);
}
if pledged_src_size <= state.mt_job_size_min as u64 {
nb_workers = 0;
unsafe { (state.set_nb_workers)(state.params, 0) };
}
if nb_workers > 0 {
unsafe { (state.trace_begin)(state.callback_context) };
if unsafe { (state.get_mt_context)(state.callback_context) }.is_null() {
let result =
unsafe { (state.create_mt_context)(state.callback_context, nb_workers) };
if ERR_isError(result) {
return result;
}
}
let result = unsafe {
(state.init_mt)(
state.callback_context,
(state.get_mt_context)(state.callback_context),
dictionaries,
state.params,
pledged_src_size,
)
};
if ERR_isError(result) {
return result;
}
unsafe { (state.commit_mt)(state.callback_context, dictionaries, state.params) };
return 0;
}
}
unsafe { (state.check_cparams)(state.params) };
let result = unsafe {
(state.compress_begin)(
state.callback_context,
dictionaries,
state.params,
pledged_src_size,
)
};
if ERR_isError(result) {
return result;
}
unsafe { (state.assert_ordinary)(state.callback_context) };
let buffer_mode = unsafe { (state.get_buffer_mode)(state.callback_context) };
let block_size = unsafe { (state.get_block_size)(state.callback_context) };
let in_buff_target = if buffer_mode == ZSTD_BM_BUFFERED {
block_size.wrapping_add(usize::from(block_size as u64 == pledged_src_size))
} else {
0
};
unsafe { (state.commit_ordinary)(state.callback_context, in_buff_target) };
0
}
/// Rust-owned high-level policy for transparent stream initialization.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressStreamInit(
state: *const ZSTD_rust_compressStreamInitState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { compress_stream_init_body_with(&*state) }
}
#[inline]
unsafe fn stream_limit_copy(
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
let length = dst_capacity.min(src_size);
if length != 0 {
unsafe {
ptr::copy_nonoverlapping(src.cast::<u8>(), dst.cast::<u8>(), length);
}
}
length
}
#[inline]
unsafe fn compress_stream_generic_body_with(
state: &ZSTD_rust_compressStreamState,
output: &mut ZSTD_outBuffer,
input: &mut ZSTD_inBuffer,
flush_mode: c_int,
) -> usize {
debug_assert!((ZSTD_E_CONTINUE..=ZSTD_E_END).contains(&flush_mode));
debug_assert!(input.pos <= input.size);
debug_assert!(output.pos <= output.size);
debug_assert!(!input.src.is_null() || input.size == 0);
debug_assert!(!output.dst.is_null() || output.size == 0);
if state.stream_stage.is_null()
|| state.stable_in_not_consumed.is_null()
|| state.in_to_compress.is_null()
|| state.in_buff_pos.is_null()
|| state.in_buff_target.is_null()
|| state.out_buff_content_size.is_null()
|| state.out_buff_flushed_size.is_null()
|| state.frame_ended.is_null()
|| state.compress_continue_state.is_null()
|| state.compress_end_state.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
if state.in_buffer_mode == ZSTD_BM_STABLE {
let stable = unsafe { *state.stable_in_not_consumed };
debug_assert!(input.pos >= stable);
input.pos = input.pos.wrapping_sub(stable);
*unsafe { state.stable_in_not_consumed.as_mut().unwrap_unchecked() } = 0;
}
debug_assert!(
state.in_buffer_mode == ZSTD_BM_BUFFERED || state.in_buffer_mode == ZSTD_BM_STABLE
);
if state.in_buffer_mode == ZSTD_BM_BUFFERED {
debug_assert!(!state.in_buff.is_null());
debug_assert!(state.in_buff_size != 0);
}
if state.out_buffer_mode == ZSTD_BM_BUFFERED {
debug_assert!(!state.out_buff.is_null());
debug_assert!(state.out_buff_size != 0);
}
let mut some_more_work = true;
while some_more_work {
let stage = unsafe { *state.stream_stage };
match stage {
ZSTD_CSTREAM_STAGE_INIT => return ERROR(ZstdErrorCode::InitMissing),
ZSTD_CSTREAM_STAGE_LOAD => {
let input_remaining = input.size - input.pos;
let output_remaining = output.size - output.pos;
let bound = crate::zstd_compress_api::ZSTD_compressBound(input_remaining);
if flush_mode == ZSTD_E_END
&& (output_remaining >= bound || state.out_buffer_mode == ZSTD_BM_STABLE)
&& unsafe { *state.in_buff_pos } == 0
{
let dst = if output.dst.is_null() {
ptr::null_mut()
} else {
unsafe { output.dst.cast::<u8>().add(output.pos).cast() }
};
let src = if input.src.is_null() {
ptr::null()
} else {
unsafe { input.src.cast::<u8>().add(input.pos).cast() }
};
let c_size = unsafe {
ZSTD_rust_compressEnd(
state.compress_end_state,
dst,
output_remaining,
src,
input_remaining,
)
};
if ERR_isError(c_size) {
return c_size;
}
input.pos = input.size;
output.pos += c_size;
unsafe { *state.frame_ended = 1 };
unsafe { (state.reset_session)(state.callback_context) };
some_more_work = false;
continue;
}
if state.in_buffer_mode == ZSTD_BM_BUFFERED {
let to_load =
unsafe { (*state.in_buff_target).wrapping_sub(*state.in_buff_pos) };
let src = if input.src.is_null() {
ptr::null()
} else {
unsafe { input.src.cast::<u8>().add(input.pos).cast() }
};
let dst = if state.in_buff.is_null() {
ptr::null_mut()
} else {
unsafe { state.in_buff.cast::<u8>().add(*state.in_buff_pos).cast() }
};
let loaded = unsafe { stream_limit_copy(dst, to_load, src, input_remaining) };
input.pos += loaded;
unsafe { *state.in_buff_pos += loaded };
if flush_mode == ZSTD_E_CONTINUE
&& unsafe { *state.in_buff_pos < *state.in_buff_target }
{
some_more_work = false;
continue;
}
if flush_mode == ZSTD_E_FLUSH
&& unsafe { *state.in_buff_pos == *state.in_to_compress }
{
some_more_work = false;
continue;
}
} else {
debug_assert_eq!(state.in_buffer_mode, ZSTD_BM_STABLE);
let input_remaining = input.size - input.pos;
if flush_mode == ZSTD_E_CONTINUE && input_remaining < state.block_size_max {
unsafe { *state.stable_in_not_consumed = input_remaining };
input.pos = input.size;
some_more_work = false;
continue;
}
if flush_mode == ZSTD_E_FLUSH && input.pos == input.size {
some_more_work = false;
continue;
}
}
let input_buffered = state.in_buffer_mode == ZSTD_BM_BUFFERED;
let mut output_size = output.size - output.pos;
let input_size = if input_buffered {
unsafe { (*state.in_buff_pos).wrapping_sub(*state.in_to_compress) }
} else {
(input.size - input.pos).min(state.block_size_max)
};
let output_dst = if output_size
>= crate::zstd_compress_api::ZSTD_compressBound(input_size)
|| state.out_buffer_mode == ZSTD_BM_STABLE
{
if output.dst.is_null() {
ptr::null_mut()
} else {
unsafe { output.dst.cast::<u8>().add(output.pos).cast() }
}
} else {
output_size = state.out_buff_size;
state.out_buff
};
let last_block = flush_mode == ZSTD_E_END
&& if input_buffered {
input.pos == input.size
} else {
input.pos + input_size == input.size
};
let source = if input_buffered {
if state.in_buff.is_null() {
ptr::null()
} else {
unsafe { state.in_buff.cast::<u8>().add(*state.in_to_compress).cast() }
}
} else if input.src.is_null() {
ptr::null()
} else {
unsafe { input.src.cast::<u8>().add(input.pos).cast() }
};
let c_size = unsafe {
if last_block {
ZSTD_rust_compressEnd(
state.compress_end_state,
output_dst,
output_size,
source,
input_size,
)
} else {
ZSTD_rust_compressContinue(
state.compress_continue_state,
output_dst,
output_size,
source,
input_size,
1,
0,
)
}
};
if !input_buffered {
/* Match C: stable input is consumed before forwarding a
* compression error. */
input.pos += input_size;
}
if ERR_isError(c_size) {
return c_size;
}
unsafe { *state.frame_ended = c_uint::from(last_block) };
if input_buffered {
unsafe {
*state.in_buff_target =
(*state.in_buff_pos).wrapping_add(state.block_size_max);
if *state.in_buff_target > state.in_buff_size {
*state.in_buff_pos = 0;
*state.in_buff_target = state.block_size_max;
}
*state.in_to_compress = *state.in_buff_pos;
}
}
let current_output = if output.dst.is_null() {
ptr::null_mut()
} else {
unsafe { output.dst.cast::<u8>().add(output.pos).cast() }
};
if output_dst == current_output {
output.pos += c_size;
if unsafe { *state.frame_ended } != 0 {
unsafe { (state.reset_session)(state.callback_context) };
some_more_work = false;
}
continue;
}
unsafe {
*state.out_buff_content_size = c_size;
*state.out_buff_flushed_size = 0;
*state.stream_stage = ZSTD_CSTREAM_STAGE_FLUSH;
}
}
ZSTD_CSTREAM_STAGE_FLUSH => {
debug_assert_eq!(state.out_buffer_mode, ZSTD_BM_BUFFERED);
let to_flush = unsafe {
(*state.out_buff_content_size).wrapping_sub(*state.out_buff_flushed_size)
};
let src = if state.out_buff.is_null() {
ptr::null()
} else {
unsafe {
state
.out_buff
.cast::<u8>()
.add(*state.out_buff_flushed_size)
.cast()
}
};
let dst = if output.dst.is_null() {
ptr::null_mut()
} else {
unsafe { output.dst.cast::<u8>().add(output.pos).cast() }
};
let flushed =
unsafe { stream_limit_copy(dst, output.size - output.pos, src, to_flush) };
output.pos += flushed;
unsafe { *state.out_buff_flushed_size += flushed };
if to_flush != flushed {
some_more_work = false;
continue;
}
unsafe {
*state.out_buff_content_size = 0;
*state.out_buff_flushed_size = 0;
}
if unsafe { *state.frame_ended } != 0 {
unsafe { (state.reset_session)(state.callback_context) };
some_more_work = false;
continue;
}
unsafe { *state.stream_stage = ZSTD_CSTREAM_STAGE_LOAD };
}
_ => {
debug_assert!(false, "invalid C stream stage");
return ERROR(ZstdErrorCode::StageWrong);
}
}
}
if unsafe { *state.frame_ended } != 0 {
return 0;
}
next_input_size_hint(
state.in_buffer_mode,
state.block_size_max,
unsafe { *state.stable_in_not_consumed },
unsafe { *state.in_buff_target },
unsafe { *state.in_buff_pos },
)
}
/// Drive the single-threaded stream state machine through a C-owned context.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressStreamGeneric(
state: *const ZSTD_rust_compressStreamState,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
flush_mode: c_int,
) -> usize {
if state.is_null() || output.is_null() || input.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { compress_stream_generic_body_with(&*state, &mut *output, &mut *input, flush_mode) }
}
/// Explicit projection of the state used by `ZSTD_compressSequences_internal`.
///
/// The C context and its function-pointer-bearing parameter structure remain
/// private to C. Only the sequence store, compressed-block state slots, and
/// scalar policy/workspace fields read by the per-block loop cross the ABI.
#[repr(C)]
pub struct ZSTD_rust_sequenceCompressionState {
seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
tmp_workspace: *mut c_void,
tmp_wksp_size: usize,
block_size_max: usize,
bmi2: c_int,
block_delimiters: c_int,
strategy: c_int,
disable_literal_compression: c_int,
search_for_external_repcodes: c_int,
validate_sequences: c_int,
min_match: c_uint,
window_log: c_uint,
dict_size: c_uint,
use_sequence_producer: c_int,
is_first_block: *mut c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_sequenceCompressionState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_sequenceCompressionState, prev_c_block) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceCompressionState, next_c_block) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_sequenceCompressionState, tmp_workspace) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_sequenceCompressionState, tmp_wksp_size) == 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_sequenceCompressionState, block_size_max) == 5 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_sequenceCompressionState, bmi2) == 6 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_sequenceCompressionState, min_match)
== 6 * size_of::<usize>() + 6 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_sequenceCompressionState, use_sequence_producer)
== 6 * size_of::<usize>() + 6 * size_of::<c_int>() + 3 * size_of::<c_uint>()
);
assert!(
offset_of!(ZSTD_rust_sequenceCompressionState, is_first_block)
== 6 * size_of::<usize>() + 7 * size_of::<c_int>() + 3 * size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_sequenceCompressionState>()
== if size_of::<usize>() == 8 { 96 } else { 68 }
);
};
/// C callback signature for the CCtx-dependent sequence conversion leaf.
type SequenceLiteralsConvertFn =
unsafe extern "C" fn(*mut c_void, *const ZSTD_Sequence, usize, c_int) -> usize;
/// Explicit projection of the state used by
/// ZSTD_compressSequencesAndLiterals_internal.
///
/// CCtx initialization, parameter validation, and the sequence conversion
/// callback remain in C. Rust owns the block loop, external literal cursor,
/// entropy invocation, block framing, and per-frame completion checks.
#[repr(C)]
pub struct ZSTD_rust_sequenceLiteralsState {
seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
tmp_workspace: *mut c_void,
tmp_wksp_size: usize,
block_size_max: usize,
bmi2: c_int,
strategy: c_int,
disable_literal_compression: c_int,
repcode_resolution: c_int,
is_first_block: *mut c_int,
callback_context: *mut c_void,
convert_block_sequences: SequenceLiteralsConvertFn,
}
/// Explicit projection for the public block-sequence conversion entry point.
/// The surrounding `ZSTD_CCtx` remains opaque; only the sequence store and
/// compressed-block repcode slots are needed by the converter.
#[repr(C)]
pub struct ZSTD_rust_convertBlockSequencesState {
seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_convertBlockSequencesState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_convertBlockSequencesState, prev_c_block) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_convertBlockSequencesState, next_c_block) == 2 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_convertBlockSequencesState>() == 3 * size_of::<usize>());
};
const _: () = {
assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, prev_c_block) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, next_c_block) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, tmp_workspace) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, tmp_wksp_size) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, block_size_max) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, bmi2) == 6 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_sequenceLiteralsState, strategy)
== 6 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_sequenceLiteralsState, disable_literal_compression)
== 6 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_sequenceLiteralsState, repcode_resolution)
== 6 * size_of::<usize>() + 3 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_sequenceLiteralsState, is_first_block)
== 6 * size_of::<usize>() + 4 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_sequenceLiteralsState, callback_context)
== 6 * size_of::<usize>() + 4 * size_of::<c_int>() + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_sequenceLiteralsState, convert_block_sequences)
== 6 * size_of::<usize>() + 4 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_sequenceLiteralsState>()
== 6 * size_of::<usize>() + 4 * size_of::<c_int>() + 3 * size_of::<usize>()
);
};
type SequenceApiInitFn =
unsafe extern "C" fn(*mut c_void, usize, *mut ZSTD_rust_sequenceApiState) -> usize;
/// Explicit projection for the public sequence-compression API orchestration.
///
/// Rust owns validation ordering, frame-header/checksum sequencing, and
/// output accounting. Frame-header parameters are projected as scalars; C
/// retains the private CCtx and sequence-store/block layouts through the
/// initialization callback and two block-state projections. The XXH64 state
/// is projected directly so checksum updates and serialization stay in Rust.
#[repr(C)]
pub struct ZSTD_rust_sequenceApiState {
callback_context: *mut c_void,
sequence_state: *mut ZSTD_rust_sequenceCompressionState,
sequence_literals_state: *mut ZSTD_rust_sequenceLiteralsState,
init: SequenceApiInitFn,
checksum_state: *mut XXH64_state_t,
checksum_flag: c_int,
block_delimiters: c_int,
validate_sequences: c_int,
no_dict_id_flag: c_int,
content_size_flag: c_int,
format: c_int,
window_log: c_uint,
dict_id: c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_sequenceApiState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_sequenceApiState, sequence_state) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_sequenceApiState, sequence_literals_state) == 2 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_sequenceApiState, init) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, checksum_state) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, checksum_flag) == size_of::<[usize; 5]>());
assert!(
offset_of!(ZSTD_rust_sequenceApiState, block_delimiters)
== size_of::<[usize; 5]>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_sequenceApiState, validate_sequences)
== size_of::<[usize; 5]>() + size_of::<[c_int; 2]>()
);
assert!(
offset_of!(ZSTD_rust_sequenceApiState, no_dict_id_flag)
== size_of::<[usize; 5]>() + size_of::<[c_int; 3]>()
);
assert!(
offset_of!(ZSTD_rust_sequenceApiState, content_size_flag)
== size_of::<[usize; 5]>() + size_of::<[c_int; 4]>()
);
assert!(
offset_of!(ZSTD_rust_sequenceApiState, format)
== size_of::<[usize; 5]>() + size_of::<[c_int; 5]>()
);
assert!(
offset_of!(ZSTD_rust_sequenceApiState, window_log)
== size_of::<[usize; 5]>() + size_of::<[c_int; 6]>()
);
assert!(
offset_of!(ZSTD_rust_sequenceApiState, dict_id)
== size_of::<[usize; 5]>() + size_of::<[c_int; 6]>() + size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_sequenceApiState>() == if size_of::<usize>() == 8 { 72 } else { 52 }
);
};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct SequenceApiPlan {
update_input_checksum: bool,
append_frame_checksum: bool,
}
/// Apply the public sequence API's post-initialization validation policy.
///
/// The order is intentional and matches the C entry point: the literals
/// variant rejects no-delimiter mode first, then sequence validation, then a
/// frame checksum. The ordinary variant permits all three independently.
fn sequence_api_plan(
with_literals: bool,
block_delimiters: c_int,
validate_sequences: c_int,
checksum_flag: c_int,
) -> Result<SequenceApiPlan, usize> {
if with_literals {
if block_delimiters == ZSTD_SF_NO_BLOCK_DELIMITERS {
return Err(ERROR(ZstdErrorCode::FrameParameterUnsupported));
}
if validate_sequences != 0 {
return Err(ERROR(ZstdErrorCode::ParameterUnsupported));
}
if checksum_flag != 0 {
return Err(ERROR(ZstdErrorCode::FrameParameterUnsupported));
}
return Ok(SequenceApiPlan {
update_input_checksum: false,
append_frame_checksum: false,
});
}
Ok(SequenceApiPlan {
update_input_checksum: checksum_flag != 0,
append_frame_checksum: checksum_flag != 0,
})
}
#[inline]
fn sequence_api_validate_literal_capacity(lit_size: usize, lit_capacity: usize) -> usize {
if lit_capacity < lit_size {
ERROR(ZstdErrorCode::WorkSpaceTooSmall)
} else {
0
}
}
/// Explicit projection of the state used by `ZSTD_compressSeqStore_singleBlock`.
///
/// Sequence-store construction and split discovery remain in C. Only the
/// sequence store, simulated repcode histories, compressed-block state slots,
/// sequence collector, and scalar compression settings cross the ABI.
#[repr(C)]
pub struct ZSTD_rust_seqStoreSingleBlockState {
seq_store: *const SeqStore_t,
d_rep: *mut u32,
c_rep: *mut u32,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
tmp_workspace: *mut c_void,
tmp_wksp_size: usize,
seq_collector: *mut SeqCollector,
strategy: c_int,
disable_literal_compression: c_int,
bmi2: c_int,
is_first_block: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, d_rep) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, c_rep) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, prev_c_block) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, next_c_block) == 4 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_seqStoreSingleBlockState, tmp_workspace) == 5 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_seqStoreSingleBlockState, tmp_wksp_size) == 6 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_seqStoreSingleBlockState, seq_collector) == 7 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, strategy) == usize::BITS as usize);
assert!(
offset_of!(
ZSTD_rust_seqStoreSingleBlockState,
disable_literal_compression
) == usize::BITS as usize + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_seqStoreSingleBlockState, bmi2)
== usize::BITS as usize + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_seqStoreSingleBlockState, is_first_block)
== usize::BITS as usize + 3 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_seqStoreSingleBlockState>()
== usize::BITS as usize + 4 * size_of::<c_int>()
);
};
/// Explicit projection of the state used by the post-split partition loop.
///
/// The C caller still owns the private compression context and derives the
/// partition boundaries. Rust owns the partition accounting, repcode
/// simulation, and repeated single-block dispatch.
#[repr(C)]
pub struct ZSTD_rust_splitBlockState {
seq_store: *const SeqStore_t,
partitions: *const u32,
next_seq_store: *mut SeqStore_t,
curr_seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
tmp_workspace: *mut c_void,
tmp_wksp_size: usize,
seq_collector: *mut SeqCollector,
block_size_max: usize,
strategy: c_int,
disable_literal_compression: c_int,
bmi2: c_int,
is_first_block: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_splitBlockState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_splitBlockState, partitions) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, next_seq_store) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, curr_seq_store) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, prev_c_block) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, next_c_block) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, tmp_workspace) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, tmp_wksp_size) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, seq_collector) == usize::BITS as usize);
assert!(offset_of!(ZSTD_rust_splitBlockState, block_size_max) == 9 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_splitBlockState, strategy) == 10 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_splitBlockState, disable_literal_compression)
== 10 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_splitBlockState, bmi2)
== 10 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_splitBlockState, is_first_block)
== 10 * size_of::<usize>() + 3 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_splitBlockState>() == 10 * size_of::<usize>() + 4 * size_of::<c_int>()
);
};
/// Explicit projection of the state used by `ZSTD_compressBlock_internal`.
///
/// Sequence-store construction enters through an explicit build-state
/// projection. Rust owns sequence collection, entropy emission, the legacy
/// first-block RLE gate, and the compressed-block state transitions after the
/// store is ready.
#[repr(C)]
pub struct ZSTD_rust_blockInternalState {
seq_store: *const SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
tmp_workspace: *mut c_void,
tmp_wksp_size: usize,
seq_collector: *mut SeqCollector,
strategy: c_int,
disable_literal_compression: c_int,
bmi2: c_int,
is_first_block: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_blockInternalState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_blockInternalState, prev_c_block) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_blockInternalState, next_c_block) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_blockInternalState, tmp_workspace) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_blockInternalState, tmp_wksp_size) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_blockInternalState, seq_collector) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_blockInternalState, strategy) == 6 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_blockInternalState, disable_literal_compression)
== 6 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_blockInternalState, bmi2)
== 6 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_blockInternalState, is_first_block)
== 6 * size_of::<usize>() + 3 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_blockInternalState>()
== 6 * size_of::<usize>() + 4 * size_of::<c_int>()
);
};
/// Explicit projection of the state used by the target-sized block body.
///
/// Sequence-store construction and the matchfinder remain in C. This state
/// contains only the already-Rust-owned block-compression inputs and the two
/// pointer slots that the compressed-block confirmation leaf swaps.
#[repr(C)]
pub struct ZSTD_rust_targetCBlockSizeState {
seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
tmp_workspace: *mut c_void,
tmp_wksp_size: usize,
strategy: c_int,
disable_literal_compression: c_int,
bmi2: c_int,
window_log: c_uint,
target_c_block_size: usize,
is_first_block: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, seq_store) == 0);
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, prev_c_block) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, next_c_block) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, tmp_workspace) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, tmp_wksp_size) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, strategy) == 5 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_targetCBlockSizeState, disable_literal_compression)
== 5 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_targetCBlockSizeState, bmi2)
== 5 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_targetCBlockSizeState, window_log)
== 5 * size_of::<usize>() + 3 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_targetCBlockSizeState, target_c_block_size)
== 5 * size_of::<usize>() + 4 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_targetCBlockSizeState, is_first_block)
== 5 * size_of::<usize>() + 4 * size_of::<c_int>() + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_targetCBlockSizeState>()
== if size_of::<usize>() == 8 { 72 } else { 44 }
);
};
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum TargetCBlockAction {
Raw = 0,
Rle = 1,
Compressed = 2,
Error = 3,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum SequenceBlockAction {
Raw,
Rle,
Compressed,
}
type SingleBlockEntropyFn = unsafe extern "C" fn(
*const SeqStore_t,
*const ZSTD_entropyCTables_t,
*mut ZSTD_entropyCTables_t,
c_int,
c_int,
*mut c_void,
usize,
usize,
*mut c_void,
usize,
c_int,
) -> usize;
type SingleBlockResolveOffCodesFn =
unsafe extern "C" fn(*mut u32, *mut u32, *const SeqStore_t, c_uint);
type SingleBlockCopySequencesFn =
unsafe extern "C" fn(*mut SeqCollector, *const SeqStore_t, *const u32) -> usize;
type SingleBlockConfirmFn = unsafe extern "C" fn(
*mut *mut ZSTD_compressedBlockState_t,
*mut *mut ZSTD_compressedBlockState_t,
);
type SingleBlockRawFn =
unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_uint) -> usize;
type SingleBlockRleFn = unsafe extern "C" fn(*mut c_void, usize, u8, usize, c_uint) -> usize;
type SingleBlockWriteHeaderFn = unsafe extern "C" fn(*mut c_void, usize, usize, c_uint);
type SingleBlockIsRleFn = unsafe extern "C" fn(*const u8, usize) -> c_int;
#[derive(Clone, Copy)]
struct SingleBlockSeams {
entropy_compress: SingleBlockEntropyFn,
resolve_off_codes: SingleBlockResolveOffCodesFn,
copy_sequences: SingleBlockCopySequencesFn,
confirm: SingleBlockConfirmFn,
raw: SingleBlockRawFn,
rle: SingleBlockRleFn,
write_header: SingleBlockWriteHeaderFn,
is_rle: SingleBlockIsRleFn,
}
impl SingleBlockSeams {
fn production() -> Self {
Self {
entropy_compress: ZSTD_rust_entropyCompressSeqStore,
resolve_off_codes: ZSTD_rust_seqStore_resolveOffCodes,
copy_sequences: ZSTD_rust_copyBlockSequences,
confirm: ZSTD_rust_confirmRepcodesAndEntropyTables,
raw: ZSTD_rust_noCompressBlock,
rle: ZSTD_rust_rleCompressBlock,
write_header: ZSTD_rust_writeBlockHeader,
is_rle: ZSTD_rust_isRLE,
}
}
}
/// Rust-owned sequence-store branch policy. C callbacks perform the
/// operations which need the private matchfinder or CCtx
/// parameter/function-pointer state.
#[allow(clippy::too_many_arguments)]
unsafe fn build_seq_store_select_sequences_body_with(
state: &ZSTD_rust_buildSeqStoreState,
seq_store: *mut SeqStore_t,
next_rep: *mut u32,
src: *const c_void,
src_size: usize,
seq_store_complete: &mut c_int,
) -> usize {
if seq_store.is_null()
|| next_rep.is_null()
|| src.is_null()
|| state.compress_external_sequences as usize == 0
|| state.compress_ldm as usize == 0
|| state.try_external_sequence_producer as usize == 0
|| state.compress_internal as usize == 0
|| state.clear_ldm_seq_store as usize == 0
{
return ERROR(ZstdErrorCode::Generic);
}
*seq_store_complete = 0;
if state.has_external_sequences != 0 {
if state.has_external_sequence_producer != 0 {
return ERROR(ZstdErrorCode::ParameterCombinationUnsupported);
}
return unsafe {
(state.compress_external_sequences)(
state.callback_context,
seq_store,
next_rep,
src,
src_size,
)
};
}
if state.ldm_enabled != 0 {
if state.has_external_sequence_producer != 0 {
return ERROR(ZstdErrorCode::ParameterCombinationUnsupported);
}
return unsafe {
(state.compress_ldm)(state.callback_context, seq_store, next_rep, src, src_size)
};
}
if state.has_external_sequence_producer != 0 {
let mut allow_fallback = 0;
let producer_result = unsafe {
(state.try_external_sequence_producer)(
state.callback_context,
src,
src_size,
seq_store_complete,
&mut allow_fallback,
)
};
if *seq_store_complete != 0 {
unsafe { (state.clear_ldm_seq_store)(state.callback_context) };
return producer_result;
}
if allow_fallback == 0 || state.enable_match_finder_fallback == 0 {
return producer_result;
}
unsafe { (state.clear_ldm_seq_store)(state.callback_context) };
return unsafe {
(state.compress_internal)(state.callback_context, seq_store, next_rep, src, src_size)
};
}
unsafe { (state.clear_ldm_seq_store)(state.callback_context) };
unsafe { (state.compress_internal)(state.callback_context, seq_store, next_rep, src, src_size) }
}
/// Rust-owned sequence-store boundary. C callbacks perform the operations
/// which need the private matchfinder or CCtx parameter/function-pointer state.
#[allow(clippy::too_many_arguments)]
unsafe fn build_seq_store_body_with(
state: &ZSTD_rust_buildSeqStoreState,
src: *const c_void,
src_size: usize,
) -> usize {
if state.seq_store.is_null()
|| state.skip_small_block as usize == 0
|| state.prepare_match_state as usize == 0
{
return ERROR(ZstdErrorCode::Generic);
}
if src_size > ZSTD_BLOCKSIZE_MAX {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if src_size < MIN_COMPRESSIBLE_BLOCK_SIZE {
unsafe { (state.skip_small_block)(state.callback_context, src_size) };
return ZSTD_BSS_NO_COMPRESS as usize;
}
if src.is_null() || state.prev_c_block.is_null() || state.next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *state.prev_c_block };
let next_c_block = unsafe { *state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { ZSTD_rust_resetSeqStore(state.seq_store) };
unsafe {
(state.prepare_match_state)(state.callback_context, src, src_size);
}
unsafe {
ptr::copy_nonoverlapping(
(*prev_c_block).rep.as_ptr(),
(*next_c_block).rep.as_mut_ptr(),
ZSTD_REP_NUM,
);
}
let mut seq_store_complete = 0;
let last_literals_size = unsafe {
build_seq_store_select_sequences_body_with(
state,
state.seq_store,
(*next_c_block).rep.as_mut_ptr(),
src,
src_size,
&mut seq_store_complete,
)
};
if ERR_isError(last_literals_size) {
return last_literals_size;
}
if seq_store_complete != 0 {
return ZSTD_BSS_COMPRESS as usize;
}
if last_literals_size > src_size {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
ZSTD_rust_storeLastLiterals(
state.seq_store,
src.cast::<u8>().add(src_size - last_literals_size),
last_literals_size,
);
}
if state.validate_seq_store != 0 {
unsafe { ZSTD_rust_validateSeqStore(state.seq_store, state.min_match) };
}
ZSTD_BSS_COMPRESS as usize
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_buildSeqStore(
state: *const ZSTD_rust_buildSeqStoreState,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { build_seq_store_body_with(&*state, src, src_size) }
}
/// Rust implementation of `ZSTD_compressBlock_internal` after the C caller
/// has built the sequence store.
#[allow(clippy::too_many_arguments)]
unsafe fn compress_block_internal_body_with(
state: &ZSTD_rust_blockInternalState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
frame: c_uint,
seams: SingleBlockSeams,
) -> usize {
const RLE_MAX_LENGTH: usize = 25;
const FSE_REPEAT_CHECK: c_int = 1;
const FSE_REPEAT_VALID: c_int = 2;
if state.seq_store.is_null()
|| state.prev_c_block.is_null()
|| state.next_c_block.is_null()
|| state.seq_collector.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *state.prev_c_block };
let next_c_block = unsafe { *state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*state.seq_collector).collectSequences } != 0 {
let result = unsafe {
(seams.copy_sequences)(
state.seq_collector,
state.seq_store,
(*prev_c_block).rep.as_ptr(),
)
};
if ERR_isError(result) {
return result;
}
unsafe { (seams.confirm)(state.prev_c_block, state.next_c_block) };
return 0;
}
let mut c_size = unsafe {
(seams.entropy_compress)(
state.seq_store,
ptr::addr_of!((*prev_c_block).entropy),
ptr::addr_of_mut!((*next_c_block).entropy),
state.strategy,
state.disable_literal_compression,
dst,
dst_capacity,
src_size,
state.tmp_workspace,
state.tmp_wksp_size,
state.bmi2,
)
};
if frame != 0
&& state.is_first_block == 0
&& c_size < RLE_MAX_LENGTH
&& unsafe { (seams.is_rle)(src.cast(), src_size) } != 0
{
/* Preserve the legacy decoder compatibility rule for the first frame
* block: later repeated blocks may use the one-byte RLE form. */
c_size = 1;
unsafe {
*dst.cast::<u8>() = *src.cast::<u8>();
}
}
if !ERR_isError(c_size) && c_size > 1 {
unsafe { (seams.confirm)(state.prev_c_block, state.next_c_block) };
}
let prev_c_block = unsafe { *state.prev_c_block };
if prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode } == FSE_REPEAT_VALID {
unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode = FSE_REPEAT_CHECK };
}
c_size
}
unsafe fn compress_block_internal_after_build_body_with(
state: &ZSTD_rust_blockInternalState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
frame: c_uint,
bss: c_int,
seams: SingleBlockSeams,
) -> usize {
if bss == ZSTD_BSS_NO_COMPRESS {
if state.seq_collector.is_null() || state.prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*state.seq_collector).collectSequences } != 0 {
return ERROR(ZstdErrorCode::SequenceProducerFailed);
}
let prev_c_block = unsafe { *state.prev_c_block };
if prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode } == FSE_REPEAT_VALID {
unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode = FSE_REPEAT_CHECK };
}
return 0;
}
if bss != ZSTD_BSS_COMPRESS {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_internal_body_with(state, dst, dst_capacity, src, src_size, frame, seams)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBlockInternal(
state: *const ZSTD_rust_blockInternalState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
frame: c_uint,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_internal_body_with(
&*state,
dst,
dst_capacity,
src,
src_size,
frame,
SingleBlockSeams::production(),
)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBlockInternalAfterBuild(
state: *const ZSTD_rust_blockInternalState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
frame: c_uint,
bss: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_internal_after_build_body_with(
&*state,
dst,
dst_capacity,
src,
src_size,
frame,
bss,
SingleBlockSeams::production(),
)
}
}
#[inline]
fn sequence_block_action(
is_first_block: c_int,
maybe_rle: c_int,
is_rle: c_int,
compressed_size: usize,
) -> SequenceBlockAction {
if is_first_block == 0 && maybe_rle != 0 && is_rle != 0 {
return SequenceBlockAction::Rle;
}
match compressed_size {
0 => SequenceBlockAction::Raw,
1 => SequenceBlockAction::Rle,
_ => SequenceBlockAction::Compressed,
}
}
#[inline]
fn target_c_block_size_action(
bss: c_int,
is_first_block: c_int,
maybe_rle: c_int,
is_rle: c_int,
c_size: usize,
src_size: usize,
strategy: c_int,
) -> TargetCBlockAction {
if bss != ZSTD_TARGET_CBLOCK_BSS_COMPRESS {
return TargetCBlockAction::Raw;
}
if is_first_block == 0 && maybe_rle != 0 && is_rle != 0 {
return TargetCBlockAction::Rle;
}
if c_size == 0 || c_size == ERROR(ZstdErrorCode::DstSizeTooSmall) {
return TargetCBlockAction::Raw;
}
if ERR_isError(c_size) {
return TargetCBlockAction::Error;
}
let max_c_size = src_size.wrapping_sub(min_gain(src_size, strategy));
if c_size < max_c_size.wrapping_add(ZSTD_BLOCK_HEADER_SIZE) {
TargetCBlockAction::Compressed
} else {
TargetCBlockAction::Raw
}
}
type TargetCBlockSuperBlockFn = unsafe extern "C" fn(
*const c_void,
*const c_void,
*mut c_void,
c_int,
c_int,
*mut c_void,
usize,
c_int,
c_uint,
usize,
*mut c_void,
usize,
*const c_void,
usize,
c_uint,
) -> usize;
/// Rust implementation of `ZSTD_compressBlock_targetCBlockSize_body()`.
///
/// The C caller has already built the sequence store. This function owns
/// only the RLE/superblock/raw decision and leaves context construction,
/// matchfinder state, frame progress, and outer repeat-mode cleanup in C.
#[allow(clippy::too_many_arguments)]
unsafe fn compress_block_target_c_block_size_body_with(
state: &ZSTD_rust_targetCBlockSizeState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
bss: c_int,
last_block: c_uint,
compress_super_block: TargetCBlockSuperBlockFn,
) -> usize {
if state.seq_store.is_null() || state.prev_c_block.is_null() || state.next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *state.prev_c_block };
let next_c_block = unsafe { *state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let is_compress = bss == ZSTD_TARGET_CBLOCK_BSS_COMPRESS;
let (maybe_rle, is_rle) = if is_compress {
let maybe_rle = unsafe { ZSTD_rust_maybeRLE(state.seq_store) };
let is_rle = unsafe { ZSTD_rust_isRLE(src.cast(), src_size) };
(maybe_rle, is_rle)
} else {
(0, 0)
};
let action = target_c_block_size_action(
bss,
state.is_first_block,
maybe_rle,
is_rle,
0,
src_size,
state.strategy,
);
if action == TargetCBlockAction::Rle {
return unsafe {
ZSTD_rust_rleCompressBlock(dst, dst_capacity, *src.cast::<u8>(), src_size, last_block)
};
}
if is_compress {
/* The superblock result is not bounded by ZSTD_compressBound(). The
* policy helper therefore falls back to a raw block for zero,
* dstSize_tooSmall, or an expansion beyond blockBound(srcSize). */
let c_size = unsafe {
compress_super_block(
state.seq_store.cast(),
prev_c_block.cast(),
next_c_block.cast(),
state.strategy,
state.disable_literal_compression,
state.tmp_workspace,
state.tmp_wksp_size,
state.bmi2,
state.window_log,
state.target_c_block_size,
dst,
dst_capacity,
src,
src_size,
last_block,
)
};
let action = target_c_block_size_action(
bss,
state.is_first_block,
0,
0,
c_size,
src_size,
state.strategy,
);
if action == TargetCBlockAction::Error {
return c_size;
}
if action == TargetCBlockAction::Compressed {
unsafe {
ZSTD_rust_confirmRepcodesAndEntropyTables(state.prev_c_block, state.next_c_block);
}
return c_size;
}
}
unsafe { ZSTD_rust_noCompressBlock(dst, dst_capacity, src, src_size, last_block) }
}
unsafe fn compress_block_target_c_block_size_after_build_body_with(
state: &ZSTD_rust_targetCBlockSizeState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
bss: c_int,
last_block: c_uint,
compress_super_block: TargetCBlockSuperBlockFn,
) -> usize {
if bss != ZSTD_BSS_COMPRESS && bss != ZSTD_BSS_NO_COMPRESS {
return ERROR(ZstdErrorCode::Generic);
}
let c_size = unsafe {
compress_block_target_c_block_size_body_with(
state,
dst,
dst_capacity,
src,
src_size,
bss,
last_block,
compress_super_block,
)
};
if ERR_isError(c_size) {
return c_size;
}
if state.prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *state.prev_c_block };
if prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode } == FSE_REPEAT_VALID {
unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode = FSE_REPEAT_CHECK };
}
c_size
}
/// C ABI entry point for the target-sized block body. The public and outer
/// block APIs remain C-owned; this is only the body after `ZSTD_buildSeqStore`.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBlockTargetCBlockSize(
state: *const ZSTD_rust_targetCBlockSizeState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
bss: c_int,
last_block: c_uint,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_target_c_block_size_body_with(
&*state,
dst,
dst_capacity,
src,
src_size,
bss,
last_block,
ZSTD_rust_compressSuperBlock,
)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBlockTargetCBlockSizeAfterBuild(
state: *const ZSTD_rust_targetCBlockSizeState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
bss: c_int,
last_block: c_uint,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_target_c_block_size_after_build_body_with(
&*state,
dst,
dst_capacity,
src,
src_size,
bss,
last_block,
ZSTD_rust_compressSuperBlock,
)
}
}
/// Rust implementation of `ZSTD_compressSeqStore_singleBlock`.
///
/// The C caller still owns sequence-store construction, split-block control,
/// and the private compression context. This body owns the exact block
/// decision and the associated state transitions after the sequence store is
/// ready.
#[allow(clippy::too_many_arguments)]
unsafe fn compress_seq_store_single_block_body_with(
state: &ZSTD_rust_seqStoreSingleBlockState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
last_block: c_uint,
is_partition: c_uint,
seams: SingleBlockSeams,
) -> usize {
const RLE_MAX_LENGTH: usize = 25;
if state.seq_store.is_null()
|| state.d_rep.is_null()
|| state.c_rep.is_null()
|| state.prev_c_block.is_null()
|| state.next_c_block.is_null()
|| state.seq_collector.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *state.prev_c_block };
let next_c_block = unsafe { *state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
/* Preserve the decompression-side history for raw/RLE output and for the
* sequence collector, exactly as the original C body does. */
let mut d_rep_original = [0u32; ZSTD_REP_NUM];
unsafe {
ptr::copy_nonoverlapping(state.d_rep, d_rep_original.as_mut_ptr(), ZSTD_REP_NUM);
}
if is_partition != 0 {
let seq_store = unsafe { &*state.seq_store };
let nb_seq = unsafe { seq_store.sequences.offset_from(seq_store.sequencesStart) } as c_uint;
unsafe {
(seams.resolve_off_codes)(state.d_rep, state.c_rep, state.seq_store, nb_seq);
}
}
if dst_capacity < ZSTD_BLOCK_HEADER_SIZE {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let op = dst.cast::<u8>();
let ip = src.cast::<u8>();
let mut compressed_sequences_size = unsafe {
(seams.entropy_compress)(
state.seq_store,
ptr::addr_of!((*prev_c_block).entropy),
ptr::addr_of_mut!((*next_c_block).entropy),
state.strategy,
state.disable_literal_compression,
op.add(ZSTD_BLOCK_HEADER_SIZE).cast(),
dst_capacity - ZSTD_BLOCK_HEADER_SIZE,
src_size,
state.tmp_workspace,
state.tmp_wksp_size,
state.bmi2,
)
};
if ERR_isError(compressed_sequences_size) {
return compressed_sequences_size;
}
if state.is_first_block == 0
&& compressed_sequences_size < RLE_MAX_LENGTH
&& unsafe { (seams.is_rle)(ip, src_size) } != 0
{
/* Do not emit the first frame block as RLE; this preserves the legacy
* decoder compatibility rule in the original implementation. */
compressed_sequences_size = 1;
}
/* Sequence collection is deliberately before block serialization and
* returns without the outer repeat-mode cleanup, matching C. */
if unsafe { (*state.seq_collector).collectSequences } != 0 {
let result = unsafe {
(seams.copy_sequences)(
state.seq_collector,
state.seq_store,
d_rep_original.as_ptr(),
)
};
if ERR_isError(result) {
return result;
}
unsafe { (seams.confirm)(state.prev_c_block, state.next_c_block) };
return 0;
}
let c_size = if compressed_sequences_size == 0 {
let c_size = unsafe { (seams.raw)(dst, dst_capacity, src, src_size, last_block) };
if ERR_isError(c_size) {
return c_size;
}
unsafe {
ptr::copy_nonoverlapping(d_rep_original.as_ptr(), state.d_rep, ZSTD_REP_NUM);
}
c_size
} else if compressed_sequences_size == 1 {
let c_size = unsafe { (seams.rle)(dst, dst_capacity, *ip, src_size, last_block) };
if ERR_isError(c_size) {
return c_size;
}
unsafe {
ptr::copy_nonoverlapping(d_rep_original.as_ptr(), state.d_rep, ZSTD_REP_NUM);
}
c_size
} else {
unsafe { (seams.confirm)(state.prev_c_block, state.next_c_block) };
unsafe {
(seams.write_header)(dst, compressed_sequences_size, src_size, last_block);
}
ZSTD_BLOCK_HEADER_SIZE.wrapping_add(compressed_sequences_size)
};
let prev_c_block = unsafe { *state.prev_c_block };
if unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode } == 2 {
unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode = 1 };
}
c_size
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressSeqStoreSingleBlock(
state: *const ZSTD_rust_seqStoreSingleBlockState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
last_block: c_uint,
is_partition: c_uint,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_seq_store_single_block_body_with(
&*state,
dst,
dst_capacity,
src,
src_size,
last_block,
is_partition,
SingleBlockSeams::production(),
)
}
}
#[inline]
fn split_single_block_state(
split_state: &ZSTD_rust_splitBlockState,
seq_store: *const SeqStore_t,
d_rep: &mut [u32; ZSTD_REP_NUM],
c_rep: &mut [u32; ZSTD_REP_NUM],
) -> ZSTD_rust_seqStoreSingleBlockState {
ZSTD_rust_seqStoreSingleBlockState {
seq_store,
d_rep: d_rep.as_mut_ptr(),
c_rep: c_rep.as_mut_ptr(),
prev_c_block: split_state.prev_c_block,
next_c_block: split_state.next_c_block,
tmp_workspace: split_state.tmp_workspace,
tmp_wksp_size: split_state.tmp_wksp_size,
seq_collector: split_state.seq_collector,
strategy: split_state.strategy,
disable_literal_compression: split_state.disable_literal_compression,
bmi2: split_state.bmi2,
is_first_block: split_state.is_first_block,
}
}
/// Rust implementation of the post-split partition loop.
///
/// C retains the private `ZSTD_CCtx` and block-split storage; Rust receives
/// projected estimator inputs alongside the sequence-store views and owns the
/// partition accounting, repcode simulation, and block emission.
#[allow(clippy::too_many_arguments)]
unsafe fn compress_block_split_body_with(
state: &ZSTD_rust_splitBlockState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
block_size: usize,
last_block: c_uint,
num_splits: usize,
seams: SingleBlockSeams,
) -> usize {
if state.seq_store.is_null()
|| state.partitions.is_null()
|| state.next_seq_store.is_null()
|| state.curr_seq_store.is_null()
|| state.prev_c_block.is_null()
|| state.next_c_block.is_null()
|| state.seq_collector.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *state.prev_c_block };
let next_c_block = unsafe { *state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
/* cRep and dRep start from the preceding block and diverge only when a
* partition is emitted as raw or RLE. */
let mut d_rep = [0u32; ZSTD_REP_NUM];
unsafe {
ptr::copy_nonoverlapping(
(*prev_c_block).rep.as_ptr(),
d_rep.as_mut_ptr(),
ZSTD_REP_NUM,
);
}
let mut c_rep = d_rep;
unsafe {
ptr::write_bytes(
state.next_seq_store.cast::<u8>(),
0,
size_of::<SeqStore_t>(),
);
}
if num_splits == 0 {
let single_state = split_single_block_state(state, state.seq_store, &mut d_rep, &mut c_rep);
let c_size = unsafe {
compress_seq_store_single_block_body_with(
&single_state,
dst,
dst_capacity,
src,
block_size,
last_block,
0,
seams,
)
};
debug_assert!(state.block_size_max <= ZSTD_BLOCKSIZE_MAX);
debug_assert!(
c_size <= state.block_size_max.wrapping_add(ZSTD_BLOCK_HEADER_SIZE)
|| ERR_isError(c_size)
);
return c_size;
}
unsafe {
ZSTD_rust_deriveSeqStoreChunk(
state.curr_seq_store,
state.seq_store,
0,
*state.partitions as usize,
);
}
let mut c_size = 0usize;
let mut src_bytes_total = 0usize;
let mut ip = src.cast::<u8>();
let mut op = dst.cast::<u8>();
let mut remaining_capacity = dst_capacity;
for i in 0..=num_splits {
let last_partition = i == num_splits;
let mut last_block_entire_src = 0;
let partition_seq_store = state.curr_seq_store as *const SeqStore_t;
let partition_bytes = unsafe {
ZSTD_rust_countSeqStoreLiteralsBytes(partition_seq_store)
.wrapping_add(ZSTD_rust_countSeqStoreMatchBytes(partition_seq_store))
};
src_bytes_total = src_bytes_total.wrapping_add(partition_bytes);
let src_bytes = if last_partition {
last_block_entire_src = last_block;
partition_bytes.wrapping_add(block_size.wrapping_sub(src_bytes_total))
} else {
unsafe {
ZSTD_rust_deriveSeqStoreChunk(
state.next_seq_store,
state.seq_store,
*state.partitions.add(i) as usize,
*state.partitions.add(i + 1) as usize,
);
}
partition_bytes
};
let single_state =
split_single_block_state(state, partition_seq_store, &mut d_rep, &mut c_rep);
let c_size_chunk = unsafe {
compress_seq_store_single_block_body_with(
&single_state,
op.cast(),
remaining_capacity,
ip.cast(),
src_bytes,
last_block_entire_src,
1,
seams,
)
};
if ERR_isError(c_size_chunk) {
return c_size_chunk;
}
unsafe {
ip = ip.add(src_bytes);
op = op.add(c_size_chunk);
}
remaining_capacity = remaining_capacity.wrapping_sub(c_size_chunk);
c_size = c_size.wrapping_add(c_size_chunk);
unsafe {
ptr::copy_nonoverlapping(state.next_seq_store, state.curr_seq_store, 1);
}
debug_assert!(c_size_chunk <= state.block_size_max.wrapping_add(ZSTD_BLOCK_HEADER_SIZE));
}
let final_prev_c_block = unsafe { *state.prev_c_block };
if final_prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
ptr::copy_nonoverlapping(
d_rep.as_ptr(),
(*final_prev_c_block).rep.as_mut_ptr(),
ZSTD_REP_NUM,
);
}
c_size
}
unsafe fn compress_block_split_after_build_body_with(
state: &ZSTD_rust_splitBlockState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
block_size: usize,
last_block: c_uint,
num_splits: usize,
bss: c_int,
seams: SingleBlockSeams,
) -> usize {
if bss == ZSTD_BSS_NO_COMPRESS {
if state.seq_collector.is_null() || state.prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*state.seq_collector).collectSequences } != 0 {
return ERROR(ZstdErrorCode::SequenceProducerFailed);
}
let prev_c_block = unsafe { *state.prev_c_block };
if prev_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode } == FSE_REPEAT_VALID {
unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode = FSE_REPEAT_CHECK };
}
return unsafe {
ZSTD_rust_noCompressBlock(dst, dst_capacity, src, block_size, last_block)
};
}
if bss != ZSTD_BSS_COMPRESS {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_split_body_with(
state,
dst,
dst_capacity,
src,
block_size,
last_block,
num_splits,
seams,
)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBlockSplit(
state: *const ZSTD_rust_splitBlockState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
block_size: usize,
last_block: c_uint,
num_splits: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_split_body_with(
&*state,
dst,
dst_capacity,
src,
block_size,
last_block,
num_splits,
SingleBlockSeams::production(),
)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBlockSplitAfterBuild(
state: *const ZSTD_rust_splitBlockState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
block_size: usize,
last_block: c_uint,
num_splits: usize,
bss: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
compress_block_split_after_build_body_with(
&*state,
dst,
dst_capacity,
src,
block_size,
last_block,
num_splits,
bss,
SingleBlockSeams::production(),
)
}
}
unsafe fn store_converted_sequence(
seq_store: &mut SeqStore_t,
lit_length: u32,
off_base: u32,
match_length: u32,
) -> bool {
let sequence_index = unsafe { seq_store.sequences.offset_from(seq_store.sequencesStart) };
if sequence_index < 0 || sequence_index as usize >= seq_store.maxNbSeq {
return false;
}
let ml_base = match (match_length as usize).checked_sub(MINMATCH) {
Some(value) => value,
None => return false,
};
if lit_length as usize > u16::MAX as usize {
if seq_store.longLengthType != 0 {
return false;
}
seq_store.longLengthType = ZSTD_LLT_LITERAL_LENGTH;
seq_store.longLengthPos = sequence_index as u32;
}
if ml_base > u16::MAX as usize {
if seq_store.longLengthType != 0 {
return false;
}
seq_store.longLengthType = ZSTD_LLT_MATCH_LENGTH;
seq_store.longLengthPos = sequence_index as u32;
}
unsafe {
(*seq_store.sequences).litLength = lit_length as u16;
(*seq_store.sequences).offBase = off_base;
(*seq_store.sequences).mlBase = ml_base as u16;
seq_store.sequences = seq_store.sequences.add(1);
}
true
}
unsafe fn convert_block_sequences_body_with(
state: &ZSTD_rust_convertBlockSequencesState,
in_seqs: *const ZSTD_Sequence,
nb_sequences: usize,
repcode_resolution: c_int,
) -> usize {
if state.seq_store.is_null()
|| state.prev_c_block.is_null()
|| state.next_c_block.is_null()
|| in_seqs.is_null()
|| nb_sequences == 0
{
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
let seq_store = unsafe { &mut *state.seq_store };
if nb_sequences >= seq_store.maxNbSeq {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
let delimiter = unsafe { *in_seqs.add(nb_sequences - 1) };
if delimiter.matchLength != 0 || delimiter.offset != 0 {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
let prev_c_block = unsafe { *state.prev_c_block };
let next_c_block = unsafe { *state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
let mut updated_repcodes = [0u32; ZSTD_REP_NUM];
unsafe {
ptr::copy_nonoverlapping(
(*prev_c_block).rep.as_ptr(),
updated_repcodes.as_mut_ptr(),
ZSTD_REP_NUM,
);
}
if repcode_resolution == 0 {
let full_sequence_count = nb_sequences - 1;
let long_length = unsafe {
ZSTD_rust_convertSequencesNoRepcodes(
seq_store.sequencesStart,
in_seqs,
full_sequence_count,
)
};
if long_length != 0 {
if seq_store.longLengthType != 0 {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
if long_length <= full_sequence_count {
seq_store.longLengthType = ZSTD_LLT_MATCH_LENGTH;
seq_store.longLengthPos = (long_length - 1) as u32;
} else if long_length <= full_sequence_count.saturating_mul(2) {
seq_store.longLengthType = ZSTD_LLT_LITERAL_LENGTH;
seq_store.longLengthPos = (long_length - nb_sequences) as u32;
} else {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
}
seq_store.sequences = unsafe { seq_store.sequencesStart.add(full_sequence_count) };
if nb_sequences > 1 {
let rep = &mut updated_repcodes;
if nb_sequences >= 4 {
let last_seq_idx = nb_sequences - 2;
rep[2] = unsafe { (*in_seqs.add(last_seq_idx - 2)).offset };
rep[1] = unsafe { (*in_seqs.add(last_seq_idx - 1)).offset };
rep[0] = unsafe { (*in_seqs.add(last_seq_idx)).offset };
} else if nb_sequences == 3 {
rep[2] = rep[0];
rep[1] = unsafe { (*in_seqs).offset };
rep[0] = unsafe { (*in_seqs.add(1)).offset };
} else {
rep[2] = rep[1];
rep[1] = rep[0];
rep[0] = unsafe { (*in_seqs).offset };
}
}
} else {
for index in 0..(nb_sequences - 1) {
let sequence = unsafe { *in_seqs.add(index) };
let ll0 = sequence.litLength == 0;
let off_base = unsafe {
ZSTD_rust_finalizeOffBase(
sequence.offset,
updated_repcodes.as_ptr(),
u32::from(ll0),
)
};
if !unsafe {
store_converted_sequence(
seq_store,
sequence.litLength,
off_base,
sequence.matchLength,
)
} {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
update_rep(&mut updated_repcodes, off_base, ll0);
}
}
unsafe {
ptr::copy_nonoverlapping(
updated_repcodes.as_ptr(),
(*next_c_block).rep.as_mut_ptr(),
ZSTD_REP_NUM,
);
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_convertBlockSequences(
state: *const ZSTD_rust_convertBlockSequencesState,
in_seqs: *const ZSTD_Sequence,
nb_sequences: usize,
repcode_resolution: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
unsafe { convert_block_sequences_body_with(&*state, in_seqs, nb_sequences, repcode_resolution) }
}
/// Select the strategy used by the simple compression entry points.
///
/// This is the Rust equivalent of the strategy portion of
/// `ZSTD_getParams_internal(..., ZSTD_cpm_noAttachDict)`: use the actual
/// source size for table selection, then apply the ordinary automatic
/// parameter adjustment before dispatching to the Rust or C frame path.
#[no_mangle]
pub extern "C" fn ZSTD_rust_compressCCtxStrategy(
src_size: usize,
compression_level: c_int,
) -> c_int {
let cparams = ZSTD_rust_params_selectCParams(
compression_level,
src_size as u64,
0,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
);
ZSTD_rust_params_adjustCParams(
cparams,
src_size as u64,
0,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
ZSTD_RUST_PS_AUTO,
)
.strategy
}
/// Classify the target-sized block policy without crossing the C context ABI.
///
/// C calls this once before the superblock attempt to classify the RLE
/// precondition, then again with the superblock result and zero RLE flags.
#[no_mangle]
pub extern "C" fn ZSTD_rust_targetCBlockSizeAction(
bss: c_int,
is_first_block: c_int,
maybe_rle: c_int,
is_rle: c_int,
c_size: usize,
src_size: usize,
strategy: c_int,
) -> c_int {
target_c_block_size_action(
bss,
is_first_block,
maybe_rle,
is_rle,
c_size,
src_size,
strategy,
) as c_int
}
#[repr(C)]
pub struct ZSTD_inBuffer {
pub src: *const c_void,
pub size: usize,
pub pos: usize,
}
#[repr(C)]
pub struct ZSTD_outBuffer {
pub dst: *mut c_void,
pub size: usize,
pub pos: usize,
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_inBufferForEndFlush(
in_buffer_mode: c_int,
expected_src: *const c_void,
expected_size: usize,
expected_pos: usize,
) -> ZSTD_inBuffer {
if in_buffer_mode == ZSTD_BM_STABLE {
ZSTD_inBuffer {
src: expected_src,
size: expected_size,
pos: expected_pos,
}
} else {
ZSTD_inBuffer {
src: ptr::null(),
size: 0,
pos: 0,
}
}
}
#[inline]
fn end_stream_remaining(
remaining_to_flush: usize,
frame_ended: c_int,
checksum_flag: c_int,
) -> usize {
if frame_ended != 0 {
return remaining_to_flush;
}
remaining_to_flush
.wrapping_add(ZSTD_BLOCK_HEADER_SIZE)
.wrapping_add((checksum_flag as usize).wrapping_mul(4))
}
/// Estimate single-threaded end-stream output without crossing C context state.
#[no_mangle]
pub extern "C" fn ZSTD_rust_endStreamRemaining(
remaining_to_flush: usize,
frame_ended: c_int,
checksum_flag: c_int,
) -> usize {
end_stream_remaining(remaining_to_flush, frame_ended, checksum_flag)
}
#[inline]
fn check_buffer_stability(
in_buffer_mode: c_int,
out_buffer_mode: c_int,
expected_in_src: *const c_void,
expected_in_pos: usize,
input_src: *const c_void,
input_pos: usize,
expected_out_buffer_size: usize,
output_size: usize,
output_pos: usize,
) -> usize {
if in_buffer_mode == ZSTD_BM_STABLE
&& (expected_in_src != input_src || expected_in_pos != input_pos)
{
return ERROR(ZstdErrorCode::StabilityConditionNotRespected);
}
if out_buffer_mode == ZSTD_BM_STABLE
&& expected_out_buffer_size != output_size.wrapping_sub(output_pos)
{
return ERROR(ZstdErrorCode::StabilityConditionNotRespected);
}
0
}
/// Validate the stable input/output buffer expectations without crossing the
/// private `ZSTD_CCtx` layout into Rust. Raw pointers are compared only for
/// identity and are never dereferenced.
#[no_mangle]
pub extern "C" fn ZSTD_rust_checkBufferStability(
in_buffer_mode: c_int,
out_buffer_mode: c_int,
expected_in_src: *const c_void,
expected_in_pos: usize,
input_src: *const c_void,
input_pos: usize,
expected_out_buffer_size: usize,
output_size: usize,
output_pos: usize,
) -> usize {
check_buffer_stability(
in_buffer_mode,
out_buffer_mode,
expected_in_src,
expected_in_pos,
input_src,
input_pos,
expected_out_buffer_size,
output_size,
output_pos,
)
}
#[inline]
unsafe fn set_buffer_expectations(
in_buffer_mode: c_int,
out_buffer_mode: c_int,
expected_in_buffer: *mut ZSTD_inBuffer,
expected_out_buffer_size: *mut usize,
output: *const ZSTD_outBuffer,
input: *const ZSTD_inBuffer,
) {
if in_buffer_mode == ZSTD_BM_STABLE {
unsafe {
*expected_in_buffer = ptr::read(input);
}
}
if out_buffer_mode == ZSTD_BM_STABLE {
let output = unsafe { &*output };
unsafe {
*expected_out_buffer_size = output.size.wrapping_sub(output.pos);
}
}
}
/// Update the stable input/output expectations after a compression call.
///
/// C retains the private `ZSTD_CCtx` fields and passes only their projections;
/// Rust owns the buffer-mode policy and C-size arithmetic.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_setBufferExpectations(
in_buffer_mode: c_int,
out_buffer_mode: c_int,
expected_in_buffer: *mut ZSTD_inBuffer,
expected_out_buffer_size: *mut usize,
output: *const ZSTD_outBuffer,
input: *const ZSTD_inBuffer,
) {
unsafe {
set_buffer_expectations(
in_buffer_mode,
out_buffer_mode,
expected_in_buffer,
expected_out_buffer_size,
output,
input,
);
}
}
#[inline]
fn select_sequence_copier(mode: c_int) -> c_int {
debug_assert!(
(ZSTD_SF_NO_BLOCK_DELIMITERS..=ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS).contains(&mode)
);
if mode == ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS {
ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS
} else {
debug_assert_eq!(mode, ZSTD_SF_NO_BLOCK_DELIMITERS);
ZSTD_SF_NO_BLOCK_DELIMITERS
}
}
/// Select the C-side sequence transfer policy without crossing private
/// function pointers through the Rust ABI. Invalid values retain the C
/// release fallback to the no-delimiter policy after debug validation.
#[no_mangle]
pub extern "C" fn ZSTD_rust_selectSequenceCopier(mode: c_int) -> c_int {
select_sequence_copier(mode)
}
/* HUF_WORKSPACE_SIZE + (MaxSeq + 2) * sizeof(unsigned), rounded up. The
* superblock leaf also accepts the larger pre-split workspace, so a fixed
* 16 KiB buffer is sufficient for this first non-splitting path on both
* supported pointer widths. */
const TMP_WORKSPACE_SIZE: usize = 16 << 10;
#[inline]
fn update_frame_progression(
consumed_src_size: &mut u64,
produced_c_size: &mut u64,
pledged_src_size_plus_one: u64,
src_size: usize,
c_size: usize,
frame_header_size: usize,
) -> bool {
*consumed_src_size = consumed_src_size.wrapping_add(src_size as u64);
*produced_c_size = produced_c_size.wrapping_add(c_size.wrapping_add(frame_header_size) as u64);
pledged_src_size_plus_one != 0 && consumed_src_size.wrapping_add(1) > pledged_src_size_plus_one
}
/// Update C-owned frame counters after successful compression.
///
/// A zero pledge means that the source size is unknown. C keeps the
/// diagnostic that accompanies a nonzero overrun result.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_updateFrameProgression(
consumed_src_size: *mut u64,
produced_c_size: *mut u64,
pledged_src_size_plus_one: u64,
src_size: usize,
c_size: usize,
frame_header_size: usize,
) -> c_int {
let consumed_src_size = unsafe { &mut *consumed_src_size };
let produced_c_size = unsafe { &mut *produced_c_size };
update_frame_progression(
consumed_src_size,
produced_c_size,
pledged_src_size_plus_one,
src_size,
c_size,
frame_header_size,
) as c_int
}
/// ABI-compatible representation of `ZSTD_frameProgression` from `zstd.h`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_frameProgression {
pub ingested: u64,
pub consumed: u64,
pub produced: u64,
pub flushed: u64,
pub currentJobID: c_uint,
pub nbActiveWorkers: c_uint,
}
#[inline]
fn frame_progression(
consumed_src_size: u64,
buffered: usize,
produced_c_size: u64,
) -> ZSTD_frameProgression {
ZSTD_frameProgression {
// C's usual arithmetic conversions promote size_t to U64 before wrapping.
ingested: consumed_src_size.wrapping_add(buffered as u64),
consumed: consumed_src_size,
produced: produced_c_size,
flushed: produced_c_size,
currentJobID: 0,
nbActiveWorkers: 0,
}
}
/// Construct single-threaded frame progression from C-owned scalar state.
#[no_mangle]
pub extern "C" fn ZSTD_rust_frameProgression(
consumed_src_size: u64,
buffered: usize,
produced_c_size: u64,
) -> ZSTD_frameProgression {
frame_progression(consumed_src_size, buffered, produced_c_size)
}
type ToFlushNowFn = unsafe extern "C" fn(*mut c_void) -> usize;
/// Explicit projection for the `ZSTD_toFlushNow` multithread policy.
///
/// Rust owns the worker-count branch; C retains the private CCtx and MT
/// context behind the callback.
#[repr(C)]
pub struct ZSTD_rust_toFlushNowState {
callback_context: *mut c_void,
nb_workers: c_int,
to_flush_now: ToFlushNowFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_toFlushNowState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_toFlushNowState, nb_workers) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_toFlushNowState, to_flush_now) == 2 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_toFlushNowState>() == 3 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_toFlushNow(state: *const ZSTD_rust_toFlushNowState) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.nb_workers <= 0 {
return 0;
}
unsafe { (state.to_flush_now)(state.callback_context) }
}
#[inline]
fn window_correct_overflow(curr: u32, cycle_log: u32, max_dist: u32) -> u32 {
let cycle_size = 1u32.wrapping_shl(cycle_log);
let cycle_mask = cycle_size.wrapping_sub(1);
let current_cycle = curr & cycle_mask;
let current_cycle_correction = if current_cycle < ZSTD_WINDOW_START_INDEX {
cycle_size.max(ZSTD_WINDOW_START_INDEX)
} else {
0
};
let new_current = current_cycle
.wrapping_add(current_cycle_correction)
.wrapping_add(max_dist.max(cycle_size));
curr.wrapping_sub(new_current)
}
/// Return the scalar correction for C's window-overflow state transition.
///
/// C retains pointer arithmetic, invariant checks, and all `ZSTD_window_t`
/// mutation. This leaf owns only the U32 cycle arithmetic so its wrapping
/// behavior is explicit and independent of the host pointer width.
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowCorrectOverflow(curr: u32, cycle_log: u32, max_dist: u32) -> u32 {
window_correct_overflow(curr, cycle_log, max_dist)
}
#[inline]
fn window_can_overflow_correct(
curr: u32,
cycle_log: u32,
max_dist: u32,
loaded_dict_end: u32,
nb_overflow_corrections: u32,
) -> bool {
let cycle_size = 1u32.wrapping_shl(cycle_log);
let min_index_to_overflow_correct = cycle_size
.wrapping_add(max_dist.max(cycle_size))
.wrapping_add(ZSTD_WINDOW_START_INDEX);
let adjustment = nb_overflow_corrections.wrapping_add(1);
let adjusted_index = min_index_to_overflow_correct
.wrapping_mul(adjustment)
.max(min_index_to_overflow_correct);
let index_large_enough = curr > adjusted_index;
let dictionary_invalidated = curr > max_dist.wrapping_add(loaded_dict_end);
index_large_enough && dictionary_invalidated
}
/// Return whether the C-owned window state should enter overflow correction.
///
/// C retains both pointer subtractions and the `ZSTD_window_t` state. The
/// early-correction result is supplied as a scalar because it is based on the
/// block-start index, while `curr` is the block-end index used by the normal
/// current-limit fallback. The current limit and fuzzing policy are supplied
/// by C so their 32/64-bit and build-mode choices remain authoritative.
#[inline]
fn window_need_overflow_correction(
curr: u32,
can_overflow_correct: bool,
current_max: u32,
overflow_correct_frequently: bool,
) -> bool {
(overflow_correct_frequently && can_overflow_correct) || curr > current_max
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowCanOverflowCorrect(
curr: u32,
cycle_log: u32,
max_dist: u32,
loaded_dict_end: u32,
nb_overflow_corrections: u32,
) -> u32 {
window_can_overflow_correct(
curr,
cycle_log,
max_dist,
loaded_dict_end,
nb_overflow_corrections,
) as u32
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowNeedOverflowCorrection(
curr: u32,
can_overflow_correct: u32,
current_max: u32,
overflow_correct_frequently: c_int,
) -> u32 {
window_need_overflow_correction(
curr,
can_overflow_correct != 0,
current_max,
overflow_correct_frequently != 0,
) as u32
}
type OverflowNeedCorrectionFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void) -> c_int;
type OverflowCorrectFn = unsafe extern "C" fn(*mut c_void, *const c_void) -> c_uint;
type OverflowCallbackFn = unsafe extern "C" fn(*mut c_void);
type OverflowReduceIndexFn = unsafe extern "C" fn(*mut c_void, c_uint);
/// Projection for the overflow-correction ordering around C-owned match state.
///
/// Rust owns the correction branch and callback order. C retains the window,
/// workspace, match tables, and dictionary pointers behind these callbacks.
#[repr(C)]
pub struct ZSTD_rust_overflowCorrectState {
callback_context: *mut c_void,
next_to_update: *mut c_uint,
need_correction: Option<OverflowNeedCorrectionFn>,
correct_overflow: Option<OverflowCorrectFn>,
mark_tables_dirty: Option<OverflowCallbackFn>,
reduce_index: Option<OverflowReduceIndexFn>,
mark_tables_clean: Option<OverflowCallbackFn>,
loaded_dict_end: *mut c_uint,
dict_match_state: *mut *const c_void,
}
const _: () = {
assert!(size_of::<OverflowNeedCorrectionFn>() == size_of::<usize>());
assert!(size_of::<OverflowCorrectFn>() == size_of::<usize>());
assert!(size_of::<OverflowCallbackFn>() == size_of::<usize>());
assert!(size_of::<OverflowReduceIndexFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_overflowCorrectState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_overflowCorrectState, next_to_update) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_overflowCorrectState, need_correction) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_overflowCorrectState, correct_overflow) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_overflowCorrectState, mark_tables_dirty) == 4 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_overflowCorrectState, reduce_index) == 5 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_overflowCorrectState, mark_tables_clean) == 6 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_overflowCorrectState, loaded_dict_end) == 7 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_overflowCorrectState, dict_match_state) == size_of::<[usize; 8]>()
);
assert!(size_of::<ZSTD_rust_overflowCorrectState>() == size_of::<[usize; 9]>());
};
/// Apply one overflow correction while keeping all private codec state in C.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_overflowCorrectIfNeeded(
state: *const ZSTD_rust_overflowCorrectState,
src: *const c_void,
src_end: *const c_void,
) {
if state.is_null() {
return;
}
let state = unsafe { &*state };
if state.callback_context.is_null()
|| state.next_to_update.is_null()
|| state.loaded_dict_end.is_null()
|| state.dict_match_state.is_null()
{
return;
}
let (
Some(need_correction),
Some(correct_overflow),
Some(mark_tables_dirty),
Some(reduce_index),
Some(mark_tables_clean),
) = (
state.need_correction,
state.correct_overflow,
state.mark_tables_dirty,
state.reduce_index,
state.mark_tables_clean,
)
else {
return;
};
unsafe {
if need_correction(state.callback_context, src, src_end) == 0 {
return;
}
let correction = correct_overflow(state.callback_context, src);
mark_tables_dirty(state.callback_context);
reduce_index(state.callback_context, correction);
mark_tables_clean(state.callback_context);
*state.next_to_update = (*state.next_to_update).saturating_sub(correction);
*state.loaded_dict_end = 0;
*state.dict_match_state = ptr::null();
}
}
#[inline]
fn limit_next_to_update(curr: u32, next_to_update: u32) -> u32 {
let const_gap = 384u32;
let const_step = 192u32;
if curr > next_to_update.wrapping_add(const_gap) {
curr.wrapping_sub(const_step.min(curr.wrapping_sub(next_to_update).wrapping_sub(const_gap)))
} else {
next_to_update
}
}
/// Limit a stale matchfinder update point after a very long match.
///
/// C computes the source index against its private window base; Rust owns the
/// wrapping U32 threshold and bounded catch-up arithmetic.
#[no_mangle]
pub extern "C" fn ZSTD_rust_limitNextToUpdate(curr: u32, next_to_update: u32) -> u32 {
limit_next_to_update(curr, next_to_update)
}
#[inline]
fn next_input_size_hint(
in_buffer_mode: c_int,
block_size_max: usize,
stable_in_not_consumed: usize,
in_buff_target: usize,
in_buff_pos: usize,
) -> usize {
if in_buffer_mode == ZSTD_BM_STABLE {
return block_size_max.wrapping_sub(stable_in_not_consumed);
}
let hint_in_size = in_buff_target.wrapping_sub(in_buff_pos);
if hint_in_size == 0 {
block_size_max
} else {
hint_in_size
}
}
/// Return the next input size required by the C streaming state machine.
#[no_mangle]
pub extern "C" fn ZSTD_rust_nextInputSizeHint(
in_buffer_mode: c_int,
block_size_max: usize,
stable_in_not_consumed: usize,
in_buff_target: usize,
in_buff_pos: usize,
) -> usize {
next_input_size_hint(
in_buffer_mode,
block_size_max,
stable_in_not_consumed,
in_buff_target,
in_buff_pos,
)
}
type NextInputSizeHintMTorSTFn = unsafe extern "C" fn(*mut c_void) -> usize;
/// Projection for the MT-versus-single-thread input-hint dispatch.
///
/// Rust owns only the worker-count branch. The single-thread hint inputs are
/// scalar, while the MT context remains opaque behind its C callback.
#[repr(C)]
pub struct ZSTD_rust_nextInputSizeHintMTorSTState {
block_size_max: usize,
stable_in_not_consumed: usize,
in_buff_target: usize,
in_buff_pos: usize,
nb_workers: c_int,
in_buffer_mode: c_int,
mt_context: *mut c_void,
mt_next_input_size_hint: Option<NextInputSizeHintMTorSTFn>,
}
const _: () = {
assert!(size_of::<NextInputSizeHintMTorSTFn>() == size_of::<usize>());
assert!(size_of::<Option<NextInputSizeHintMTorSTFn>>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_nextInputSizeHintMTorSTState, block_size_max) == 0);
assert!(
offset_of!(
ZSTD_rust_nextInputSizeHintMTorSTState,
stable_in_not_consumed
) == size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_nextInputSizeHintMTorSTState, in_buff_target)
== 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_nextInputSizeHintMTorSTState, in_buff_pos) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_nextInputSizeHintMTorSTState, nb_workers) == 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_nextInputSizeHintMTorSTState, in_buffer_mode)
== 4 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_nextInputSizeHintMTorSTState, mt_context)
== 4 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(
ZSTD_rust_nextInputSizeHintMTorSTState,
mt_next_input_size_hint
) == 4 * size_of::<usize>() + 2 * size_of::<c_int>() + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_nextInputSizeHintMTorSTState>()
== 4 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
};
/// Select the MT or single-thread input-size hint without exposing the MT
/// context representation to Rust.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_nextInputSizeHintMTorST(
state: *const ZSTD_rust_nextInputSizeHintMTorSTState,
) -> usize {
let Some(state) = (unsafe { state.as_ref() }) else {
return 0;
};
if state.nb_workers >= 1 {
let Some(mt_next_input_size_hint) = state.mt_next_input_size_hint else {
return 0;
};
return unsafe { mt_next_input_size_hint(state.mt_context) };
}
next_input_size_hint(
state.in_buffer_mode,
state.block_size_max,
state.stable_in_not_consumed,
state.in_buff_target,
state.in_buff_pos,
)
}
#[inline]
fn mt_next_input_size_hint(target_section_size: usize, in_buff_filled: usize) -> usize {
let hint_in_size = target_section_size.wrapping_sub(in_buff_filled);
if hint_in_size == 0 {
target_section_size
} else {
hint_in_size
}
}
/// Return the next input size required by the C multithreaded streaming state.
#[no_mangle]
pub extern "C" fn ZSTDMT_rust_nextInputSizeHint(
target_section_size: usize,
in_buff_filled: usize,
) -> usize {
mt_next_input_size_hint(target_section_size, in_buff_filled)
}
#[inline]
fn mt_sizeof_cctx(
mtctx_size: usize,
factory_size: usize,
buffer_pool_size: usize,
jobs_size: usize,
cctx_pool_size: usize,
seq_pool_size: usize,
cdict_size: usize,
round_buff_size: usize,
) -> usize {
mtctx_size
.wrapping_add(factory_size)
.wrapping_add(buffer_pool_size)
.wrapping_add(jobs_size)
.wrapping_add(cctx_pool_size)
.wrapping_add(seq_pool_size)
.wrapping_add(cdict_size)
.wrapping_add(round_buff_size)
}
/// Aggregate C-owned multithreaded context size components with C `size_t`
/// wrapping semantics.
#[no_mangle]
pub extern "C" fn ZSTDMT_rust_sizeofCCtx(
mtctx_size: usize,
factory_size: usize,
buffer_pool_size: usize,
jobs_size: usize,
cctx_pool_size: usize,
seq_pool_size: usize,
cdict_size: usize,
round_buff_size: usize,
) -> usize {
mt_sizeof_cctx(
mtctx_size,
factory_size,
buffer_pool_size,
jobs_size,
cctx_pool_size,
seq_pool_size,
cdict_size,
round_buff_size,
)
}
#[inline]
fn bitmix(mut val: u64, len: u64) -> u64 {
val ^= val.rotate_right(49) ^ val.rotate_right(24);
val = val.wrapping_mul(0x9FB21C651E98DF25);
val ^= (val >> 35).wrapping_add(len);
val = val.wrapping_mul(0x9FB21C651E98DF25);
val ^ (val >> 28)
}
#[inline]
fn advance_hash_salt(hash_salt: u64, hash_salt_entropy: u64) -> u64 {
bitmix(hash_salt, 8) ^ bitmix(hash_salt_entropy, 4)
}
/// Advance the row-matchfinder salt without exposing C's private match state.
#[no_mangle]
pub extern "C" fn ZSTD_rust_advanceHashSalt(hash_salt: u64, hash_salt_entropy: u64) -> u64 {
advance_hash_salt(hash_salt, hash_salt_entropy)
}
/// Advance a C-owned row-matchfinder salt in place while C retains the
/// private match-state layout.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_advanceHashSaltInPlace(
hash_salt: *mut u64,
hash_salt_entropy: u64,
) {
debug_assert!(!hash_salt.is_null());
unsafe {
*hash_salt = advance_hash_salt(*hash_salt, hash_salt_entropy);
}
}
#[inline]
fn index_too_close_to_max(next_src_base_offset: usize) -> bool {
next_src_base_offset > ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN
}
/// Return whether a scalar C window offset is within the overflow margin.
#[no_mangle]
pub extern "C" fn ZSTD_indexTooCloseToMax(next_src_base_offset: usize) -> c_int {
index_too_close_to_max(next_src_base_offset) as c_int
}
#[inline]
fn dict_too_big(loaded_dict_size: usize) -> bool {
loaded_dict_size > ZSTD_CHUNKSIZE_MAX
}
/// Return whether a dictionary exceeds the maximum loadable chunk size.
#[no_mangle]
pub extern "C" fn ZSTD_dictTooBig(loaded_dict_size: usize) -> c_int {
dict_too_big(loaded_dict_size) as c_int
}
type ResetMatchStateCallback = unsafe extern "C" fn(*mut c_void);
type ResetMatchStateSetPointer = unsafe extern "C" fn(*mut c_void, c_int, *mut c_void);
type ResetMatchStateReserve = unsafe extern "C" fn(*mut c_void, c_int, usize) -> *mut c_void;
type ResetMatchStateReserveFailed = unsafe extern "C" fn(*mut c_void) -> c_int;
type ResetMatchStateZero = unsafe extern "C" fn(*mut c_void, *mut c_void, usize);
const RESET_MATCH_RESERVE_TABLE: c_int = 0;
const RESET_MATCH_RESERVE_ALIGNED_INIT_ONCE: c_int = 1;
const RESET_MATCH_RESERVE_ALIGNED64: c_int = 2;
const RESET_MATCH_POINTER_HASH_TABLE: c_int = 0;
const RESET_MATCH_POINTER_CHAIN_TABLE: c_int = 1;
const RESET_MATCH_POINTER_HASH_TABLE3: c_int = 2;
const RESET_MATCH_POINTER_TAG_TABLE: c_int = 3;
const RESET_MATCH_POINTER_LIT_FREQ: c_int = 4;
const RESET_MATCH_POINTER_LIT_LENGTH_FREQ: c_int = 5;
const RESET_MATCH_POINTER_MATCH_LENGTH_FREQ: c_int = 6;
const RESET_MATCH_POINTER_OFF_CODE_FREQ: c_int = 7;
const RESET_MATCH_POINTER_MATCH_TABLE: c_int = 8;
const RESET_MATCH_POINTER_PRICE_TABLE: c_int = 9;
/// Private C layout pointers and callback seams for match-state reset.
///
/// Rust owns the reset policy and allocation order. C retains the workspace,
/// window, and private match-state pointer representations behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_resetMatchStateState {
callback_context: *mut c_void,
c_params: ZSTD_compressionParameters,
dedicated_dict_search: c_int,
use_row_match_finder: c_int,
comp_reset_policy: c_int,
index_reset_policy: c_int,
reset_target: c_int,
hash_log3_max: c_uint,
lit_freq_size: usize,
lit_length_freq_size: usize,
match_length_freq_size: usize,
off_code_freq_size: usize,
match_size: usize,
optimal_size: usize,
hash_log3: *mut c_uint,
row_hash_log: *mut c_uint,
hash_salt: *mut u64,
lazy_skipping: *mut c_int,
c_params_out: *mut ZSTD_compressionParameters,
set_pointer: Option<ResetMatchStateSetPointer>,
window_init: Option<ResetMatchStateCallback>,
mark_tables_dirty: Option<ResetMatchStateCallback>,
invalidate_match_state: Option<ResetMatchStateCallback>,
clear_tables: Option<ResetMatchStateCallback>,
clean_tables: Option<ResetMatchStateCallback>,
advance_hash_salt: Option<ResetMatchStateCallback>,
reserve: Option<ResetMatchStateReserve>,
reserve_failed: Option<ResetMatchStateReserveFailed>,
zero: Option<ResetMatchStateZero>,
}
const _: () = {
assert!(size_of::<ResetMatchStateCallback>() == size_of::<usize>());
assert!(size_of::<ResetMatchStateSetPointer>() == size_of::<usize>());
assert!(size_of::<ResetMatchStateReserve>() == size_of::<usize>());
assert!(size_of::<ResetMatchStateReserveFailed>() == size_of::<usize>());
assert!(size_of::<ResetMatchStateZero>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetMatchStateState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_resetMatchStateState, c_params) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetMatchStateState, lit_freq_size)
> offset_of!(ZSTD_rust_resetMatchStateState, hash_log3_max)
);
assert!(
offset_of!(ZSTD_rust_resetMatchStateState, hash_log3)
== offset_of!(ZSTD_rust_resetMatchStateState, optimal_size) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetMatchStateState, set_pointer)
== offset_of!(ZSTD_rust_resetMatchStateState, c_params_out) + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_resetMatchStateState>()
== offset_of!(ZSTD_rust_resetMatchStateState, zero) + size_of::<usize>()
);
};
#[inline]
fn reset_match_state_table_size(log: u32) -> Option<usize> {
1usize.checked_shl(log)
}
/// Reset and allocate one private match-state projection.
///
/// The callback context deliberately remains opaque here. This keeps C's
/// workspace phases and match-state layout out of the Rust ABI while making
/// the policy/order independently testable.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetMatchState(
state: *const ZSTD_rust_resetMatchStateState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(set_pointer) = state.set_pointer else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(window_init) = state.window_init else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(mark_tables_dirty) = state.mark_tables_dirty else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(invalidate_match_state) = state.invalidate_match_state else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(clear_tables) = state.clear_tables else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(clean_tables) = state.clean_tables else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(advance_hash_salt) = state.advance_hash_salt else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reserve) = state.reserve else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reserve_failed) = state.reserve_failed else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(zero) = state.zero else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.hash_log3.is_null()
|| state.row_hash_log.is_null()
|| state.hash_salt.is_null()
|| state.lazy_skipping.is_null()
|| state.c_params_out.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
if state.use_row_match_finder == ZSTD_RUST_PS_AUTO
|| !matches!(state.comp_reset_policy, 0..=1)
|| !matches!(state.index_reset_policy, 0..=1)
|| !matches!(state.reset_target, 0..=1)
{
return ERROR(ZstdErrorCode::Generic);
}
let chain_size = if ZSTD_rust_params_allocateChainTable(
state.c_params.strategy,
state.use_row_match_finder,
c_int::from(state.dedicated_dict_search != 0 && state.reset_target == 0),
) != 0
{
let Some(size) = reset_match_state_table_size(state.c_params.chainLog) else {
return ERROR(ZstdErrorCode::Generic);
};
size
} else {
0
};
let Some(hash_size) = reset_match_state_table_size(state.c_params.hashLog) else {
return ERROR(ZstdErrorCode::Generic);
};
let hash_log3 = if state.reset_target == 1 && state.c_params.minMatch == 3 {
state.hash_log3_max.min(state.c_params.windowLog)
} else {
0
};
let hash3_size = if hash_log3 == 0 {
0
} else {
let Some(size) = reset_match_state_table_size(hash_log3) else {
return ERROR(ZstdErrorCode::Generic);
};
size
};
if state.index_reset_policy == 1 {
unsafe {
window_init(state.callback_context);
mark_tables_dirty(state.callback_context);
}
}
unsafe {
*state.hash_log3 = hash_log3;
*state.lazy_skipping = 0;
invalidate_match_state(state.callback_context);
debug_assert_eq!(reserve_failed(state.callback_context), 0);
clear_tables(state.callback_context);
}
unsafe {
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_HASH_TABLE,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_TABLE,
hash_size.wrapping_mul(size_of::<c_uint>()),
),
);
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_CHAIN_TABLE,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_TABLE,
chain_size.wrapping_mul(size_of::<c_uint>()),
),
);
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_HASH_TABLE3,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_TABLE,
hash3_size.wrapping_mul(size_of::<c_uint>()),
),
);
}
if unsafe { reserve_failed(state.callback_context) } != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
if state.comp_reset_policy != 1 {
unsafe { clean_tables(state.callback_context) };
}
if ZSTD_rust_params_rowMatchFinderUsed(state.c_params.strategy, state.use_row_match_finder) != 0
{
if state.reset_target == 1 {
let tag_table = unsafe {
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED_INIT_ONCE,
hash_size,
)
};
unsafe {
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_TAG_TABLE,
tag_table,
);
advance_hash_salt(state.callback_context);
}
} else {
let tag_table = unsafe {
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED64,
hash_size,
)
};
unsafe {
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_TAG_TABLE,
tag_table,
);
}
if unsafe { reserve_failed(state.callback_context) } != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe {
zero(state.callback_context, tag_table, hash_size);
*state.hash_salt = 0;
}
}
if unsafe { reserve_failed(state.callback_context) } != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let row_log = state.c_params.searchLog.clamp(4, 6);
debug_assert!(state.c_params.hashLog >= row_log);
unsafe {
*state.row_hash_log = state.c_params.hashLog.wrapping_sub(row_log);
}
}
if state.reset_target == 1 && state.c_params.strategy >= 7 {
unsafe {
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_LIT_FREQ,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED64,
state.lit_freq_size,
),
);
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_LIT_LENGTH_FREQ,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED64,
state.lit_length_freq_size,
),
);
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_MATCH_LENGTH_FREQ,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED64,
state.match_length_freq_size,
),
);
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_OFF_CODE_FREQ,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED64,
state.off_code_freq_size,
),
);
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_MATCH_TABLE,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED64,
state.match_size,
),
);
set_pointer(
state.callback_context,
RESET_MATCH_POINTER_PRICE_TABLE,
reserve(
state.callback_context,
RESET_MATCH_RESERVE_ALIGNED64,
state.optimal_size,
),
);
}
}
unsafe {
*state.c_params_out = state.c_params;
}
if unsafe { reserve_failed(state.callback_context) } != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
0
}
#[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 sizeof_cdict(object_size: usize, workspace_size: usize) -> usize {
object_size.wrapping_add(workspace_size)
}
/// Aggregate C-owned dictionary size components with C `size_t` wrapping
/// semantics.
#[no_mangle]
pub extern "C" fn ZSTD_rust_sizeofCDict(objectSize: usize, workspaceSize: usize) -> usize {
sizeof_cdict(objectSize, workspaceSize)
}
#[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 estimate_workspace_size(
cctx_space: usize,
tmp_work_space: usize,
block_state_space: usize,
ldm_space: usize,
ldm_seq_space: usize,
match_state_size: usize,
token_space: usize,
buffer_space: usize,
external_seq_space: usize,
) -> usize {
cctx_space
.wrapping_add(tmp_work_space)
.wrapping_add(block_state_space)
.wrapping_add(ldm_space)
.wrapping_add(ldm_seq_space)
.wrapping_add(match_state_size)
.wrapping_add(token_space)
.wrapping_add(buffer_space)
.wrapping_add(external_seq_space)
}
/// Aggregate workspace-size components with C `size_t` wrapping semantics.
#[no_mangle]
pub extern "C" fn ZSTD_rust_estimateWorkspaceSize(
cctx_space: usize,
tmp_work_space: usize,
block_state_space: usize,
ldm_space: usize,
ldm_seq_space: usize,
match_state_size: usize,
token_space: usize,
buffer_space: usize,
external_seq_space: usize,
) -> usize {
estimate_workspace_size(
cctx_space,
tmp_work_space,
block_state_space,
ldm_space,
ldm_seq_space,
match_state_size,
token_space,
buffer_space,
external_seq_space,
)
}
/// Private C layout sizes needed by the CCtx workspace estimator.
///
/// The scalar sizes keep `ZSTD_CCtx`, `ldmEntry_t`, and the other private C
/// objects opaque while allowing Rust to own the workspace-sizing formula.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct ZSTD_rustCCtxWorkspaceSizing {
pub cctxSize: usize,
pub compressedBlockStateSize: usize,
pub seqDefSize: usize,
pub rawSeqSize: usize,
pub externalSequenceSize: usize,
pub tmpWorkspaceSize: usize,
pub wildcopyOverlength: usize,
pub matchTSize: usize,
pub optimalTSize: usize,
pub asanRedzoneSize: usize,
}
const _: () = {
assert!(size_of::<ZSTD_rustCCtxWorkspaceSizing>() == size_of::<[usize; 10]>());
};
const ZSTD_HASHLOG3_MAX: u32 = 17;
#[inline]
fn cctx_cwksp_alloc_size(size: usize, asan_redzone_size: usize) -> usize {
if size == 0 {
0
} else {
size.wrapping_add(asan_redzone_size.wrapping_mul(2))
}
}
#[inline]
fn cctx_cwksp_align(size: usize, alignment: usize) -> usize {
size.wrapping_add(alignment - 1) & !(alignment - 1)
}
#[inline]
fn cctx_cwksp_aligned64_alloc_size(size: usize, asan_redzone_size: usize) -> usize {
cctx_cwksp_alloc_size(cctx_cwksp_align(size, 64), asan_redzone_size)
}
#[inline]
fn cctx_ldm_table_size(
ldm_enable: c_int,
hash_log: u32,
bucket_size_log: u32,
asan_redzone_size: usize,
) -> usize {
if ldm_enable != ZSTD_RUST_PS_ENABLE {
return 0;
}
let hash_size = 1usize << hash_log;
let bucket_log = bucket_size_log.min(hash_log);
let bucket_size = 1usize << (hash_log - bucket_log);
cctx_cwksp_alloc_size(bucket_size, asan_redzone_size).wrapping_add(cctx_cwksp_alloc_size(
hash_size.wrapping_mul(2 * size_of::<u32>()),
asan_redzone_size,
))
}
/// Estimate the CCtx workspace using the private C layout sizes supplied by
/// the adapter. This owns the orchestration formerly kept in
/// `ZSTD_estimateCCtxSize_usingCCtxParams_internal()`.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_estimateCCtxWorkspaceSize(
cparams: ZSTD_compressionParameters,
ldm_enable: c_int,
ldm_hash_log: u32,
ldm_bucket_size_log: u32,
ldm_min_match_length: u32,
is_static: c_int,
use_row_match_finder: c_int,
buff_in_size: usize,
buff_out_size: usize,
pledged_src_size: u64,
use_sequence_producer: c_int,
max_block_size: usize,
sizing: *const ZSTD_rustCCtxWorkspaceSizing,
) -> usize {
if sizing.is_null() {
return 0;
}
let sizing = unsafe { *sizing };
let window_limit = 1u64.checked_shl(cparams.windowLog).unwrap_or(u64::MAX);
let window_size = (window_limit.min(pledged_src_size).min(usize::MAX as u64) as usize).max(1);
let block_size = ZSTD_resolveMaxBlockSize(max_block_size).min(window_size);
let max_nb_seq = ZSTD_rust_params_maxNbSeq(block_size, cparams.minMatch, use_sequence_producer);
let token_space = cctx_cwksp_alloc_size(
sizing.wildcopyOverlength.wrapping_add(block_size),
sizing.asanRedzoneSize,
)
.wrapping_add(cctx_cwksp_aligned64_alloc_size(
max_nb_seq.wrapping_mul(sizing.seqDefSize),
sizing.asanRedzoneSize,
))
.wrapping_add(3usize.wrapping_mul(cctx_cwksp_alloc_size(
max_nb_seq.wrapping_mul(size_of::<u8>()),
sizing.asanRedzoneSize,
)));
let tmp_workspace = cctx_cwksp_alloc_size(sizing.tmpWorkspaceSize, sizing.asanRedzoneSize);
let block_state_space = 2usize.wrapping_mul(cctx_cwksp_alloc_size(
sizing.compressedBlockStateSize,
sizing.asanRedzoneSize,
));
let match_state_sizing = ZSTD_rustMatchStateSizing {
hashLog3Max: ZSTD_HASHLOG3_MAX,
matchTSize: sizing.matchTSize,
optimalTSize: sizing.optimalTSize,
asanRedzoneSize: sizing.asanRedzoneSize,
};
let match_state_size = unsafe {
ZSTD_rust_params_estimateMatchStateSize(
cparams,
use_row_match_finder,
0,
1,
&match_state_sizing,
)
};
let ldm_space = cctx_ldm_table_size(
ldm_enable,
ldm_hash_log,
ldm_bucket_size_log,
sizing.asanRedzoneSize,
);
let max_nb_ldm_seq = if ldm_enable == ZSTD_RUST_PS_ENABLE && ldm_min_match_length != 0 {
block_size / ldm_min_match_length as usize
} else {
0
};
let ldm_seq_space = if ldm_enable == ZSTD_RUST_PS_ENABLE {
cctx_cwksp_aligned64_alloc_size(
max_nb_ldm_seq.wrapping_mul(sizing.rawSeqSize),
sizing.asanRedzoneSize,
)
} else {
0
};
let buffer_space = cctx_cwksp_alloc_size(buff_in_size, sizing.asanRedzoneSize)
.wrapping_add(cctx_cwksp_alloc_size(buff_out_size, sizing.asanRedzoneSize));
let cctx_space = if is_static != 0 {
cctx_cwksp_alloc_size(sizing.cctxSize, sizing.asanRedzoneSize)
} else {
0
};
let external_seq_space = if use_sequence_producer != 0 {
cctx_cwksp_aligned64_alloc_size(
crate::zstd_compress_api::ZSTD_sequenceBound(block_size)
.wrapping_mul(sizing.externalSequenceSize),
sizing.asanRedzoneSize,
)
} else {
0
};
estimate_workspace_size(
cctx_space,
tmp_workspace,
block_state_space,
ldm_space,
ldm_seq_space,
match_state_size,
token_space,
buffer_space,
external_seq_space,
)
}
/// Scalar outputs computed while resetting a private C compression context.
///
/// C still owns workspace resizing and all private reservations. Rust owns
/// this policy projection so the reset formula is testable without exposing
/// `ZSTD_CCtx` or `ZSTD_cwksp` layouts across the ABI.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rustCCtxResetPlan {
pub windowSize: usize,
pub blockSize: usize,
pub maxNbSeq: usize,
pub buffInSize: usize,
pub buffOutSize: usize,
pub maxNbLdmSeq: usize,
pub maxNbExternalSeq: usize,
pub neededSpace: usize,
pub needsIndexReset: c_int,
}
/// Scalar inputs needed to plan a private C compression-context reset.
///
/// The C adapter supplies resolved parameters, private object sizes, and the
/// already-reduced index/dictionary predicates. No private C pointer or
/// layout is passed through this record.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ZSTD_rustCCtxResetState {
pub cParams: ZSTD_compressionParameters,
pub ldmEnable: c_int,
pub ldmHashLog: c_uint,
pub ldmBucketSizeLog: c_uint,
pub ldmMinMatchLength: c_uint,
pub isStatic: c_int,
pub useRowMatchFinder: c_int,
pub inBufferBuffered: c_int,
pub outBufferBuffered: c_int,
pub useSequenceProducer: c_int,
pub initialized: c_int,
pub indexTooClose: c_int,
pub dictTooBig: c_int,
pub pledgedSrcSize: u64,
pub maxBlockSize: usize,
pub sizing: *const ZSTD_rustCCtxWorkspaceSizing,
pub plan: *mut ZSTD_rustCCtxResetPlan,
}
const _: () = {
assert!(size_of::<ZSTD_rustCCtxResetPlan>() == 9 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, windowSize) == 0);
assert!(offset_of!(ZSTD_rustCCtxResetPlan, blockSize) == size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, maxNbSeq) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, buffInSize) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, buffOutSize) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, maxNbLdmSeq) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, maxNbExternalSeq) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, neededSpace) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCCtxResetPlan, needsIndexReset) == size_of::<[usize; 8]>());
assert!(offset_of!(ZSTD_rustCCtxResetState, cParams) == 0);
assert!(
offset_of!(ZSTD_rustCCtxResetState, ldmEnable) == size_of::<ZSTD_compressionParameters>()
);
assert!(
offset_of!(ZSTD_rustCCtxResetState, pledgedSrcSize)
> offset_of!(ZSTD_rustCCtxResetState, dictTooBig)
);
assert!(
offset_of!(ZSTD_rustCCtxResetState, maxBlockSize)
== offset_of!(ZSTD_rustCCtxResetState, pledgedSrcSize) + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rustCCtxResetState, sizing)
== offset_of!(ZSTD_rustCCtxResetState, maxBlockSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rustCCtxResetState, plan)
== offset_of!(ZSTD_rustCCtxResetState, sizing) + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rustCCtxResetState>()
== offset_of!(ZSTD_rustCCtxResetState, plan) + size_of::<usize>()
);
};
/// Compute the scalar portion of `ZSTD_resetCCtx_internal()`.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_planCCtxReset(state: *const ZSTD_rustCCtxResetState) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.sizing.is_null() || state.plan.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let ldm_enabled = state.ldmEnable == ZSTD_RUST_PS_ENABLE;
if ldm_enabled && state.ldmMinMatchLength == 0 {
return ERROR(ZstdErrorCode::Generic);
}
let window_limit = 1u64
.checked_shl(state.cParams.windowLog)
.unwrap_or(u64::MAX);
let window_size = (window_limit
.min(state.pledgedSrcSize)
.min(usize::MAX as u64) as usize)
.max(1);
let block_size = state.maxBlockSize.min(window_size);
let max_nb_seq = ZSTD_rust_params_maxNbSeq(
block_size,
state.cParams.minMatch,
state.useSequenceProducer,
);
let buff_out_size = if state.outBufferBuffered != 0 {
crate::zstd_compress_api::ZSTD_compressBound(block_size).wrapping_add(1)
} else {
0
};
let buff_in_size = if state.inBufferBuffered != 0 {
window_size.wrapping_add(block_size)
} else {
0
};
let max_nb_ldm_seq = if ldm_enabled {
block_size / state.ldmMinMatchLength as usize
} else {
0
};
let max_nb_external_seq = if state.useSequenceProducer != 0 {
crate::zstd_compress_api::ZSTD_sequenceBound(block_size)
} else {
0
};
let needed_space = unsafe {
ZSTD_rust_estimateCCtxWorkspaceSize(
state.cParams,
state.ldmEnable,
state.ldmHashLog,
state.ldmBucketSizeLog,
state.ldmMinMatchLength,
state.isStatic,
state.useRowMatchFinder,
buff_in_size,
buff_out_size,
state.pledgedSrcSize,
state.useSequenceProducer,
state.maxBlockSize,
state.sizing,
)
};
if ERR_isError(needed_space) {
return needed_space;
}
unsafe {
*state.plan = ZSTD_rustCCtxResetPlan {
windowSize: window_size,
blockSize: block_size,
maxNbSeq: max_nb_seq,
buffInSize: buff_in_size,
buffOutSize: buff_out_size,
maxNbLdmSeq: max_nb_ldm_seq,
maxNbExternalSeq: max_nb_external_seq,
neededSpace: needed_space,
needsIndexReset: c_int::from(
state.indexTooClose != 0 || state.dictTooBig != 0 || state.initialized == 0,
),
};
}
0
}
type ResetCCtxStorageSetPointer = unsafe extern "C" fn(*mut c_void, c_int, *mut c_void);
type ResetCCtxStorageSetSize = unsafe extern "C" fn(*mut c_void, c_int, usize);
type ResetCCtxStorageSetInt = unsafe extern "C" fn(*mut c_void, c_int, c_int);
type ResetCCtxStorageReserve = unsafe extern "C" fn(*mut c_void, c_int, usize) -> *mut c_void;
type ResetCCtxStorageReserveFailed = unsafe extern "C" fn(*mut c_void) -> c_int;
type ResetCCtxStorageZero = unsafe extern "C" fn(*mut c_void, *mut c_void, usize);
type ResetCCtxStorageCallback = unsafe extern "C" fn(*mut c_void);
const RESET_CCTX_RESERVE_ALIGNED64: c_int = 0;
const RESET_CCTX_RESERVE_BUFFER: c_int = 1;
const RESET_CCTX_POINTER_SEQ_START: c_int = 0;
const RESET_CCTX_POINTER_LDM_HASH: c_int = 1;
const RESET_CCTX_POINTER_LDM_SEQUENCES: c_int = 2;
const RESET_CCTX_POINTER_EXTERNAL_SEQUENCES: c_int = 3;
const RESET_CCTX_POINTER_LITERALS: c_int = 4;
const RESET_CCTX_POINTER_INPUT_BUFFER: c_int = 5;
const RESET_CCTX_POINTER_OUTPUT_BUFFER: c_int = 6;
const RESET_CCTX_POINTER_LL_CODE: c_int = 7;
const RESET_CCTX_POINTER_ML_CODE: c_int = 8;
const RESET_CCTX_POINTER_OF_CODE: c_int = 9;
const RESET_CCTX_POINTER_LDM_BUCKETS: c_int = 10;
const RESET_CCTX_POINTER_PREV_CBLOCK: c_int = 11;
const RESET_CCTX_POINTER_NEXT_CBLOCK: c_int = 12;
const RESET_CCTX_POINTER_TMP_WORKSPACE: c_int = 13;
const RESET_CCTX_SIZE_MAX_NB_SEQ: c_int = 0;
const RESET_CCTX_SIZE_MAX_NB_LIT: c_int = 1;
const RESET_CCTX_SIZE_MAX_NB_LDM_SEQ: c_int = 2;
const RESET_CCTX_SIZE_EXTERNAL_SEQ_CAPACITY: c_int = 3;
const RESET_CCTX_SIZE_INPUT_BUFFER: c_int = 4;
const RESET_CCTX_SIZE_OUTPUT_BUFFER: c_int = 5;
const RESET_CCTX_SIZE_TMP_WORKSPACE: c_int = 6;
const RESET_CCTX_INT_BUFFERED_POLICY: c_int = 0;
const RESET_CCTX_INT_INITIALIZED: c_int = 1;
const RESET_CCTX_INDEX_RESET: c_int = 1;
/// C-owned workspace/layout projection for the post-match-state reset tail.
///
/// Rust owns the reservation order and size policy. C retains the private
/// `ZSTD_CCtx`/`ldmState_t` layouts and implements the workspace callbacks.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxStorageState {
pub callbackContext: *mut c_void,
pub ldmEnable: c_int,
pub hasExtSeqProd: c_int,
pub hashLog: c_uint,
pub bucketSizeLog: c_uint,
pub blockSize: usize,
pub maxNbSeq: usize,
pub maxNbLdmSeq: usize,
pub maxNbExternalSeq: usize,
pub buffInSize: usize,
pub buffOutSize: usize,
pub seqDefSize: usize,
pub ldmEntrySize: usize,
pub rawSeqSize: usize,
pub externalSequenceSize: usize,
pub byteSize: usize,
pub wildcopyOverlength: usize,
pub bufferedPolicy: c_int,
pub setPointer: Option<ResetCCtxStorageSetPointer>,
pub setSize: Option<ResetCCtxStorageSetSize>,
pub setInt: Option<ResetCCtxStorageSetInt>,
pub reserve: Option<ResetCCtxStorageReserve>,
pub reserveFailed: Option<ResetCCtxStorageReserveFailed>,
pub zero: Option<ResetCCtxStorageZero>,
pub windowInit: Option<ResetCCtxStorageCallback>,
pub resetExternalSequences: Option<ResetCCtxStorageCallback>,
}
const _: () = {
assert!(size_of::<ResetCCtxStorageSetPointer>() == size_of::<usize>());
assert!(size_of::<ResetCCtxStorageSetSize>() == size_of::<usize>());
assert!(size_of::<ResetCCtxStorageSetInt>() == size_of::<usize>());
assert!(size_of::<ResetCCtxStorageReserve>() == size_of::<usize>());
assert!(size_of::<ResetCCtxStorageReserveFailed>() == size_of::<usize>());
assert!(size_of::<ResetCCtxStorageZero>() == size_of::<usize>());
assert!(size_of::<ResetCCtxStorageCallback>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxStorageState, callbackContext) == 0);
assert!(offset_of!(ZSTD_rust_resetCCtxStorageState, ldmEnable) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, hasExtSeqProd)
== size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, hashLog)
> offset_of!(ZSTD_rust_resetCCtxStorageState, hasExtSeqProd)
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, bucketSizeLog)
== offset_of!(ZSTD_rust_resetCCtxStorageState, hashLog) + size_of::<c_uint>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, blockSize)
> offset_of!(ZSTD_rust_resetCCtxStorageState, bucketSizeLog)
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbSeq)
== offset_of!(ZSTD_rust_resetCCtxStorageState, blockSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbLdmSeq)
== offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbSeq) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbExternalSeq)
== offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbLdmSeq) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, buffInSize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbExternalSeq) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, buffOutSize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, buffInSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, seqDefSize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, buffOutSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, ldmEntrySize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, seqDefSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, rawSeqSize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, ldmEntrySize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, externalSequenceSize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, rawSeqSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, byteSize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, externalSequenceSize)
+ size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, wildcopyOverlength)
== offset_of!(ZSTD_rust_resetCCtxStorageState, byteSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, bufferedPolicy)
> offset_of!(ZSTD_rust_resetCCtxStorageState, wildcopyOverlength)
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, setSize)
== offset_of!(ZSTD_rust_resetCCtxStorageState, setPointer) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, setInt)
== offset_of!(ZSTD_rust_resetCCtxStorageState, setSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxStorageState, reserve)
== offset_of!(ZSTD_rust_resetCCtxStorageState, setInt) + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_resetCCtxStorageState>()
== offset_of!(ZSTD_rust_resetCCtxStorageState, resetExternalSequences)
+ size_of::<usize>()
);
};
type ResetCCtxWorkspaceCreate = unsafe extern "C" fn(*mut c_void, usize) -> usize;
type ResetCCtxWorkspaceReserveObject = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
type ResetCCtxTailCallback = unsafe extern "C" fn(*mut c_void) -> usize;
/// C-owned workspace operations used by the CCtx reset policy.
///
/// Rust owns the resize decision, error ordering, object-reservation order,
/// and workspace-clear boundary. C retains the workspace representation,
/// allocator, and private CCtx publication callbacks.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxWorkspaceState {
callbackContext: *mut c_void,
isStatic: c_int,
workspaceTooSmall: c_int,
workspaceWasteful: c_int,
neededSpace: usize,
compressedBlockStateSize: usize,
tmpWorkspaceSize: usize,
needsIndexReset: *mut c_int,
bumpOversizedDuration: Option<ResetCCtxStorageCallback>,
freeWorkspace: Option<ResetCCtxStorageCallback>,
createWorkspace: Option<ResetCCtxWorkspaceCreate>,
reserveObject: Option<ResetCCtxWorkspaceReserveObject>,
setPointer: Option<ResetCCtxStorageSetPointer>,
setSize: Option<ResetCCtxStorageSetSize>,
clearWorkspace: Option<ResetCCtxStorageCallback>,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_resetCCtxWorkspaceState, callbackContext) == 0);
assert!(offset_of!(ZSTD_rust_resetCCtxWorkspaceState, isStatic) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, workspaceTooSmall)
== size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, workspaceWasteful)
== size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, neededSpace)
> offset_of!(ZSTD_rust_resetCCtxWorkspaceState, workspaceWasteful)
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, compressedBlockStateSize)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, neededSpace) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, tmpWorkspaceSize)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, compressedBlockStateSize)
+ size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, needsIndexReset)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, tmpWorkspaceSize) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, freeWorkspace)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, bumpOversizedDuration)
+ size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, createWorkspace)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, freeWorkspace) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, reserveObject)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, createWorkspace) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, setPointer)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, reserveObject) + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxWorkspaceState, setSize)
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, setPointer) + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_resetCCtxWorkspaceState>()
== offset_of!(ZSTD_rust_resetCCtxWorkspaceState, clearWorkspace) + size_of::<usize>()
);
};
/// C-owned operations used by the post-workspace CCtx reset tail.
///
/// Rust owns the operation order, compressed-block reset, and error
/// propagation. C retains the private context initialization, match-state
/// reset, and storage publication operations.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxTailState {
callbackContext: *mut c_void,
initialize: Option<ResetCCtxStorageCallback>,
compressedBlockState: *mut ZSTD_compressedBlockState_t,
resetMatchState: Option<ResetCCtxTailCallback>,
resetStorage: Option<ResetCCtxTailCallback>,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_resetCCtxTailState, callbackContext) == 0);
assert!(offset_of!(ZSTD_rust_resetCCtxTailState, initialize) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxTailState, compressedBlockState) == 2 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_resetCCtxTailState, resetMatchState) == 3 * size_of::<usize>());
assert!(
size_of::<ZSTD_rust_resetCCtxTailState>()
== offset_of!(ZSTD_rust_resetCCtxTailState, resetStorage) + size_of::<usize>()
);
};
type ResetCCtxWorkspacePrepare =
unsafe extern "C" fn(*mut c_void, usize, usize, usize, *mut c_int, *mut c_int);
type ResetCCtxTailPrepare = unsafe extern "C" fn(*mut c_void, usize, c_int);
/// State for composing the already-projected CCtx reset policies.
///
/// Rust owns the plan/workspace/tail order and the scalar hand-off between
/// those helpers. C retains the workspace checks, context initialization,
/// match-state reset, and private pointer publication behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxInternalState {
pub callbackContext: *mut c_void,
pub resetState: *const ZSTD_rustCCtxResetState,
pub storageState: *mut ZSTD_rust_resetCCtxStorageState,
pub workspaceState: *mut ZSTD_rust_resetCCtxWorkspaceState,
pub tailState: *mut ZSTD_rust_resetCCtxTailState,
pub prepareWorkspace: Option<ResetCCtxWorkspacePrepare>,
pub prepareTail: Option<ResetCCtxTailPrepare>,
}
const _: () = {
assert!(size_of::<ResetCCtxWorkspacePrepare>() == size_of::<usize>());
assert!(size_of::<ResetCCtxTailPrepare>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxInternalState, callbackContext) == 0);
assert!(offset_of!(ZSTD_rust_resetCCtxInternalState, resetState) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxInternalState, storageState) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxInternalState, workspaceState) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxInternalState, tailState) == 4 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxInternalState, prepareWorkspace) == 5 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_resetCCtxInternalState, prepareTail) == 6 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_resetCCtxInternalState>() == 7 * size_of::<usize>());
};
#[inline]
fn project_cctx_reset_plan(
plan: &ZSTD_rustCCtxResetPlan,
storage_state: &mut ZSTD_rust_resetCCtxStorageState,
workspace_state: &mut ZSTD_rust_resetCCtxWorkspaceState,
) {
storage_state.blockSize = plan.blockSize;
storage_state.maxNbSeq = plan.maxNbSeq;
storage_state.maxNbLdmSeq = plan.maxNbLdmSeq;
storage_state.maxNbExternalSeq = plan.maxNbExternalSeq;
storage_state.buffInSize = plan.buffInSize;
storage_state.buffOutSize = plan.buffOutSize;
workspace_state.neededSpace = plan.neededSpace;
}
#[inline]
fn run_cctx_reset_policy(
plan: impl FnOnce() -> usize,
prepare_workspace: impl FnOnce(),
reset_workspace: impl FnOnce() -> usize,
prepare_tail: impl FnOnce(),
reset_tail: impl FnOnce() -> usize,
) -> usize {
let result = plan();
if ERR_isError(result) {
return result;
}
prepare_workspace();
let result = reset_workspace();
if ERR_isError(result) {
return result;
}
prepare_tail();
reset_tail()
}
/// Compose the private CCtx reset without moving C-only layout operations.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtxInternal(
state: *const ZSTD_rust_resetCCtxInternalState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(prepare_workspace) = state.prepareWorkspace else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(prepare_tail) = state.prepareTail else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callbackContext.is_null()
|| state.resetState.is_null()
|| state.storageState.is_null()
|| state.workspaceState.is_null()
|| state.tailState.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let reset_state = unsafe { &*state.resetState };
if reset_state.plan.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let workspace_state = unsafe { &*state.workspaceState };
if workspace_state.needsIndexReset.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let plan = reset_state.plan;
let reset_state_ptr = state.resetState;
let storage_state_ptr = state.storageState;
let workspace_state_ptr = state.workspaceState;
let tail_state_ptr = state.tailState;
let callback_context = state.callbackContext;
run_cctx_reset_policy(
|| unsafe {
let result = ZSTD_rust_planCCtxReset(reset_state_ptr);
if ERR_isError(result) {
return result;
}
let plan = &*plan;
let storage_state = &mut *storage_state_ptr;
let workspace_state = &mut *workspace_state_ptr;
project_cctx_reset_plan(plan, storage_state, workspace_state);
*workspace_state.needsIndexReset = plan.needsIndexReset;
0
},
|| unsafe {
let plan = &*plan;
let workspace_state = &mut *workspace_state_ptr;
prepare_workspace(
callback_context,
plan.neededSpace,
plan.windowSize,
plan.blockSize,
&mut workspace_state.workspaceTooSmall,
&mut workspace_state.workspaceWasteful,
);
},
|| unsafe { ZSTD_rust_resetCCtxWorkspace(workspace_state_ptr) },
|| unsafe {
let workspace_state = &*workspace_state_ptr;
prepare_tail(
callback_context,
(&*plan).blockSize,
*workspace_state.needsIndexReset,
);
},
|| unsafe { ZSTD_rust_resetCCtxTail(tail_state_ptr) },
)
}
/// Reserve and publish the private CCtx storage that follows match-state reset.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtxStorage(
state: *const ZSTD_rust_resetCCtxStorageState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callbackContext.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let Some(set_pointer) = state.setPointer else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(set_size) = state.setSize else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(set_int) = state.setInt else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reserve_callback) = state.reserve else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reserve_failed) = state.reserveFailed else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(zero) = state.zero else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(window_init) = state.windowInit else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reset_external_sequences) = state.resetExternalSequences else {
return ERROR(ZstdErrorCode::Generic);
};
let reserve = |kind: c_int, size: usize| -> Result<*mut c_void, usize> {
let pointer = unsafe { reserve_callback(state.callbackContext, kind, size) };
if unsafe { reserve_failed(state.callbackContext) } != 0 {
Err(ERROR(ZstdErrorCode::MemoryAllocation))
} else {
Ok(pointer)
}
};
let publish_pointer = |kind: c_int, pointer: *mut c_void| unsafe {
set_pointer(state.callbackContext, kind, pointer)
};
let publish_size =
|kind: c_int, value: usize| unsafe { set_size(state.callbackContext, kind, value) };
let sequence_start = match reserve(
RESET_CCTX_RESERVE_ALIGNED64,
state.maxNbSeq.wrapping_mul(state.seqDefSize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_SEQ_START, sequence_start);
if state.ldmEnable == ZSTD_RUST_PS_ENABLE {
if state.bucketSizeLog > state.hashLog {
return ERROR(ZstdErrorCode::Generic);
}
let Some(hash_size) = 1usize.checked_shl(state.hashLog) else {
return ERROR(ZstdErrorCode::Generic);
};
let hash_table = match reserve(
RESET_CCTX_RESERVE_ALIGNED64,
hash_size.wrapping_mul(state.ldmEntrySize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_LDM_HASH, hash_table);
unsafe {
zero(
state.callbackContext,
hash_table,
hash_size.wrapping_mul(state.ldmEntrySize),
)
};
let ldm_sequences = match reserve(
RESET_CCTX_RESERVE_ALIGNED64,
state.maxNbLdmSeq.wrapping_mul(state.rawSeqSize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_LDM_SEQUENCES, ldm_sequences);
publish_size(RESET_CCTX_SIZE_MAX_NB_LDM_SEQ, state.maxNbLdmSeq);
unsafe { window_init(state.callbackContext) };
}
if state.hasExtSeqProd != 0 {
let external_sequences = match reserve(
RESET_CCTX_RESERVE_ALIGNED64,
state
.maxNbExternalSeq
.wrapping_mul(state.externalSequenceSize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_EXTERNAL_SEQUENCES, external_sequences);
publish_size(
RESET_CCTX_SIZE_EXTERNAL_SEQ_CAPACITY,
state.maxNbExternalSeq,
);
}
let literals = match reserve(
RESET_CCTX_RESERVE_BUFFER,
state.blockSize.wrapping_add(state.wildcopyOverlength),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_LITERALS, literals);
publish_size(RESET_CCTX_SIZE_MAX_NB_LIT, state.blockSize);
unsafe {
set_int(
state.callbackContext,
RESET_CCTX_INT_BUFFERED_POLICY,
state.bufferedPolicy,
)
};
let input_buffer = match reserve(RESET_CCTX_RESERVE_BUFFER, state.buffInSize) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_INPUT_BUFFER, input_buffer);
publish_size(RESET_CCTX_SIZE_INPUT_BUFFER, state.buffInSize);
let output_buffer = match reserve(RESET_CCTX_RESERVE_BUFFER, state.buffOutSize) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_OUTPUT_BUFFER, output_buffer);
publish_size(RESET_CCTX_SIZE_OUTPUT_BUFFER, state.buffOutSize);
if state.ldmEnable == ZSTD_RUST_PS_ENABLE {
let bucket_count = match 1usize.checked_shl(state.hashLog - state.bucketSizeLog) {
Some(count) => count,
None => return ERROR(ZstdErrorCode::Generic),
};
let bucket_offsets = match reserve(
RESET_CCTX_RESERVE_BUFFER,
bucket_count.wrapping_mul(state.byteSize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_LDM_BUCKETS, bucket_offsets);
unsafe {
zero(
state.callbackContext,
bucket_offsets,
bucket_count.wrapping_mul(state.byteSize),
)
};
}
unsafe { reset_external_sequences(state.callbackContext) };
publish_size(RESET_CCTX_SIZE_MAX_NB_SEQ, state.maxNbSeq);
let ll_code = match reserve(
RESET_CCTX_RESERVE_BUFFER,
state.maxNbSeq.wrapping_mul(state.byteSize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_LL_CODE, ll_code);
let ml_code = match reserve(
RESET_CCTX_RESERVE_BUFFER,
state.maxNbSeq.wrapping_mul(state.byteSize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_ML_CODE, ml_code);
let of_code = match reserve(
RESET_CCTX_RESERVE_BUFFER,
state.maxNbSeq.wrapping_mul(state.byteSize),
) {
Ok(pointer) => pointer,
Err(error) => return error,
};
publish_pointer(RESET_CCTX_POINTER_OF_CODE, of_code);
unsafe { set_int(state.callbackContext, RESET_CCTX_INT_INITIALIZED, 1) };
0
}
/// Own the CCtx workspace resize, object reservation, and clear order.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtxWorkspace(
state: *const ZSTD_rust_resetCCtxWorkspaceState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callbackContext.is_null() || state.needsIndexReset.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let Some(bump_oversized_duration) = state.bumpOversizedDuration else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(free_workspace) = state.freeWorkspace else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(create_workspace) = state.createWorkspace else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reserve_object) = state.reserveObject else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(set_pointer) = state.setPointer else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(set_size) = state.setSize else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(clear_workspace) = state.clearWorkspace else {
return ERROR(ZstdErrorCode::Generic);
};
unsafe {
if state.isStatic == 0 {
bump_oversized_duration(state.callbackContext);
}
let resize_workspace = state.workspaceTooSmall != 0 || state.workspaceWasteful != 0;
if resize_workspace {
if state.isStatic != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
*state.needsIndexReset = RESET_CCTX_INDEX_RESET;
free_workspace(state.callbackContext);
let result = create_workspace(state.callbackContext, state.neededSpace);
if ERR_isError(result) {
return result;
}
let previous = reserve_object(state.callbackContext, state.compressedBlockStateSize);
if previous.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
set_pointer(
state.callbackContext,
RESET_CCTX_POINTER_PREV_CBLOCK,
previous,
);
let next = reserve_object(state.callbackContext, state.compressedBlockStateSize);
if next.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
set_pointer(state.callbackContext, RESET_CCTX_POINTER_NEXT_CBLOCK, next);
let tmp_workspace = reserve_object(state.callbackContext, state.tmpWorkspaceSize);
if tmp_workspace.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
set_pointer(
state.callbackContext,
RESET_CCTX_POINTER_TMP_WORKSPACE,
tmp_workspace,
);
set_size(
state.callbackContext,
RESET_CCTX_SIZE_TMP_WORKSPACE,
state.tmpWorkspaceSize,
);
}
clear_workspace(state.callbackContext);
}
0
}
/// Own the callback order for the post-workspace CCtx reset tail.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtxTail(
state: *const ZSTD_rust_resetCCtxTailState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callbackContext.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let Some(initialize) = state.initialize else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reset_match_state) = state.resetMatchState else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reset_storage) = state.resetStorage else {
return ERROR(ZstdErrorCode::Generic);
};
if state.compressedBlockState.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
initialize(state.callbackContext);
ZSTD_rust_resetCompressedBlockState(state.compressedBlockState);
let result = reset_match_state(state.callbackContext);
if ERR_isError(result) {
return result;
}
let result = reset_storage(state.callbackContext);
if ERR_isError(result) {
return result;
}
}
0
}
#[inline]
fn max_estimate_cctx_size(
estimate0: usize,
estimate1: usize,
estimate2: usize,
estimate3: usize,
) -> usize {
estimate0.max(estimate1).max(estimate2).max(estimate3)
}
type EstimateCCtxSizeGetCParamsFn =
unsafe extern "C" fn(*mut c_void, c_int, u64) -> ZSTD_compressionParameters;
type EstimateCCtxSizeEstimateFn =
unsafe extern "C" fn(*mut c_void, ZSTD_compressionParameters) -> usize;
/// Rust owns the fixed source-size tier policy for CCtx estimates. C keeps
/// the configuration-sensitive parameter lookup and workspace estimator behind
/// callbacks, so this seam does not expose any private context layout.
#[repr(C)]
pub struct ZSTD_rust_estimateCCtxSizeState {
callback_context: *mut c_void,
compression_level: c_int,
get_c_params: EstimateCCtxSizeGetCParamsFn,
estimate: EstimateCCtxSizeEstimateFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, compression_level) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, get_c_params) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, estimate) == 3 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_estimateCCtxSizeState>() == 4 * size_of::<usize>());
};
/// Evaluate the four source-size tiers used by `ZSTD_estimateCCtxSize()` and
/// return the largest raw result, including size_t-encoded error values.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_estimateCCtxSizeInternal(
state: *const ZSTD_rust_estimateCCtxSizeState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let source_size_tiers = [16 * 1024, 128 * 1024, 256 * 1024, ZSTD_CONTENTSIZE_UNKNOWN];
let mut estimates = [0usize; 4];
for (estimate, &source_size_hint) in estimates.iter_mut().zip(source_size_tiers.iter()) {
let c_params = unsafe {
(state.get_c_params)(
state.callback_context,
state.compression_level,
source_size_hint,
)
};
*estimate = unsafe { (state.estimate)(state.callback_context, c_params) };
}
max_estimate_cctx_size(estimates[0], estimates[1], estimates[2], estimates[3])
}
/// Return the largest raw estimate, including any C size_t error values.
#[no_mangle]
pub extern "C" fn ZSTD_rust_maxEstimateCCtxSize(
estimate0: usize,
estimate1: usize,
estimate2: usize,
estimate3: usize,
) -> usize {
max_estimate_cctx_size(estimate0, estimate1, estimate2, estimate3)
}
#[inline]
fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) {
debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0);
debug_assert!(table.len() < (1usize << 31));
/* Protect special index values < ZSTD_WINDOW_START_INDEX. */
let reducer_threshold = reducer_value.wrapping_add(ZSTD_WINDOW_START_INDEX);
let mut rows = table.chunks_exact_mut(ZSTD_ROWSIZE);
for row in &mut rows {
for cell in row {
let value = *cell;
*cell = if preserve_mark && value == ZSTD_DUBT_UNSORTED_MARK {
/* Keep the btlazy2 unsorted marker across table reduction. */
ZSTD_DUBT_UNSORTED_MARK
} else if value < reducer_threshold {
0
} else {
value.wrapping_sub(reducer_value)
};
}
}
debug_assert!(rows.into_remainder().is_empty());
}
/// Rust implementation of the C match-table reduction leaf.
///
/// The C caller supplies a clear zero/one `preserve_mark` value for the
/// strategy-specific btlazy2 policy.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_reduceTable(
table: *mut u32,
size: u32,
reducer_value: u32,
preserve_mark: c_int,
) {
debug_assert!(!table.is_null() || size == 0);
debug_assert_eq!(size % ZSTD_ROWSIZE as u32, 0);
debug_assert!(size < (1u32 << 31));
if size == 0 {
return;
}
let table = unsafe { std::slice::from_raw_parts_mut(table, size as usize) };
reduce_table_internal(table, reducer_value, preserve_mark != 0);
}
/// Reduce the match tables selected by C's stateful overflow-correction path.
///
/// C retains match-state access and table selection. Each non-zero size is
/// paired with a validated mutable table pointer; zero-sized optional tables
/// may use null pointers. Only the chain table receives the btlazy2 marker
/// policy.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_reduceIndex(
hash_table: *mut u32,
hash_size: u32,
chain_table: *mut u32,
chain_size: u32,
hash_table3: *mut u32,
hash_size3: u32,
reducer_value: u32,
preserve_chain_mark: c_int,
) {
debug_assert!(!hash_table.is_null() || hash_size == 0);
debug_assert!(!chain_table.is_null() || chain_size == 0);
debug_assert!(!hash_table3.is_null() || hash_size3 == 0);
debug_assert!(preserve_chain_mark == 0 || preserve_chain_mark == 1);
unsafe {
ZSTD_rust_reduceTable(hash_table, hash_size, reducer_value, 0);
ZSTD_rust_reduceTable(chain_table, chain_size, reducer_value, preserve_chain_mark);
ZSTD_rust_reduceTable(hash_table3, hash_size3, reducer_value, 0);
}
}
/// Select the match tables participating in overflow correction and delegate
/// their cell updates to the Rust reducer. C supplies private table pointers;
/// Rust owns the strategy, row-matchfinder, dedicated-dictionary, and marker
/// policy that determines the participating table set.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_reduceIndexForMatchState(
hash_table: *mut u32,
hash_log: c_uint,
chain_table: *mut u32,
chain_log: c_uint,
hash_table3: *mut u32,
hash_log3: c_uint,
strategy: c_int,
use_row_match_finder: c_int,
dedicated_dict_search: c_int,
reducer_value: c_uint,
) {
let hash_size = 1u32.wrapping_shl(hash_log);
let (chain_table, chain_size) = if ZSTD_rust_params_allocateChainTable(
strategy,
use_row_match_finder,
dedicated_dict_search,
) != 0
{
(chain_table, 1u32.wrapping_shl(chain_log))
} else {
(ptr::null_mut(), 0)
};
let (hash_table3, hash_size3) = if hash_log3 != 0 {
(hash_table3, 1u32.wrapping_shl(hash_log3))
} else {
(ptr::null_mut(), 0)
};
unsafe {
ZSTD_rust_reduceIndex(
hash_table,
hash_size,
chain_table,
chain_size,
hash_table3,
hash_size3,
reducer_value,
c_int::from(strategy == ZSTD_BTLAZY2),
);
}
}
/// Copies a CDict match table into a CCtx, removing short-cache tags when the
/// C-owned compression parameters say the source table is tagged.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_copyCDictTableIntoCCtx(
dst: *mut u32,
src: *const u32,
table_size: usize,
tagged: c_int,
) {
if tagged != 0 {
for i in 0..table_size {
let index = unsafe { *src.add(i) } >> ZSTD_SHORT_CACHE_TAG_BITS;
unsafe { *dst.add(i) = index };
}
} else if table_size != 0 {
unsafe { ptr::copy_nonoverlapping(src, dst, table_size) };
}
}
/// Clear the previous block's repcodes before the next regular compression.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_invalidateRepCodes(rep: *mut u32) {
debug_assert!(!rep.is_null());
let rep = unsafe { std::slice::from_raw_parts_mut(rep, ZSTD_REP_NUM) };
rep.fill(0);
}
/// Clear a C-owned compression window using the original `size_t`-to-`U32`
/// conversion. The caller computes the pointer difference while the private
/// `ZSTD_window_t` layout remains entirely on the C side.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_windowClear(
end_t: usize,
low_limit: *mut u32,
dict_limit: *mut u32,
) {
debug_assert!(!low_limit.is_null());
debug_assert!(!dict_limit.is_null());
let end = end_t as u32;
unsafe {
*low_limit = end;
*dict_limit = end;
}
}
/// Invalidate a C-owned match-state projection after the caller computes the
/// private window pointer difference. Rust owns the paired window-limit and
/// match-state field updates while C retains the match-state layout.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_invalidateMatchState(
end_t: usize,
low_limit: *mut u32,
dict_limit: *mut u32,
next_to_update: *mut u32,
loaded_dict_end: *mut u32,
lit_length_sum: *mut u32,
dict_match_state: *mut *const c_void,
) {
debug_assert!(!low_limit.is_null());
debug_assert!(!dict_limit.is_null());
debug_assert!(!next_to_update.is_null());
debug_assert!(!loaded_dict_end.is_null());
debug_assert!(!lit_length_sum.is_null());
debug_assert!(!dict_match_state.is_null());
unsafe {
ZSTD_rust_windowClear(end_t, low_limit, dict_limit);
*next_to_update = *dict_limit;
*loaded_dict_end = 0;
*lit_length_sum = 0;
*dict_match_state = ptr::null();
}
}
const HASH_READ_SIZE: u32 = 8;
/// Private C window fields projected for the shared window-update policy.
/// Pointer arithmetic is intentionally performed as wrapping address math to
/// mirror the original C implementation's pointer-overflow contract.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rust_windowUpdateState {
pub nextSrc: *const c_void,
pub base: *const c_void,
pub dictBase: *const c_void,
pub dictLimit: u32,
pub lowLimit: u32,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_windowUpdateState, nextSrc) == 0);
assert!(offset_of!(ZSTD_rust_windowUpdateState, base) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_windowUpdateState, dictBase) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_windowUpdateState, dictLimit) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_windowUpdateState, lowLimit)
== 3 * size_of::<usize>() + size_of::<u32>()
);
assert!(
size_of::<ZSTD_rust_windowUpdateState>() == 3 * size_of::<usize>() + 2 * size_of::<u32>()
);
};
static WINDOW_INIT_SENTINEL: [u8; 2] = [b' ', 0];
/// Initialize the projected window fields. C retains the containing object
/// and its overflow-correction counter.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_windowInit(state: *mut ZSTD_rust_windowUpdateState) {
if state.is_null() {
return;
}
let base = WINDOW_INIT_SENTINEL.as_ptr();
unsafe {
*state = ZSTD_rust_windowUpdateState {
nextSrc: base.wrapping_add(2).cast(),
base: base.cast(),
dictBase: base.cast(),
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
};
}
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowIsEmpty(
next_src: *const c_void,
base: *const c_void,
dict_limit: u32,
low_limit: u32,
) -> c_uint {
(dict_limit == ZSTD_WINDOW_START_INDEX
&& low_limit == ZSTD_WINDOW_START_INDEX
&& (next_src as usize).wrapping_sub(base as usize) == ZSTD_WINDOW_START_INDEX as usize)
as c_uint
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowHasExtDict(dict_limit: u32, low_limit: u32) -> c_uint {
(low_limit < dict_limit) as c_uint
}
/// Project the dictionary/window limit policy while C retains pointer-owned
/// match-state invalidation and the private window layout.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rust_windowDictState {
pub lowLimit: u32,
pub dictLimit: u32,
pub loadedDictEnd: u32,
pub invalidate: u32,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_windowDictState, lowLimit) == 0);
assert!(offset_of!(ZSTD_rust_windowDictState, dictLimit) == size_of::<u32>());
assert!(offset_of!(ZSTD_rust_windowDictState, loadedDictEnd) == 2 * size_of::<u32>());
assert!(offset_of!(ZSTD_rust_windowDictState, invalidate) == 3 * size_of::<u32>());
assert!(size_of::<ZSTD_rust_windowDictState>() == 4 * size_of::<u32>());
};
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowEnforceMaxDist(
block_end_idx: u32,
max_dist: u32,
mut state: ZSTD_rust_windowDictState,
) -> ZSTD_rust_windowDictState {
state.invalidate = 0;
if block_end_idx > max_dist.wrapping_add(state.loadedDictEnd) {
let new_low_limit = block_end_idx.wrapping_sub(max_dist);
if state.lowLimit < new_low_limit {
state.lowLimit = new_low_limit;
}
if state.dictLimit < state.lowLimit {
state.dictLimit = state.lowLimit;
}
state.loadedDictEnd = 0;
state.invalidate = 1;
}
state
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowCheckDictValidity(
block_end_idx: u32,
max_dist: u32,
loaded_dict_end: u32,
dict_limit: u32,
) -> c_uint {
(block_end_idx > loaded_dict_end.wrapping_add(max_dist) || loaded_dict_end != dict_limit)
as c_uint
}
/// Update the rolling prefix/ext-dictionary window for one input segment.
/// C owns the containing match state; Rust owns this five-field transition.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_windowUpdate(
state: *mut ZSTD_rust_windowUpdateState,
src: *const c_void,
src_size: usize,
force_non_contiguous: c_int,
) -> c_uint {
if state.is_null() {
return 1;
}
let state = unsafe { &mut *state };
if src_size == 0 {
return 1;
}
debug_assert!(!state.base.is_null());
debug_assert!(!state.dictBase.is_null());
let ip = src as usize;
let input_end = ip.wrapping_add(src_size);
let mut contiguous = 1;
if ip != state.nextSrc as usize || force_non_contiguous != 0 {
let distance_from_base = (state.nextSrc as usize).wrapping_sub(state.base as usize);
state.lowLimit = state.dictLimit;
debug_assert!(distance_from_base <= u32::MAX as usize);
state.dictLimit = distance_from_base as u32;
state.dictBase = state.base;
state.base = ip.wrapping_sub(distance_from_base) as *const c_void;
if state.dictLimit.wrapping_sub(state.lowLimit) < HASH_READ_SIZE {
state.lowLimit = state.dictLimit;
}
contiguous = 0;
}
state.nextSrc = input_end as *const c_void;
let dict_base = state.dictBase as usize;
if input_end > dict_base.wrapping_add(state.lowLimit as usize)
&& ip < dict_base.wrapping_add(state.dictLimit as usize)
{
let high_input_idx = input_end.wrapping_sub(dict_base);
debug_assert!(high_input_idx < u32::MAX as usize);
let low_limit_max = if high_input_idx > state.dictLimit as usize {
state.dictLimit
} else {
high_input_idx as u32
};
state.lowLimit = low_limit_max;
}
contiguous
}
#[inline]
fn set_pledged_src_size(
stream_stage: c_int,
pledged_src_size: u64,
pledged_src_size_plus_one: *mut u64,
) -> usize {
if stream_stage != ZSTD_CSTREAM_STAGE_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
debug_assert!(!pledged_src_size_plus_one.is_null());
unsafe {
*pledged_src_size_plus_one = pledged_src_size.wrapping_add(1);
}
0
}
/// Set the pledged source size through scalar stage state and a C-owned field.
/// The stage error remains the public `stage_wrong` encoding, while the
/// unsigned increment preserves C's defined wrap at `U64::MAX`.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_setPledgedSrcSize(
stream_stage: c_int,
pledged_src_size: u64,
pledged_src_size_plus_one: *mut u64,
) -> usize {
set_pledged_src_size(stream_stage, pledged_src_size, pledged_src_size_plus_one)
}
#[inline]
fn zeroed_state() -> ZSTD_compressedBlockState_t {
/* The state contains only integer arrays and enum fields. */
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() }
}
#[inline]
fn checked_table_size(log: u32) -> Option<usize> {
1usize.checked_shl(log)
}
#[inline]
fn ceil_log2(size: usize) -> u32 {
if size <= 1 {
0
} else {
usize::BITS - (size - 1).leading_zeros()
}
}
#[inline]
unsafe fn write_empty_sequence_block(dst: *mut u8, dst_capacity: usize) -> usize {
/* Keep the original C helper's four-byte write and capacity check. */
if dst_capacity < 4 {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let header = 1u32.to_le_bytes();
unsafe { ptr::copy_nonoverlapping(header.as_ptr(), dst, header.len()) };
ZSTD_BLOCK_HEADER_SIZE
}
/// Rust implementation of the per-block loop from
/// `ZSTD_compressSequences_internal()`.
///
/// Public initialization, frame-header/checksum ordering, and validation are
/// driven by the Rust API orchestrator below. The projected state keeps the
/// ABI explicit while allowing this loop to reuse the existing Rust
/// sequence-transfer, entropy, and block-serialization leaves.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressSequencesInternal(
state: *const ZSTD_rust_sequenceCompressionState,
dst: *mut c_void,
dst_capacity: usize,
in_seqs: *const ZSTD_Sequence,
in_seqs_size: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.seq_store.is_null()
|| state.prev_c_block.is_null()
|| state.next_c_block.is_null()
|| state.is_first_block.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*state.prev_c_block).is_null() || (*state.next_c_block).is_null() } {
return ERROR(ZstdErrorCode::Generic);
}
let mut c_size = 0usize;
let mut remaining = src_size;
let mut seq_pos = ZSTD_SequencePosition::default();
let mut ip = src.cast::<u8>();
let mut op = dst.cast::<u8>();
let mut dst_capacity = dst_capacity;
let explicit_delimiters =
ZSTD_rust_selectSequenceCopier(state.block_delimiters) == ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS;
/* Special case: empty frame. */
if remaining == 0 {
return unsafe { write_empty_sequence_block(op, dst_capacity) };
}
while remaining != 0 {
let mut block_size = unsafe {
ZSTD_rust_determineBlockSize(
state.block_delimiters,
state.block_size_max,
remaining,
in_seqs,
in_seqs_size,
seq_pos.idx,
)
};
let last_block = u32::from(block_size == remaining);
if ERR_isError(block_size) {
return block_size;
}
debug_assert!(block_size <= remaining);
unsafe { ZSTD_rust_resetSeqStore(state.seq_store) };
let prev_block = unsafe { *state.prev_c_block };
let next_block = unsafe { *state.next_c_block };
block_size = if explicit_delimiters {
unsafe {
ZSTD_rust_transferSequencesWBlockDelim(
state.seq_store,
&mut seq_pos,
in_seqs,
in_seqs_size,
ip,
block_size,
state.search_for_external_repcodes,
(*prev_block).rep.as_ptr(),
(*next_block).rep.as_mut_ptr(),
state.dict_size,
state.validate_sequences,
state.min_match,
state.window_log,
state.use_sequence_producer,
)
}
} else {
unsafe {
ZSTD_rust_transferSequencesNoDelim(
state.seq_store,
&mut seq_pos,
in_seqs,
in_seqs_size,
ip,
block_size,
(*prev_block).rep.as_ptr(),
(*next_block).rep.as_mut_ptr(),
state.dict_size,
state.validate_sequences,
state.min_match,
state.window_log,
state.use_sequence_producer,
)
}
};
if ERR_isError(block_size) {
return block_size;
}
/* If blocks are too small, emit as a nocompress block. */
if block_size < MIN_COMPRESSIBLE_BLOCK_SIZE {
let c_block_size = unsafe {
ZSTD_rust_noCompressBlock(
op.cast(),
dst_capacity,
ip.cast(),
block_size,
last_block,
)
};
if ERR_isError(c_block_size) {
return c_block_size;
}
c_size = c_size.wrapping_add(c_block_size);
unsafe {
ip = ip.add(block_size);
op = op.add(c_block_size);
}
remaining -= block_size;
dst_capacity -= c_block_size;
continue;
}
if dst_capacity < ZSTD_BLOCK_HEADER_SIZE {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let compressed_size = unsafe {
ZSTD_rust_entropyCompressSeqStore(
state.seq_store,
ptr::addr_of!((*prev_block).entropy),
ptr::addr_of_mut!((*next_block).entropy),
state.strategy,
state.disable_literal_compression,
op.add(ZSTD_BLOCK_HEADER_SIZE).cast(),
dst_capacity - ZSTD_BLOCK_HEADER_SIZE,
block_size,
state.tmp_workspace,
state.tmp_wksp_size,
state.bmi2,
)
};
if ERR_isError(compressed_size) {
return compressed_size;
}
let is_first_block = unsafe { *state.is_first_block };
let (maybe_rle, is_rle) = if is_first_block == 0 {
let maybe_rle = unsafe { ZSTD_rust_maybeRLE(state.seq_store) };
let is_rle = if maybe_rle != 0 {
unsafe { ZSTD_rust_isRLE(ip, block_size) }
} else {
0
};
(maybe_rle, is_rle)
} else {
(0, 0)
};
let action = sequence_block_action(is_first_block, maybe_rle, is_rle, compressed_size);
let c_block_size = match action {
SequenceBlockAction::Raw => unsafe {
/* ZSTD_noCompressBlock writes the block header as well. */
ZSTD_rust_noCompressBlock(
op.cast(),
dst_capacity,
ip.cast(),
block_size,
last_block,
)
},
SequenceBlockAction::Rle => unsafe {
ZSTD_rust_rleCompressBlock(op.cast(), dst_capacity, *ip, block_size, last_block)
},
SequenceBlockAction::Compressed => {
/* Error checking and repcodes update. */
unsafe {
ZSTD_rust_confirmRepcodesAndEntropyTables(
state.prev_c_block,
state.next_c_block,
);
}
let prev_block = unsafe { *state.prev_c_block };
if unsafe { (*prev_block).entropy.fse.offcode_repeatMode } == 2 {
unsafe { (*prev_block).entropy.fse.offcode_repeatMode = 1 };
}
unsafe {
ZSTD_rust_writeBlockHeader(op.cast(), compressed_size, block_size, last_block)
};
ZSTD_BLOCK_HEADER_SIZE + compressed_size
}
};
if ERR_isError(c_block_size) {
return c_block_size;
}
c_size = c_size.wrapping_add(c_block_size);
if last_block != 0 {
break;
}
unsafe {
ip = ip.add(block_size);
op = op.add(c_block_size);
*state.is_first_block = 0;
}
remaining -= block_size;
dst_capacity -= c_block_size;
}
c_size
}
/// Compress one frame using the already migrated block leaves.
///
/// This path owns the match tables and carries their history across the
/// 128 KiB block boundaries, while keeping the private C
/// `ZSTD_MatchState_t` window out of the Rust ABI.
/// Writes the three-byte raw header used by the external
/// sequences-and-literals empty-frame special case.
#[inline]
unsafe fn write_empty_sequence_literals_block(dst: *mut u8, dst_capacity: usize) -> usize {
if dst_capacity < ZSTD_BLOCK_HEADER_SIZE {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let header = [1u8, 0, 0];
unsafe { ptr::copy_nonoverlapping(header.as_ptr(), dst, header.len()) };
ZSTD_BLOCK_HEADER_SIZE
}
/// Rust implementation of the block loop from
/// ZSTD_compressSequencesAndLiterals_internal.
///
/// The C wrapper retains the private CCtx-dependent state and sequence
/// conversion callback. Rust owns the public validation/ordering layer, the
/// external literal cursor, per-block entropy pass, compressed-block framing,
/// and completion checks.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressSequencesAndLiteralsInternal(
state: *const ZSTD_rust_sequenceLiteralsState,
dst: *mut c_void,
dst_capacity: usize,
in_seqs: *const ZSTD_Sequence,
nb_sequences: usize,
literals: *const c_void,
lit_size: usize,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.seq_store.is_null()
|| state.prev_c_block.is_null()
|| state.next_c_block.is_null()
|| state.is_first_block.is_null()
|| unsafe { (*state.prev_c_block).is_null() || (*state.next_c_block).is_null() }
{
return ERROR(ZstdErrorCode::Generic);
}
if nb_sequences == 0 {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
let mut remaining = src_size;
let mut nb_sequences = nb_sequences;
let mut c_size = 0usize;
let mut op = dst.cast::<u8>();
let mut dst_capacity = dst_capacity;
let mut in_seqs = in_seqs;
let mut literals = literals;
let mut lit_size = lit_size;
/* Special case: empty frame. Keep the C ordering: this header is emitted
* before the ordinary sequence loop. */
if nb_sequences == 1 && unsafe { (*in_seqs).litLength } == 0 {
let written = unsafe { write_empty_sequence_literals_block(op, dst_capacity) };
if ERR_isError(written) {
return written;
}
unsafe {
op = op.add(written);
}
dst_capacity -= written;
c_size += written;
}
while nb_sequences != 0 {
let block = unsafe { ZSTD_rust_get1BlockSummary(in_seqs, nb_sequences) };
let last_block = c_uint::from(block.nbSequences == nb_sequences);
if ERR_isError(block.nbSequences) {
return block.nbSequences;
}
debug_assert!(block.nbSequences <= nb_sequences);
if block.litSize > lit_size {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
unsafe { ZSTD_rust_resetSeqStore(state.seq_store) };
let conversion_status = unsafe {
(state.convert_block_sequences)(
state.callback_context,
in_seqs,
block.nbSequences,
state.repcode_resolution,
)
};
if ERR_isError(conversion_status) {
return conversion_status;
}
unsafe {
in_seqs = in_seqs.add(block.nbSequences);
}
nb_sequences -= block.nbSequences;
remaining = remaining.wrapping_sub(block.blockSize);
if dst_capacity < ZSTD_BLOCK_HEADER_SIZE {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let prev_block = unsafe { *state.prev_c_block };
let next_block = unsafe { *state.next_c_block };
if prev_block.is_null() || next_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let compressed_size = unsafe {
ZSTD_rust_entropyCompressSeqStore_internal(
op.add(ZSTD_BLOCK_HEADER_SIZE).cast(),
dst_capacity - ZSTD_BLOCK_HEADER_SIZE,
literals,
block.litSize,
state.seq_store,
ptr::addr_of!((*prev_block).entropy),
ptr::addr_of_mut!((*next_block).entropy),
state.strategy,
state.disable_literal_compression,
state.tmp_workspace,
state.tmp_wksp_size,
state.bmi2,
)
};
if ERR_isError(compressed_size) {
return compressed_size;
}
let compressed_size = if compressed_size > state.block_size_max {
0
} else {
compressed_size
};
unsafe {
literals = literals.cast::<u8>().add(block.litSize).cast();
}
lit_size = lit_size.wrapping_sub(block.litSize);
if compressed_size == 0 {
return ERROR(ZstdErrorCode::CannotProduceUncompressedBlock);
}
debug_assert!(compressed_size > 1);
unsafe {
ZSTD_rust_confirmRepcodesAndEntropyTables(state.prev_c_block, state.next_c_block);
}
let prev_block = unsafe { *state.prev_c_block };
if prev_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { (*prev_block).entropy.fse.offcode_repeatMode } == 2 {
unsafe { (*prev_block).entropy.fse.offcode_repeatMode = 1 };
}
unsafe {
ZSTD_rust_writeBlockHeader(op.cast(), compressed_size, block.blockSize, last_block);
}
let c_block_size = ZSTD_BLOCK_HEADER_SIZE + compressed_size;
c_size = c_size.wrapping_add(c_block_size);
unsafe {
op = op.add(c_block_size);
*state.is_first_block = 0;
}
dst_capacity = dst_capacity.wrapping_sub(c_block_size);
if last_block != 0 {
break;
}
}
if lit_size != 0 || remaining != 0 {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
c_size
}
unsafe fn sequence_api_prepare(
state: &mut ZSTD_rust_sequenceApiState,
pledged_src_size: usize,
with_literals: bool,
lit_size: usize,
lit_capacity: usize,
) -> Result<SequenceApiPlan, usize> {
if with_literals {
let capacity_result = sequence_api_validate_literal_capacity(lit_size, lit_capacity);
if ERR_isError(capacity_result) {
return Err(capacity_result);
}
}
if state.callback_context.is_null()
|| state.sequence_state.is_null()
|| state.sequence_literals_state.is_null()
|| state.init as usize == 0
|| state.checksum_state.is_null()
{
return Err(ERROR(ZstdErrorCode::Generic));
}
let init_result = unsafe {
(state.init)(
state.callback_context,
pledged_src_size,
state as *mut ZSTD_rust_sequenceApiState,
)
};
if ERR_isError(init_result) {
return Err(init_result);
}
sequence_api_plan(
with_literals,
state.block_delimiters,
state.validate_sequences,
state.checksum_flag,
)
}
unsafe fn sequence_api_write_frame_header(
state: &ZSTD_rust_sequenceApiState,
op: &mut *mut u8,
dst_capacity: &mut usize,
c_size: &mut usize,
pledged_src_size: usize,
) -> usize {
let frame_header_size = unsafe {
ZSTD_rust_writeFrameHeader(
(*op).cast(),
*dst_capacity,
state.no_dict_id_flag,
state.checksum_flag,
state.content_size_flag,
state.format,
state.window_log,
pledged_src_size as u64,
state.dict_id,
)
};
if ERR_isError(frame_header_size) {
return frame_header_size;
}
if frame_header_size > *dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
unsafe {
*op = (*op).add(frame_header_size);
}
*dst_capacity -= frame_header_size;
*c_size = c_size.wrapping_add(frame_header_size);
0
}
unsafe fn sequence_api_append_frame_checksum(
state: &ZSTD_rust_sequenceApiState,
plan: SequenceApiPlan,
op: *mut u8,
dst_capacity: usize,
c_size: &mut usize,
) -> usize {
if !plan.append_frame_checksum {
return 0;
}
/* Keep the original ordering: digest the checksum before checking the
* remaining destination capacity. */
let checksum = unsafe { XXH64_digest(state.checksum_state) as c_uint };
if dst_capacity < 4 {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
unsafe { MEM_writeLE32(op.cast(), checksum) };
*c_size = c_size.wrapping_add(4);
0
}
/// Rust-owned orchestration for `ZSTD_compressSequences`.
///
/// The C wrapper provides the post-initialization block-state projection and
/// callback for private CCtx initialization; frame-header fields and the
/// checksum state are projected directly. Rust preserves the public ordering:
/// initialize, write the frame header, update the input checksum, emit blocks,
/// then append the frame checksum.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressSequences(
state: *mut ZSTD_rust_sequenceApiState,
dst: *mut c_void,
dst_capacity: usize,
in_seqs: *const ZSTD_Sequence,
in_seqs_size: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &mut *state };
let plan = match unsafe { sequence_api_prepare(state, src_size, false, 0, 0) } {
Ok(plan) => plan,
Err(result) => return result,
};
let mut op = dst.cast::<u8>();
let mut dst_capacity = dst_capacity;
let mut c_size = 0usize;
let header_result = unsafe {
sequence_api_write_frame_header(state, &mut op, &mut dst_capacity, &mut c_size, src_size)
};
if ERR_isError(header_result) {
return header_result;
}
if plan.update_input_checksum && src_size != 0 {
unsafe {
let _ = XXH64_update(state.checksum_state, src, src_size);
}
}
let block_size = unsafe {
ZSTD_rust_compressSequencesInternal(
&*state.sequence_state,
op.cast(),
dst_capacity,
in_seqs,
in_seqs_size,
src,
src_size,
)
};
if ERR_isError(block_size) {
return block_size;
}
if block_size > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
unsafe {
op = op.add(block_size);
}
dst_capacity -= block_size;
c_size = c_size.wrapping_add(block_size);
let checksum_result =
unsafe { sequence_api_append_frame_checksum(state, plan, op, dst_capacity, &mut c_size) };
if ERR_isError(checksum_result) {
return checksum_result;
}
c_size
}
/// Rust-owned orchestration for `ZSTD_compressSequencesAndLiterals`.
///
/// The literal-capacity check intentionally precedes CCtx initialization, and
/// the post-initialization incompatibility checks retain their original
/// precedence. The existing Rust block loop remains responsible for literal
/// accounting, sequence conversion, and block codec operations.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressSequencesAndLiterals(
state: *mut ZSTD_rust_sequenceApiState,
dst: *mut c_void,
dst_capacity: usize,
in_seqs: *const ZSTD_Sequence,
in_seqs_size: usize,
literals: *const c_void,
lit_size: usize,
lit_capacity: usize,
decompressed_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &mut *state };
let plan = match unsafe {
sequence_api_prepare(state, decompressed_size, true, lit_size, lit_capacity)
} {
Ok(plan) => plan,
Err(result) => return result,
};
let mut op = dst.cast::<u8>();
let mut dst_capacity = dst_capacity;
let mut c_size = 0usize;
let header_result = unsafe {
sequence_api_write_frame_header(
state,
&mut op,
&mut dst_capacity,
&mut c_size,
decompressed_size,
)
};
if ERR_isError(header_result) {
return header_result;
}
let block_size = unsafe {
ZSTD_rust_compressSequencesAndLiteralsInternal(
&*state.sequence_literals_state,
op.cast(),
dst_capacity,
in_seqs,
in_seqs_size,
literals,
lit_size,
decompressed_size,
)
};
if ERR_isError(block_size) {
return block_size;
}
if block_size > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
dst_capacity -= block_size;
c_size = c_size.wrapping_add(block_size);
let checksum_result =
unsafe { sequence_api_append_frame_checksum(state, plan, op, dst_capacity, &mut c_size) };
if ERR_isError(checksum_result) {
return checksum_result;
}
c_size
}
unsafe fn compress_frame(
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
compression_level: c_int,
) -> usize {
if dst.is_null() {
return ERROR(if dst_capacity == 0 {
ZstdErrorCode::DstSizeTooSmall
} else {
ZstdErrorCode::DstBufferNull
});
}
if src_size != 0 && src.is_null() {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if src_size as u64 == ZSTD_CONTENTSIZE_UNKNOWN {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let mut cparams = ZSTD_rust_params_selectCParams(
compression_level,
src_size as u64,
0,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
);
cparams = ZSTD_rust_params_adjustCParams(
cparams,
src_size as u64,
0,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
ZSTD_RUST_PS_DISABLE,
);
let header_size = unsafe {
ZSTD_rust_writeFrameHeader(
dst,
dst_capacity,
0, /* noDictIDFlag */
0, /* checksumFlag */
1, /* contentSizeFlag */
0, /* zstd frame */
cparams.windowLog,
src_size as u64,
0,
)
};
if ERR_isError(header_size) {
return header_size;
}
if src_size == 0 {
let empty_block = unsafe {
ZSTD_writeLastEmptyBlock(
dst.cast::<u8>().add(header_size).cast(),
dst_capacity - header_size,
)
};
return if ERR_isError(empty_block) {
empty_block
} else {
header_size + empty_block
};
}
let first_block_size = src_size.min(ZSTD_BLOCKSIZE_MAX);
let matcher_hash_log = cparams.hashLog;
let matcher_chain_log = cparams.chainLog.min(ceil_log2(first_block_size).max(6));
let hash_size = match checked_table_size(matcher_hash_log) {
Some(size) => size,
None => return ERROR(ZstdErrorCode::MemoryAllocation),
};
let chain_size = match checked_table_size(matcher_chain_log) {
Some(size) => size,
None => return ERROR(ZstdErrorCode::MemoryAllocation),
};
let max_nb_seq =
ZSTD_rust_params_maxNbSeq(ZSTD_BLOCKSIZE_MAX, cparams.minMatch, 0).saturating_add(1);
let disable_literal_compression =
c_int::from(cparams.strategy == ZSTD_FAST && cparams.targetLength > 0);
let mut sequences = vec![SeqDef::default(); max_nb_seq];
let mut literals = vec![0u8; ZSTD_BLOCKSIZE_MAX];
let mut ll_codes = vec![0u8; max_nb_seq];
let mut ml_codes = vec![0u8; max_nb_seq];
let mut of_codes = vec![0u8; max_nb_seq];
let mut hash_table = vec![0u32; hash_size];
let mut chain_table = vec![0u32; chain_size];
let mut workspace = vec![0u64; TMP_WORKSPACE_SIZE / size_of::<u64>()];
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
prev_block.rep = [1, 4, 8];
next_block.rep = prev_block.rep;
let seq_store = &mut SeqStore_t {
sequencesStart: sequences.as_mut_ptr(),
sequences: sequences.as_mut_ptr(),
litStart: literals.as_mut_ptr(),
lit: literals.as_mut_ptr(),
llCode: ll_codes.as_mut_ptr(),
mlCode: ml_codes.as_mut_ptr(),
ofCode: of_codes.as_mut_ptr(),
maxNbSeq: max_nb_seq,
maxNbLit: ZSTD_BLOCKSIZE_MAX,
longLengthType: 0,
longLengthPos: 0,
};
let source = src.cast::<u8>();
let output = dst.cast::<u8>();
let matcher_base = source.wrapping_sub(ZSTD_WINDOW_START_INDEX as usize);
let mut input_offset = 0usize;
let mut output_offset = header_size;
while input_offset < src_size {
let block_size = (src_size - input_offset).min(ZSTD_BLOCKSIZE_MAX);
let block_src = unsafe { source.add(input_offset) };
let block_end = unsafe { block_src.add(block_size) };
let last_block = u32::from(input_offset + block_size == src_size);
if block_size < MIN_COMPRESSIBLE_BLOCK_SIZE {
let written = unsafe {
write_raw_block(
output.add(output_offset),
dst_capacity.saturating_sub(output_offset),
block_src,
block_size,
last_block,
)
};
if ERR_isError(written) {
return written;
}
output_offset += written;
input_offset += block_size;
continue;
}
seq_store.sequences = seq_store.sequencesStart;
seq_store.lit = seq_store.litStart;
seq_store.longLengthType = 0;
seq_store.longLengthPos = 0;
next_block.rep = prev_block.rep;
let mut reps = prev_block.rep;
let last_literals = if block_size < 8 {
block_size
} else if cparams.strategy == ZSTD_DFAST {
unsafe {
crate::zstd_double_fast::ZSTD_rust_compressBlock_doubleFast(
hash_table.as_mut_ptr(),
chain_table.as_mut_ptr(),
matcher_base,
ZSTD_WINDOW_START_INDEX,
0,
matcher_hash_log,
matcher_chain_log,
cparams.minMatch,
cparams.windowLog,
(seq_store as *mut SeqStore_t).cast(),
reps.as_mut_ptr(),
block_src.cast(),
block_size,
)
}
} else {
unsafe {
crate::zstd_fast::ZSTD_rust_compressBlock_fast(
hash_table.as_mut_ptr(),
matcher_base,
ZSTD_WINDOW_START_INDEX,
0,
matcher_hash_log,
cparams.minMatch,
cparams.targetLength,
cparams.windowLog,
(seq_store as *mut SeqStore_t).cast(),
reps.as_mut_ptr(),
block_src.cast(),
block_size,
)
}
};
if last_literals > block_size {
return ERROR(ZstdErrorCode::Generic);
}
let last_literal_src = unsafe { block_end.sub(last_literals) };
if last_literals != 0 {
unsafe {
ptr::copy_nonoverlapping(last_literal_src, seq_store.lit, last_literals);
seq_store.lit = seq_store.lit.add(last_literals);
}
}
let remaining_capacity = dst_capacity.saturating_sub(output_offset);
if remaining_capacity < ZSTD_BLOCK_HEADER_SIZE {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let mut compressed_size = unsafe {
ZSTD_rust_entropyCompressSeqStore(
(seq_store as *const SeqStore_t).cast(),
ptr::addr_of!(prev_block.entropy),
ptr::addr_of_mut!(next_block.entropy),
cparams.strategy,
disable_literal_compression,
output.add(output_offset + ZSTD_BLOCK_HEADER_SIZE).cast(),
remaining_capacity - ZSTD_BLOCK_HEADER_SIZE,
block_size,
workspace.as_mut_ptr().cast(),
TMP_WORKSPACE_SIZE,
0, /* BMI2 is optional; portable Rust leaf path */
)
};
if ERR_isError(compressed_size) {
return compressed_size;
}
if input_offset != 0
&& compressed_size < 25
&& unsafe { ZSTD_rust_isRLE(block_src, block_size) } != 0
{
unsafe {
*output.add(output_offset + ZSTD_BLOCK_HEADER_SIZE) = *block_src;
}
compressed_size = 1;
}
let written = if compressed_size == 0 {
unsafe {
write_raw_block(
output.add(output_offset),
remaining_capacity,
block_src,
block_size,
last_block,
)
}
} else {
unsafe {
ZSTD_rust_writeBlockHeader(
output.add(output_offset).cast(),
compressed_size,
block_size,
last_block,
)
};
if compressed_size > 1 {
next_block.rep = reps;
}
compressed_size + ZSTD_BLOCK_HEADER_SIZE
};
if ERR_isError(written) {
return written;
}
output_offset += written;
input_offset += block_size;
if compressed_size > 1 {
std::mem::swap(&mut prev_block, &mut next_block);
}
}
output_offset
}
/// Select the complete-input streaming fast path before invoking the C-owned
/// simple-level predicate. Rust owns the stream-state branch; C keeps the
/// private context and level-selection leaf behind a callback.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_simpleCompressStream2Policy(
projection: *const ZSTD_rust_simpleCompressStream2Projection,
cctx: *const c_void,
src_size: usize,
simple_compress2_level: Option<ZSTD_rust_simpleCompress2LevelFn>,
) -> c_int {
let Some(projection) = (unsafe { projection.as_ref() }) else {
return c_int::MIN;
};
if cctx.is_null()
|| projection.stream_stage != ZSTD_CSTREAM_STAGE_INIT
|| projection.pledged_src_size_plus_one != 0
|| projection.rust_simple_compress2_completed != 0
{
return c_int::MIN;
}
let Some(simple_compress2_level) = simple_compress2_level else {
return c_int::MIN;
};
unsafe { simple_compress2_level(cctx, src_size) }
}
/// Simple one-shot compression entry point.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_compress(
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
compression_level: c_int,
) -> usize {
#[cfg(not(test))]
{
let strategy = ZSTD_rust_compressCCtxStrategy(src_size, compression_level);
if strategy != ZSTD_FAST && strategy != ZSTD_DFAST {
let cctx = unsafe { ZSTD_createCCtx() };
if cctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe {
ZSTD_compress_usingDict(
cctx,
dst,
dst_capacity,
src,
src_size,
ptr::null(),
0,
compression_level,
)
};
unsafe { ZSTD_freeCCtx(cctx) };
return result;
}
}
unsafe { compress_frame(dst, dst_capacity, src, src_size, compression_level) }
}
/// Simple explicit-context compression entry point.
///
/// The public contract deliberately ignores all advanced context parameters.
/// C performs the context reset because the private `ZSTD_CCtx_s` layout is
/// still configuration-dependent. Strategies not yet implemented by the
/// Rust frame compressor use the original C simple API before that reset.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_compressCCtx(
cctx: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
compression_level: c_int,
) -> usize {
if cctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
#[cfg(not(test))]
{
let strategy = ZSTD_rust_compressCCtxStrategy(src_size, compression_level);
if strategy != ZSTD_FAST && strategy != ZSTD_DFAST {
return unsafe {
ZSTD_compress_usingDict(
cctx,
dst,
dst_capacity,
src,
src_size,
ptr::null(),
0,
compression_level,
)
};
}
let reset = unsafe { ZSTD_rust_resetCCtxForSimpleCompression(cctx) };
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(), src_size) };
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) }
}
}
/// Compress a streaming call when the complete input and a full output bound
/// are already available for the ordinary context configuration.
///
/// The C implementation remains the fallback for partial-output streaming,
/// `ZSTD_e_continue`/`ZSTD_e_flush`, dictionaries, and every advanced context
/// configuration. The Rust path resets the C-owned session after emitting a
/// complete frame so the same context can immediately start another frame.
#[cfg(not(test))]
#[no_mangle]
pub unsafe extern "C" fn ZSTD_compressStream2(
cctx: *mut c_void,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
end_op: c_int,
) -> usize {
if cctx.is_null() || output.is_null() || input.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let output_ref = unsafe { &mut *output };
let input_ref = unsafe { &mut *input };
if output_ref.pos > output_ref.size {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
if input_ref.pos > input_ref.size {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let src_size = input_ref.size - input_ref.pos;
if end_op == ZSTD_E_END {
let level = unsafe { ZSTD_rust_simpleCompressStream2Level(cctx.cast_const(), src_size) };
if level != c_int::MIN {
let dst_capacity = output_ref.size - output_ref.pos;
let bound = ZSTD_compressBound(src_size);
if !ERR_isError(bound) && dst_capacity >= bound {
if dst_capacity != 0 && output_ref.dst.is_null() {
return ERROR(ZstdErrorCode::DstBufferNull);
}
if src_size != 0 && input_ref.src.is_null() {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let dst = if output_ref.dst.is_null() {
ptr::null_mut()
} else {
unsafe { output_ref.dst.cast::<u8>().add(output_ref.pos).cast() }
};
let src = if input_ref.src.is_null() {
ptr::null()
} else {
unsafe { input_ref.src.cast::<u8>().add(input_ref.pos).cast() }
};
let result = unsafe { compress_frame(dst, dst_capacity, src, src_size, level) };
if !ERR_isError(result) {
output_ref.pos += result;
input_ref.pos = input_ref.size;
return unsafe { ZSTD_rust_resetCCtxForSimpleCompressionSession(cctx) };
}
}
}
}
unsafe { ZSTD_compressStream2_c(cctx, output, input, end_op) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::ERR_getErrorCode;
use crate::zstd_compress_params_api::{
ZSTD_CCtxParams_getParameter, ZSTD_CCtxParams_setParameter,
};
use std::io::Write;
use std::process::{Command, Stdio};
const ZSTD_GREEDY: c_int = 3;
const ZSTD_LAZY: c_int = 4;
const ZSTD_BTULTRA: c_int = 8;
const ZSTD_BTOPT: c_int = 7;
const ZSTD_BTULTRA2: c_int = 9;
struct EstimateCCtxSizeTestContext {
compression_levels: Vec<c_int>,
source_size_hints: Vec<u64>,
parameter_tiers: Vec<u32>,
}
unsafe extern "C" fn estimate_cctx_size_test_get_c_params(
context: *mut c_void,
compression_level: c_int,
source_size_hint: u64,
) -> ZSTD_compressionParameters {
let context = unsafe { &mut *(context.cast::<EstimateCCtxSizeTestContext>()) };
context.compression_levels.push(compression_level);
context.source_size_hints.push(source_size_hint);
ZSTD_compressionParameters {
windowLog: context.source_size_hints.len() as u32,
..ZSTD_compressionParameters::default()
}
}
unsafe extern "C" fn estimate_cctx_size_test_estimate(
context: *mut c_void,
c_params: ZSTD_compressionParameters,
) -> usize {
let context = unsafe { &mut *(context.cast::<EstimateCCtxSizeTestContext>()) };
context.parameter_tiers.push(c_params.windowLog);
match c_params.windowLog {
1 => 17,
2 => 91,
3 => 23,
4 => 67,
_ => ERROR(ZstdErrorCode::Generic),
}
}
struct GenerateSequencesTestContext {
events: Vec<&'static str>,
target_c_block_size: c_int,
nb_workers: c_int,
allocation_fails: bool,
collector_result: usize,
compress_result: usize,
sequence_count: usize,
}
unsafe extern "C" fn generate_sequences_get_parameter_test(
context: *mut c_void,
parameter: c_int,
value: *mut c_int,
) -> usize {
let context = unsafe { &mut *context.cast::<GenerateSequencesTestContext>() };
let (event, result) = match parameter {
ZSTD_C_TARGET_C_BLOCK_SIZE => ("target", context.target_c_block_size),
ZSTD_C_NB_WORKERS => ("workers", context.nb_workers),
_ => ("other", 0),
};
context.events.push(event);
unsafe { *value = result };
0
}
unsafe extern "C" fn generate_sequences_allocate_test(
context: *mut c_void,
_size: usize,
) -> *mut c_void {
let context = unsafe { &mut *context.cast::<GenerateSequencesTestContext>() };
context.events.push("allocate");
if context.allocation_fails {
ptr::null_mut()
} else {
Box::into_raw(Box::new(0_u8)).cast()
}
}
unsafe extern "C" fn generate_sequences_free_test(context: *mut c_void, pointer: *mut c_void) {
let context = unsafe { &mut *context.cast::<GenerateSequencesTestContext>() };
context.events.push("free");
if !pointer.is_null() {
unsafe { drop(Box::from_raw(pointer.cast::<u8>())) };
}
}
unsafe extern "C" fn generate_sequences_set_collector_test(
context: *mut c_void,
_out_seqs: *mut ZSTD_Sequence,
_out_seqs_size: usize,
) -> usize {
let context = unsafe { &mut *context.cast::<GenerateSequencesTestContext>() };
context.events.push("collector");
context.collector_result
}
unsafe extern "C" fn generate_sequences_compress2_test(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
) -> usize {
let context = unsafe { &mut *context.cast::<GenerateSequencesTestContext>() };
context.events.push("compress2");
context.compress_result
}
unsafe extern "C" fn generate_sequences_get_count_test(context: *mut c_void) -> usize {
let context = unsafe { &mut *context.cast::<GenerateSequencesTestContext>() };
context.events.push("count");
context.sequence_count
}
fn generate_sequences_test_state(
context: &mut GenerateSequencesTestContext,
) -> ZSTD_rust_generateSequencesState {
ZSTD_rust_generateSequencesState {
callback_context: context as *mut GenerateSequencesTestContext as *mut c_void,
get_parameter: generate_sequences_get_parameter_test,
allocate: generate_sequences_allocate_test,
free: generate_sequences_free_test,
set_collector: generate_sequences_set_collector_test,
compress2: generate_sequences_compress2_test,
get_count: generate_sequences_get_count_test,
}
}
#[test]
fn generate_sequences_rejects_unsupported_parameters_before_allocation() {
let mut context = GenerateSequencesTestContext {
events: Vec::new(),
target_c_block_size: 1,
nb_workers: 0,
allocation_fails: false,
collector_result: 0,
compress_result: 0,
sequence_count: 0,
};
let state = generate_sequences_test_state(&mut context);
let result =
unsafe { ZSTD_rust_generateSequences(&state, ptr::null_mut(), 0, ptr::null(), 0) };
assert_eq!(
ERR_getErrorCode(result),
ZstdErrorCode::ParameterUnsupported as i32
);
assert_eq!(context.events, ["target"]);
}
#[test]
fn generate_sequences_frees_temporary_output_on_setup_failure() {
let mut context = GenerateSequencesTestContext {
events: Vec::new(),
target_c_block_size: 0,
nb_workers: 0,
allocation_fails: false,
collector_result: ERROR(ZstdErrorCode::Generic),
compress_result: 0,
sequence_count: 0,
};
let state = generate_sequences_test_state(&mut context);
let result =
unsafe { ZSTD_rust_generateSequences(&state, ptr::null_mut(), 0, ptr::null(), 0) };
assert_eq!(ERR_getErrorCode(result), ZstdErrorCode::Generic as i32);
assert_eq!(
context.events,
["target", "workers", "allocate", "collector", "free"]
);
}
#[test]
fn generate_sequences_runs_callbacks_in_order_and_returns_count() {
let mut context = GenerateSequencesTestContext {
events: Vec::new(),
target_c_block_size: 0,
nb_workers: 0,
allocation_fails: false,
collector_result: 0,
compress_result: 0,
sequence_count: 2,
};
let state = generate_sequences_test_state(&mut context);
let result =
unsafe { ZSTD_rust_generateSequences(&state, ptr::null_mut(), 0, ptr::null(), 0) };
assert_eq!(result, 2);
assert_eq!(
context.events,
[
"target",
"workers",
"allocate",
"collector",
"compress2",
"free",
"count"
]
);
}
unsafe extern "C" fn select_block_compressor_test_default(
_match_state: *mut c_void,
_seq_store: *mut SeqStore_t,
_rep: *mut u32,
_src: *const c_void,
_src_size: usize,
) -> usize {
7
}
unsafe extern "C" fn select_block_compressor_test_ordinary(
_match_state: *mut c_void,
_seq_store: *mut SeqStore_t,
_rep: *mut u32,
_src: *const c_void,
_src_size: usize,
) -> usize {
41
}
unsafe extern "C" fn select_block_compressor_test_row(
_match_state: *mut c_void,
_seq_store: *mut SeqStore_t,
_rep: *mut u32,
_src: *const c_void,
_src_size: usize,
) -> usize {
43
}
fn select_block_compressor_test_state(
block_compressors: &[Option<BlockCompressorFn>],
row_block_compressors: &[Option<BlockCompressorFn>],
selected_compressor: &mut Option<BlockCompressorFn>,
) -> ZSTD_rust_selectBlockCompressorState {
ZSTD_rust_selectBlockCompressorState {
block_compressors: block_compressors.as_ptr(),
row_block_compressors: row_block_compressors.as_ptr(),
selected_compressor,
}
}
#[test]
fn select_block_compressor_dispatches_ordinary_and_row_tables() {
let default = Some(select_block_compressor_test_default as BlockCompressorFn);
let mut block_compressors = [default; 4 * ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH];
let mut row_block_compressors = [default; 4 * ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH];
block_compressors[ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH + 2] =
Some(select_block_compressor_test_ordinary as BlockCompressorFn);
row_block_compressors[3 * ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH] =
Some(select_block_compressor_test_row as BlockCompressorFn);
let mut selected_compressor = None;
let state = select_block_compressor_test_state(
&block_compressors,
&row_block_compressors,
&mut selected_compressor,
);
assert_eq!(
unsafe { ZSTD_rust_selectBlockCompressor(&state, ZSTD_DFAST, ZSTD_RUST_PS_DISABLE, 1) },
0
);
let ordinary = selected_compressor.expect("ordinary compressor should be selected");
assert_eq!(
unsafe {
ordinary(
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null(),
0,
)
},
41
);
assert_eq!(
unsafe { ZSTD_rust_selectBlockCompressor(&state, ZSTD_GREEDY, ZSTD_RUST_PS_ENABLE, 3) },
0
);
let row = selected_compressor.expect("row compressor should be selected");
assert_eq!(
unsafe {
row(
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null(),
0,
)
},
43
);
}
#[test]
fn select_block_compressor_rejects_an_excluded_dispatch_leaf() {
let default = Some(select_block_compressor_test_default as BlockCompressorFn);
let mut block_compressors = [default; 4 * ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH];
let row_block_compressors = [default; 4 * ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH];
block_compressors[1] = None;
let mut selected_compressor = None;
let state = select_block_compressor_test_state(
&block_compressors,
&row_block_compressors,
&mut selected_compressor,
);
let result =
unsafe { ZSTD_rust_selectBlockCompressor(&state, ZSTD_FAST, ZSTD_RUST_PS_DISABLE, 0) };
assert!(ERR_isError(result));
assert!(selected_compressor.is_none());
}
#[derive(Default)]
struct BuildSeqStoreSelectProbe {
events: Vec<&'static str>,
external_result: usize,
ldm_result: usize,
producer_result: usize,
producer_complete: c_int,
producer_allow_fallback: c_int,
internal_result: usize,
}
unsafe fn build_seq_store_select_probe(
context: *mut c_void,
) -> &'static mut BuildSeqStoreSelectProbe {
unsafe { &mut *context.cast::<BuildSeqStoreSelectProbe>() }
}
unsafe extern "C" fn build_seq_store_test_skip(_context: *mut c_void, _src_size: usize) {}
unsafe extern "C" fn build_seq_store_test_prepare(
_context: *mut c_void,
_src: *const c_void,
_src_size: usize,
) {
}
unsafe extern "C" fn build_seq_store_test_external(
context: *mut c_void,
_seq_store: *mut SeqStore_t,
_next_rep: *mut u32,
_src: *const c_void,
_src_size: usize,
) -> usize {
let probe = unsafe { build_seq_store_select_probe(context) };
probe.events.push("external");
probe.external_result
}
unsafe extern "C" fn build_seq_store_test_ldm(
context: *mut c_void,
_seq_store: *mut SeqStore_t,
_next_rep: *mut u32,
_src: *const c_void,
_src_size: usize,
) -> usize {
let probe = unsafe { build_seq_store_select_probe(context) };
probe.events.push("ldm");
probe.ldm_result
}
unsafe extern "C" fn build_seq_store_test_producer(
context: *mut c_void,
_src: *const c_void,
_src_size: usize,
seq_store_complete: *mut c_int,
allow_fallback: *mut c_int,
) -> usize {
let probe = unsafe { build_seq_store_select_probe(context) };
probe.events.push("producer");
unsafe {
*seq_store_complete = probe.producer_complete;
*allow_fallback = probe.producer_allow_fallback;
}
probe.producer_result
}
unsafe extern "C" fn build_seq_store_test_internal(
context: *mut c_void,
_seq_store: *mut SeqStore_t,
_next_rep: *mut u32,
_src: *const c_void,
_src_size: usize,
) -> usize {
let probe = unsafe { build_seq_store_select_probe(context) };
probe.events.push("internal");
probe.internal_result
}
unsafe extern "C" fn build_seq_store_test_clear(context: *mut c_void) {
let probe = unsafe { build_seq_store_select_probe(context) };
probe.events.push("clear");
}
fn build_seq_store_select_test_state(
probe: &mut BuildSeqStoreSelectProbe,
has_external_sequences: c_int,
ldm_enabled: c_int,
has_external_sequence_producer: c_int,
enable_match_finder_fallback: c_int,
) -> ZSTD_rust_buildSeqStoreState {
ZSTD_rust_buildSeqStoreState {
seq_store: ptr::null_mut(),
prev_c_block: ptr::null_mut(),
next_c_block: ptr::null_mut(),
callback_context: (probe as *mut BuildSeqStoreSelectProbe).cast(),
min_match: 3,
validate_seq_store: 0,
skip_small_block: build_seq_store_test_skip,
prepare_match_state: build_seq_store_test_prepare,
has_external_sequences,
ldm_enabled,
has_external_sequence_producer,
enable_match_finder_fallback,
compress_external_sequences: build_seq_store_test_external,
compress_ldm: build_seq_store_test_ldm,
try_external_sequence_producer: build_seq_store_test_producer,
compress_internal: build_seq_store_test_internal,
clear_ldm_seq_store: build_seq_store_test_clear,
}
}
fn run_build_seq_store_select_test(state: &ZSTD_rust_buildSeqStoreState) -> (usize, c_int) {
let mut seq_store = unsafe { MaybeUninit::<SeqStore_t>::zeroed().assume_init() };
let mut next_rep = [0u32; ZSTD_REP_NUM];
let source = [0u8; 1];
let mut seq_store_complete = 99;
let result = unsafe {
build_seq_store_select_sequences_body_with(
state,
&mut seq_store,
next_rep.as_mut_ptr(),
source.as_ptr().cast(),
source.len(),
&mut seq_store_complete,
)
};
(result, seq_store_complete)
}
#[test]
fn build_seq_store_selects_preloaded_external_sequences_first() {
let mut probe = BuildSeqStoreSelectProbe {
external_result: 11,
ldm_result: 12,
internal_result: 13,
..Default::default()
};
let state = build_seq_store_select_test_state(&mut probe, 1, 1, 0, 0);
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
assert_eq!(result, probe.external_result);
assert_eq!(seq_store_complete, 0);
assert_eq!(probe.events, ["external"]);
}
#[test]
fn build_seq_store_uses_ldm_when_no_external_sequences_are_pending() {
let mut probe = BuildSeqStoreSelectProbe {
ldm_result: 19,
internal_result: 23,
..Default::default()
};
let state = build_seq_store_select_test_state(&mut probe, 0, 1, 0, 0);
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
assert_eq!(result, probe.ldm_result);
assert_eq!(seq_store_complete, 0);
assert_eq!(probe.events, ["ldm"]);
}
#[test]
fn build_seq_store_rejects_external_producer_with_preloaded_sequences_or_ldm() {
for (has_external_sequences, ldm_enabled) in [(1, 0), (0, 1)] {
let mut probe = BuildSeqStoreSelectProbe::default();
let state = build_seq_store_select_test_state(
&mut probe,
has_external_sequences,
ldm_enabled,
1,
1,
);
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
assert_eq!(
result,
ERROR(ZstdErrorCode::ParameterCombinationUnsupported)
);
assert_eq!(seq_store_complete, 0);
assert!(probe.events.is_empty());
}
}
#[test]
fn build_seq_store_clears_ldm_after_external_producer_completion() {
let mut probe = BuildSeqStoreSelectProbe {
producer_complete: 1,
producer_allow_fallback: 1,
producer_result: 0,
..Default::default()
};
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 1);
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
assert_eq!(result, 0);
assert_eq!(seq_store_complete, 1);
assert_eq!(probe.events, ["producer", "clear"]);
}
#[test]
fn build_seq_store_does_not_fallback_when_producer_disallows_it() {
let producer_result = ERROR(ZstdErrorCode::SequenceProducerFailed);
let mut probe = BuildSeqStoreSelectProbe {
producer_result,
producer_allow_fallback: 1,
internal_result: 17,
..Default::default()
};
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 0);
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
assert_eq!(result, producer_result);
assert_eq!(seq_store_complete, 0);
assert_eq!(probe.events, ["producer"]);
}
#[test]
fn build_seq_store_falls_back_after_external_producer_error() {
let producer_result = ERROR(ZstdErrorCode::SequenceProducerFailed);
let mut probe = BuildSeqStoreSelectProbe {
producer_result,
producer_allow_fallback: 1,
internal_result: 23,
..Default::default()
};
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 1);
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
assert_eq!(result, probe.internal_result);
assert_eq!(seq_store_complete, 0);
assert_eq!(probe.events, ["producer", "clear", "internal"]);
}
#[test]
fn build_seq_store_clears_ldm_before_internal_compression() {
let mut probe = BuildSeqStoreSelectProbe {
internal_result: 29,
..Default::default()
};
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 0, 0);
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
assert_eq!(result, probe.internal_result);
assert_eq!(seq_store_complete, 0);
assert_eq!(probe.events, ["clear", "internal"]);
}
#[derive(Default)]
struct ResetMatchStateTestContext {
events: Vec<c_int>,
reserves: Vec<(c_int, usize)>,
pointers: Vec<(c_int, *mut c_void)>,
zeroed: Vec<(usize, usize)>,
storage: [usize; 32],
fail_after: usize,
}
unsafe fn reset_match_state_test_context(
context: *mut c_void,
) -> &'static mut ResetMatchStateTestContext {
unsafe { &mut *context.cast::<ResetMatchStateTestContext>() }
}
unsafe extern "C" fn reset_match_state_test_callback(context: *mut c_void) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(1);
}
unsafe extern "C" fn reset_match_state_test_mark_dirty(context: *mut c_void) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(2);
}
unsafe extern "C" fn reset_match_state_test_invalidate(context: *mut c_void) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(3);
}
unsafe extern "C" fn reset_match_state_test_clear(context: *mut c_void) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(4);
}
unsafe extern "C" fn reset_match_state_test_clean(context: *mut c_void) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(5);
}
unsafe extern "C" fn reset_match_state_test_advance(context: *mut c_void) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(6);
}
unsafe extern "C" fn reset_match_state_test_reserve(
context: *mut c_void,
reserve_kind: c_int,
size: usize,
) -> *mut c_void {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(100 + reserve_kind);
let index = context.reserves.len();
context.reserves.push((reserve_kind, size));
if context.fail_after != 0 && index + 1 >= context.fail_after {
ptr::null_mut()
} else {
unsafe { context.storage.as_mut_ptr().add(index).cast() }
}
}
unsafe extern "C" fn reset_match_state_test_reserve_failed(context: *mut c_void) -> c_int {
let context = unsafe { reset_match_state_test_context(context) };
c_int::from(context.fail_after != 0 && context.reserves.len() >= context.fail_after)
}
unsafe extern "C" fn reset_match_state_test_set_pointer(
context: *mut c_void,
pointer_kind: c_int,
pointer: *mut c_void,
) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(200 + pointer_kind);
context.pointers.push((pointer_kind, pointer));
}
unsafe extern "C" fn reset_match_state_test_zero(
context: *mut c_void,
pointer: *mut c_void,
size: usize,
) {
let context = unsafe { reset_match_state_test_context(context) };
context.events.push(7);
context.zeroed.push((pointer as usize, size));
}
#[allow(clippy::too_many_arguments)]
fn reset_match_state_test_state(
context: &mut ResetMatchStateTestContext,
c_params: ZSTD_compressionParameters,
dedicated_dict_search: c_int,
use_row_match_finder: c_int,
comp_reset_policy: c_int,
index_reset_policy: c_int,
reset_target: c_int,
hash_log3: &mut c_uint,
row_hash_log: &mut c_uint,
hash_salt: &mut u64,
lazy_skipping: &mut c_int,
c_params_out: &mut ZSTD_compressionParameters,
) -> ZSTD_rust_resetMatchStateState {
ZSTD_rust_resetMatchStateState {
callback_context: (context as *mut ResetMatchStateTestContext).cast(),
c_params,
dedicated_dict_search,
use_row_match_finder,
comp_reset_policy,
index_reset_policy,
reset_target,
hash_log3_max: 17,
lit_freq_size: 256 * size_of::<c_uint>(),
lit_length_freq_size: 36 * size_of::<c_uint>(),
match_length_freq_size: 53 * size_of::<c_uint>(),
off_code_freq_size: 32 * size_of::<c_uint>(),
match_size: 4099 * 2 * size_of::<c_uint>(),
optimal_size: 4099 * 8 * size_of::<c_uint>(),
hash_log3,
row_hash_log,
hash_salt,
lazy_skipping,
c_params_out,
set_pointer: Some(reset_match_state_test_set_pointer),
window_init: Some(reset_match_state_test_callback),
mark_tables_dirty: Some(reset_match_state_test_mark_dirty),
invalidate_match_state: Some(reset_match_state_test_invalidate),
clear_tables: Some(reset_match_state_test_clear),
clean_tables: Some(reset_match_state_test_clean),
advance_hash_salt: Some(reset_match_state_test_advance),
reserve: Some(reset_match_state_test_reserve),
reserve_failed: Some(reset_match_state_test_reserve_failed),
zero: Some(reset_match_state_test_zero),
}
}
fn reset_match_state_test_params(
strategy: c_int,
search_log: u32,
min_match: u32,
) -> ZSTD_compressionParameters {
ZSTD_compressionParameters {
windowLog: 20,
chainLog: 11,
hashLog: 12,
searchLog: search_log,
minMatch: min_match,
targetLength: 16,
strategy,
}
}
#[test]
fn reset_match_state_orders_cctx_opt_allocations() {
let c_params = reset_match_state_test_params(ZSTD_BTOPT, 5, 3);
let mut context = ResetMatchStateTestContext::default();
let mut hash_log3 = 99;
let mut row_hash_log = 99;
let mut hash_salt = 7;
let mut lazy_skipping = 9;
let mut c_params_out = ZSTD_compressionParameters::default();
let state = reset_match_state_test_state(
&mut context,
c_params,
0,
ZSTD_RUST_PS_DISABLE,
0,
1,
1,
&mut hash_log3,
&mut row_hash_log,
&mut hash_salt,
&mut lazy_skipping,
&mut c_params_out,
);
let result = unsafe { ZSTD_rust_resetMatchState(&state) };
assert_eq!(result, 0);
assert_eq!(hash_log3, 17);
assert_eq!(row_hash_log, 99);
assert_eq!(hash_salt, 7);
assert_eq!(lazy_skipping, 0);
assert_eq!(c_params_out, c_params);
assert_eq!(
context.events,
[
1, 2, 3, 4, 100, 200, 100, 201, 100, 202, 5, 102, 204, 102, 205, 102, 206, 102,
207, 102, 208, 102, 209,
]
);
assert_eq!(
context.reserves,
[
(0, (1 << 12) * size_of::<c_uint>()),
(0, (1 << 11) * size_of::<c_uint>()),
(0, (1 << 17) * size_of::<c_uint>()),
(2, 256 * size_of::<c_uint>()),
(2, 36 * size_of::<c_uint>()),
(2, 53 * size_of::<c_uint>()),
(2, 32 * size_of::<c_uint>()),
(2, 4099 * 2 * size_of::<c_uint>()),
(2, 4099 * 8 * size_of::<c_uint>()),
]
);
assert_eq!(context.pointers.len(), 9);
assert!(context.zeroed.is_empty());
}
#[test]
fn reset_match_state_salts_cctx_row_tables_without_zeroing() {
let c_params = reset_match_state_test_params(ZSTD_GREEDY, 6, 4);
let mut context = ResetMatchStateTestContext::default();
let mut hash_log3 = 99;
let mut row_hash_log = 99;
let mut hash_salt = 7;
let mut lazy_skipping = 9;
let mut c_params_out = ZSTD_compressionParameters::default();
let state = reset_match_state_test_state(
&mut context,
c_params,
0,
ZSTD_RUST_PS_ENABLE,
0,
0,
1,
&mut hash_log3,
&mut row_hash_log,
&mut hash_salt,
&mut lazy_skipping,
&mut c_params_out,
);
let result = unsafe { ZSTD_rust_resetMatchState(&state) };
assert_eq!(result, 0);
assert_eq!(hash_log3, 0);
assert_eq!(row_hash_log, 6);
assert_eq!(hash_salt, 7);
assert_eq!(c_params_out, c_params);
assert_eq!(
context.events,
[3, 4, 100, 200, 100, 201, 100, 202, 5, 101, 203, 6]
);
assert_eq!(
context.reserves,
[
(0, (1 << 12) * size_of::<c_uint>()),
(0, 0),
(0, 0),
(1, 1 << 12),
]
);
assert_eq!(context.pointers.len(), 4);
assert!(context.zeroed.is_empty());
}
#[test]
fn reset_match_state_uses_cdict_row_policy_without_opt_storage() {
let c_params = reset_match_state_test_params(ZSTD_GREEDY, 6, 4);
let mut context = ResetMatchStateTestContext::default();
let mut hash_log3 = 99;
let mut row_hash_log = 99;
let mut hash_salt = 7;
let mut lazy_skipping = 9;
let mut c_params_out = ZSTD_compressionParameters::default();
let state = reset_match_state_test_state(
&mut context,
c_params,
1,
ZSTD_RUST_PS_ENABLE,
1,
0,
0,
&mut hash_log3,
&mut row_hash_log,
&mut hash_salt,
&mut lazy_skipping,
&mut c_params_out,
);
let result = unsafe { ZSTD_rust_resetMatchState(&state) };
assert_eq!(result, 0);
assert_eq!(hash_log3, 0);
assert_eq!(row_hash_log, 6);
assert_eq!(hash_salt, 0);
assert_eq!(c_params_out, c_params);
assert_eq!(
context.events,
[3, 4, 100, 200, 100, 201, 100, 202, 102, 203, 7]
);
assert_eq!(
context.reserves,
[
(0, (1 << 12) * size_of::<c_uint>()),
(0, (1 << 11) * size_of::<c_uint>()),
(0, 0),
(2, 1 << 12),
]
);
assert_eq!(context.pointers.len(), 4);
assert_eq!(context.zeroed.len(), 1);
assert_eq!(context.zeroed[0].1, 1 << 12);
}
#[test]
fn reset_match_state_propagates_table_allocation_failure() {
let c_params = reset_match_state_test_params(ZSTD_GREEDY, 4, 4);
let mut context = ResetMatchStateTestContext {
fail_after: 1,
..ResetMatchStateTestContext::default()
};
let mut hash_log3 = 99;
let mut row_hash_log = 99;
let mut hash_salt = 7;
let mut lazy_skipping = 9;
let mut c_params_out = ZSTD_compressionParameters::default();
let state = reset_match_state_test_state(
&mut context,
c_params,
0,
ZSTD_RUST_PS_DISABLE,
0,
1,
1,
&mut hash_log3,
&mut row_hash_log,
&mut hash_salt,
&mut lazy_skipping,
&mut c_params_out,
);
let result = unsafe { ZSTD_rust_resetMatchState(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, [1, 2, 3, 4, 100, 200, 100, 201, 100, 202]);
assert_eq!(context.pointers.len(), 3);
assert_eq!(hash_log3, 0);
assert_eq!(lazy_skipping, 0);
assert_eq!(c_params_out, ZSTD_compressionParameters::default());
}
#[test]
fn reference_external_sequences_resets_store_state() {
let mut source = [0u8; 3];
let sequence = source.as_mut_ptr().cast::<c_void>();
let mut stored_sequence = ptr::null_mut();
let mut pos = 7;
let mut pos_in_sequence = 8;
let mut size = 9;
let mut capacity = 10;
let state = ZSTD_rust_externalSequenceStoreState {
seq: &mut stored_sequence,
pos: &mut pos,
pos_in_sequence: &mut pos_in_sequence,
size: &mut size,
capacity: &mut capacity,
};
unsafe { ZSTD_rust_referenceExternalSequences(&state, sequence, 11) };
assert_eq!(stored_sequence, sequence);
assert_eq!(pos, 0);
assert_eq!(pos_in_sequence, 0);
assert_eq!(size, 11);
assert_eq!(capacity, 11);
}
#[test]
fn reference_external_sequences_rejects_incomplete_state() {
let mut pos = 7;
let mut pos_in_sequence = 8;
let mut size = 9;
let mut capacity = 10;
let state = ZSTD_rust_externalSequenceStoreState {
seq: ptr::null_mut(),
pos: &mut pos,
pos_in_sequence: &mut pos_in_sequence,
size: &mut size,
capacity: &mut capacity,
};
unsafe { ZSTD_rust_referenceExternalSequences(&state, ptr::null_mut(), 11) };
assert_eq!(pos, 7);
assert_eq!(pos_in_sequence, 8);
assert_eq!(size, 9);
assert_eq!(capacity, 10);
}
#[derive(Default)]
struct ExternalSequenceProducerProbe {
events: Vec<&'static str>,
sequences: Vec<ZSTD_Sequence>,
producer_result: usize,
producer_dict: *const c_void,
producer_dict_size: usize,
producer_level: c_int,
producer_window_size: usize,
transfer_sequence_count: usize,
transfer_src_size: usize,
transfer_result: usize,
}
unsafe fn external_sequence_producer_probe(
context: *mut c_void,
) -> &'static mut ExternalSequenceProducerProbe {
unsafe { &mut *context.cast::<ExternalSequenceProducerProbe>() }
}
unsafe extern "C" fn external_sequence_producer_test_producer(
context: *mut c_void,
out_seqs: *mut ZSTD_Sequence,
out_seqs_capacity: usize,
_src: *const c_void,
_src_size: usize,
dict: *const c_void,
dict_size: usize,
compression_level: c_int,
window_size: usize,
) -> usize {
let probe = unsafe { external_sequence_producer_probe(context) };
probe.events.push("produce");
probe.producer_dict = dict;
probe.producer_dict_size = dict_size;
probe.producer_level = compression_level;
probe.producer_window_size = window_size;
assert!(probe.sequences.len() <= out_seqs_capacity);
unsafe {
ptr::copy_nonoverlapping(probe.sequences.as_ptr(), out_seqs, probe.sequences.len());
}
probe.producer_result
}
unsafe extern "C" fn external_sequence_producer_test_transfer(
context: *mut c_void,
_seq_pos: *mut ZSTD_SequencePosition,
_in_seqs: *const ZSTD_Sequence,
in_seqs_size: usize,
_src: *const c_void,
block_size: usize,
) -> usize {
let probe = unsafe { external_sequence_producer_probe(context) };
probe.events.push("transfer");
probe.transfer_sequence_count = in_seqs_size;
probe.transfer_src_size = block_size;
probe.transfer_result
}
fn external_sequence_producer_test_state(
probe: &mut ExternalSequenceProducerProbe,
ext_seq_buf: &mut [ZSTD_Sequence],
ext_seq_buf_capacity: &usize,
src: &[u8],
src_size: &usize,
compression_level: &c_int,
window_size: &usize,
external_seq_count: &mut usize,
seq_store_complete: &mut c_int,
allow_fallback: &mut c_int,
) -> ZSTD_rust_externalSequenceProducerState {
let callback_context = (probe as *mut ExternalSequenceProducerProbe).cast();
ZSTD_rust_externalSequenceProducerState {
callback_context,
producer_state: callback_context,
producer: Some(external_sequence_producer_test_producer),
ext_seq_buf: ext_seq_buf.as_mut_ptr(),
ext_seq_buf_capacity,
src: src.as_ptr().cast(),
src_size,
compression_level,
window_size,
transfer: Some(external_sequence_producer_test_transfer),
external_seq_count,
seq_store_complete,
allow_fallback,
}
}
#[test]
fn external_sequence_producer_success_transfers_after_post_processing() {
let source = [1u8, 2, 3, 4, 5];
let mut probe = ExternalSequenceProducerProbe {
sequences: vec![ZSTD_Sequence {
offset: 0,
litLength: source.len() as u32,
matchLength: 0,
rep: 0,
}],
producer_result: 1,
..Default::default()
};
let mut ext_seq_buf = [ZSTD_Sequence {
offset: 0,
litLength: 0,
matchLength: 0,
rep: 0,
}; 2];
let ext_seq_buf_capacity = ext_seq_buf.len();
let src_size = source.len();
let compression_level = 7;
let window_size = 1 << 20;
let mut external_seq_count = 0;
let mut seq_store_complete = 0;
let mut allow_fallback = 0;
let state = external_sequence_producer_test_state(
&mut probe,
&mut ext_seq_buf,
&ext_seq_buf_capacity,
&source,
&src_size,
&compression_level,
&window_size,
&mut external_seq_count,
&mut seq_store_complete,
&mut allow_fallback,
);
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["produce", "transfer"]);
assert!(probe.producer_dict.is_null());
assert_eq!(probe.producer_dict_size, 0);
assert_eq!(probe.producer_level, compression_level);
assert_eq!(probe.producer_window_size, window_size);
assert_eq!(probe.transfer_sequence_count, 1);
assert_eq!(probe.transfer_src_size, source.len());
assert_eq!(external_seq_count, 1);
assert_eq!(seq_store_complete, 1);
assert_eq!(allow_fallback, 0);
}
#[test]
fn external_sequence_producer_errors_enable_fallback_before_transfer() {
let source = [1u8, 2, 3];
let mut probe = ExternalSequenceProducerProbe {
producer_result: ERROR(ZstdErrorCode::SequenceProducerFailed),
..Default::default()
};
let mut ext_seq_buf = [ZSTD_Sequence {
offset: 0,
litLength: 0,
matchLength: 0,
rep: 0,
}; 2];
let ext_seq_buf_capacity = ext_seq_buf.len();
let src_size = source.len();
let compression_level = 3;
let window_size = 1 << 20;
let mut external_seq_count = 0;
let mut seq_store_complete = 0;
let mut allow_fallback = 0;
let state = external_sequence_producer_test_state(
&mut probe,
&mut ext_seq_buf,
&ext_seq_buf_capacity,
&source,
&src_size,
&compression_level,
&window_size,
&mut external_seq_count,
&mut seq_store_complete,
&mut allow_fallback,
);
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::SequenceProducerFailed));
assert_eq!(probe.events, ["produce"]);
assert_eq!(external_seq_count, result);
assert_eq!(seq_store_complete, 0);
assert_eq!(allow_fallback, 1);
}
#[test]
fn external_sequence_producer_invalid_length_does_not_enable_fallback() {
let source = [1u8, 2, 3];
let mut probe = ExternalSequenceProducerProbe {
sequences: vec![ZSTD_Sequence {
offset: 0,
litLength: (source.len() + 1) as u32,
matchLength: 0,
rep: 0,
}],
producer_result: 1,
..Default::default()
};
let mut ext_seq_buf = [ZSTD_Sequence {
offset: 0,
litLength: 0,
matchLength: 0,
rep: 0,
}; 2];
let ext_seq_buf_capacity = ext_seq_buf.len();
let src_size = source.len();
let compression_level = 3;
let window_size = 1 << 20;
let mut external_seq_count = 0;
let mut seq_store_complete = 0;
let mut allow_fallback = 0;
let state = external_sequence_producer_test_state(
&mut probe,
&mut ext_seq_buf,
&ext_seq_buf_capacity,
&source,
&src_size,
&compression_level,
&window_size,
&mut external_seq_count,
&mut seq_store_complete,
&mut allow_fallback,
);
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
assert_eq!(probe.events, ["produce"]);
assert_eq!(seq_store_complete, 0);
assert_eq!(allow_fallback, 0);
}
#[test]
fn external_sequence_producer_transfer_errors_do_not_enable_fallback() {
let source = [1u8, 2, 3];
let mut probe = ExternalSequenceProducerProbe {
sequences: vec![ZSTD_Sequence {
offset: 0,
litLength: source.len() as u32,
matchLength: 0,
rep: 0,
}],
producer_result: 1,
transfer_result: ERROR(ZstdErrorCode::ExternalSequencesInvalid),
..Default::default()
};
let mut ext_seq_buf = [ZSTD_Sequence {
offset: 0,
litLength: 0,
matchLength: 0,
rep: 0,
}; 2];
let ext_seq_buf_capacity = ext_seq_buf.len();
let src_size = source.len();
let compression_level = 3;
let window_size = 1 << 20;
let mut external_seq_count = 0;
let mut seq_store_complete = 0;
let mut allow_fallback = 0;
let state = external_sequence_producer_test_state(
&mut probe,
&mut ext_seq_buf,
&ext_seq_buf_capacity,
&source,
&src_size,
&compression_level,
&window_size,
&mut external_seq_count,
&mut seq_store_complete,
&mut allow_fallback,
);
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
assert_eq!(probe.events, ["produce", "transfer"]);
assert_eq!(seq_store_complete, 0);
assert_eq!(allow_fallback, 0);
}
#[derive(Default)]
struct Compress2TestContext {
events: Vec<&'static str>,
in_buffer_mode: c_int,
out_buffer_mode: c_int,
reset_result: usize,
stream_result: usize,
output_pos: usize,
input_pos: usize,
}
unsafe fn compress2_test_context(context: *mut c_void) -> &'static mut Compress2TestContext {
unsafe { &mut *context.cast::<Compress2TestContext>() }
}
unsafe extern "C" fn compress2_test_reset(context: *mut c_void) -> usize {
let context = unsafe { compress2_test_context(context) };
context.events.push("reset");
context.reset_result
}
unsafe extern "C" fn compress2_test_set_buffer_modes(
context: *mut c_void,
in_buffer_mode: c_int,
out_buffer_mode: c_int,
) {
let context = unsafe { compress2_test_context(context) };
context.events.push(
if in_buffer_mode == ZSTD_BM_STABLE && out_buffer_mode == ZSTD_BM_STABLE {
"stable"
} else {
"restore"
},
);
context.in_buffer_mode = in_buffer_mode;
context.out_buffer_mode = out_buffer_mode;
}
unsafe extern "C" fn compress2_test_stream_end(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
dst_pos: *mut usize,
_src: *const c_void,
_src_size: usize,
src_pos: *mut usize,
) -> usize {
let context = unsafe { compress2_test_context(context) };
context.events.push("stream-end");
unsafe {
*dst_pos = context.output_pos;
*src_pos = context.input_pos;
}
context.stream_result
}
fn compress2_test_state(
context: &mut Compress2TestContext,
original_in_buffer_mode: c_int,
original_out_buffer_mode: c_int,
) -> ZSTD_rust_compress2State {
ZSTD_rust_compress2State {
callback_context: (context as *mut Compress2TestContext).cast(),
reset_session: compress2_test_reset,
set_buffer_modes: compress2_test_set_buffer_modes,
compress_stream_end: compress2_test_stream_end,
original_in_buffer_mode,
original_out_buffer_mode,
}
}
#[test]
fn compress2_fallback_restores_modes_after_codec_error() {
let mut context = Compress2TestContext {
stream_result: ERROR(ZstdErrorCode::MemoryAllocation),
output_pos: 4,
input_pos: 3,
in_buffer_mode: 7,
out_buffer_mode: 8,
..Compress2TestContext::default()
};
let state = compress2_test_state(&mut context, 7, 8);
let src = [0u8; 3];
let mut dst = [0u8; 4];
let result = unsafe {
ZSTD_rust_compress2(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset", "stable", "stream-end", "restore"]);
assert_eq!((context.in_buffer_mode, context.out_buffer_mode), (7, 8));
}
#[test]
fn compress2_fallback_maps_remaining_output_to_dst_size_too_small() {
let mut context = Compress2TestContext {
stream_result: 1,
output_pos: 4,
input_pos: 3,
in_buffer_mode: 7,
out_buffer_mode: 8,
..Compress2TestContext::default()
};
let state = compress2_test_state(&mut context, 7, 8);
let src = [0u8; 3];
let mut dst = [0u8; 4];
let result = unsafe {
ZSTD_rust_compress2(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::DstSizeTooSmall));
assert_eq!(context.events, ["reset", "stable", "stream-end", "restore"]);
assert_eq!((context.in_buffer_mode, context.out_buffer_mode), (7, 8));
}
#[derive(Default)]
struct CompressEndTestContext {
events: Vec<&'static str>,
continue_result: usize,
trace_extra: usize,
continue_frame: c_uint,
continue_last_frame_chunk: c_uint,
stage: c_int,
window: ZSTD_rust_windowUpdateState,
force_non_contiguous: c_int,
next_to_update: c_uint,
window_projection: ZSTD_rust_compressContinueWindowProjection,
frame_chunk_clamp_state: Option<ZSTD_rust_frameChunkClampState>,
frame_chunk_prepare_state: Option<ZSTD_rust_frameChunkPrepareState>,
frame_chunk_state: Option<ZSTD_rust_frameChunkState>,
continue_state: Option<ZSTD_rust_compressContinueState>,
is_first_block: c_int,
produced_c_size: u64,
}
static COMPRESS_END_TEST_CHECKSUM_STATE: XXH64_state_t = XXH64_state_t {
total_len: 0,
v: [0; 4],
mem64: [0; 4],
memsize: 0,
reserved32: 0,
reserved64: 0,
};
unsafe fn compress_end_test_context(
context: *mut c_void,
) -> &'static mut CompressEndTestContext {
unsafe { &mut *context.cast::<CompressEndTestContext>() }
}
unsafe extern "C" fn compress_end_test_frame_prepare_overflow(
_context: *mut c_void,
_src: *const c_void,
_block_size: usize,
) {
}
unsafe extern "C" fn compress_end_test_frame_prepare_window(
_context: *mut c_void,
_src: *const c_void,
_block_size: usize,
_max_dist: c_uint,
) {
}
unsafe extern "C" fn compress_end_test_frame_target(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
last_block: c_uint,
) -> usize {
let context = unsafe { compress_end_test_context(context) };
context.events.push("continue");
context.continue_frame = 1;
context.continue_last_frame_chunk = last_block;
context.continue_result
}
unsafe extern "C" fn compress_end_test_trace(context: *mut c_void, extra_c_size: usize) {
let context = unsafe { compress_end_test_context(context) };
context.events.push("trace");
context.trace_extra = extra_c_size;
}
fn compress_end_test_state(
context: &mut CompressEndTestContext,
consumed_src_size: &mut u64,
pledged_src_size_plus_one: u64,
content_size_flag: c_int,
) -> ZSTD_rust_compressEndState {
let base = WINDOW_INIT_SENTINEL.as_ptr().cast::<c_void>();
context.stage = ZSTD_COMPRESSION_STAGE_ONGOING;
context.window = ZSTD_rust_windowUpdateState {
nextSrc: base,
base,
dictBase: base,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
};
context.force_non_contiguous = 0;
context.next_to_update = ZSTD_WINDOW_START_INDEX;
context.is_first_block = 1;
context.produced_c_size = 0;
context.window_projection = ZSTD_rust_compressContinueWindowProjection {
next_src: ptr::addr_of_mut!(context.window.nextSrc),
base: ptr::addr_of_mut!(context.window.base),
dict_base: ptr::addr_of_mut!(context.window.dictBase),
dict_limit: ptr::addr_of_mut!(context.window.dictLimit),
low_limit: ptr::addr_of_mut!(context.window.lowLimit),
force_non_contiguous: ptr::addr_of_mut!(context.force_non_contiguous),
next_to_update: ptr::addr_of_mut!(context.next_to_update),
};
let callback_context = (context as *mut CompressEndTestContext).cast();
context.frame_chunk_clamp_state = Some(ZSTD_rust_frameChunkClampState {
next_to_update: ptr::addr_of_mut!(context.next_to_update),
low_limit: ptr::addr_of!(context.window.lowLimit),
});
let frame_chunk_clamp_state = context
.frame_chunk_clamp_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.frame_chunk_prepare_state = Some(ZSTD_rust_frameChunkPrepareState {
callback_context,
max_dist: 64,
correct_overflow: compress_end_test_frame_prepare_overflow,
check_dict_validity: compress_end_test_frame_prepare_window,
enforce_max_dist: compress_end_test_frame_prepare_window,
clamp_state: frame_chunk_clamp_state,
});
let frame_chunk_prepare_state = context
.frame_chunk_prepare_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.frame_chunk_state = Some(ZSTD_rust_frameChunkState {
callback_context,
tmp_workspace: ptr::null_mut(),
checksum_state: ptr::null_mut(),
is_first_block: ptr::addr_of_mut!(context.is_first_block),
stage: ptr::addr_of_mut!(context.stage),
tmp_wksp_size: 0,
block_size_max: 1,
savings: 0,
pre_block_splitter_level: 1,
strategy: ZSTD_FAST,
use_target_c_block_size: 1,
block_splitter_enabled: 0,
checksum_flag: 0,
ending_stage: ZSTD_COMPRESSION_STAGE_ENDING,
prepare_state: frame_chunk_prepare_state,
compress_target: Some(compress_end_test_frame_target),
compress_split: Some(compress_end_test_frame_target),
compress_internal: Some(compress_end_test_frame_target),
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits_state: ptr::null(),
});
let frame_chunk_state = context
.frame_chunk_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.continue_state = Some(ZSTD_rust_compressContinueState {
callback_context,
window_state: &context.window_projection,
ldm_window_state: ptr::null(),
overflow_state: ptr::null(),
frame_chunk_state,
compress_block: compress_end_test_frame_target,
stage: ptr::addr_of_mut!(context.stage),
consumed_src_size: consumed_src_size as *mut u64,
produced_c_size: ptr::addr_of_mut!(context.produced_c_size),
pledged_src_size_plus_one,
block_size_max: 1,
check_block_size: 0,
no_dict_id_flag: 0,
checksum_flag: 0,
content_size_flag: 0,
format: 0,
window_log: 20,
dict_id: 0,
ldm_enabled: 0,
});
let continue_state = context
.continue_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
ZSTD_rust_compressEndState {
callback_context,
compress_continue_state: continue_state,
trace: compress_end_test_trace,
consumed_src_size: consumed_src_size as *const u64,
pledged_src_size_plus_one,
content_size_flag,
stage: &mut context.stage,
no_dict_id_flag: 0,
checksum_flag: 1,
format: 0,
window_log: 20,
checksum_state: &COMPRESS_END_TEST_CHECKSUM_STATE,
}
}
#[test]
fn compress_end_preserves_callback_order_and_output_accounting() {
let mut dst = [0u8; 32];
let mut context = CompressEndTestContext {
continue_result: 3,
..CompressEndTestContext::default()
};
let mut consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &mut consumed_src_size, 9, 1);
let source = [0u8; 1];
let result = unsafe {
ZSTD_rust_compressEnd(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
source.as_ptr().cast(),
source.len(),
)
};
assert_eq!(result, 7);
assert_eq!(context.events, ["continue", "trace"]);
assert_eq!(
(context.continue_frame, context.continue_last_frame_chunk),
(1, 1)
);
assert_eq!(context.trace_extra, 4);
assert_eq!((consumed_src_size, context.produced_c_size), (8, 3));
assert_eq!(context.stage, 0);
}
#[test]
fn compress_end_stops_before_epilogue_when_continue_fails() {
let mut dst = [0u8; 16];
let mut context = CompressEndTestContext {
continue_result: ERROR(ZstdErrorCode::MemoryAllocation),
..CompressEndTestContext::default()
};
let mut consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &mut consumed_src_size, 8, 0);
let source = [0u8; 1];
let result = unsafe {
ZSTD_rust_compressEnd(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
source.as_ptr().cast(),
source.len(),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["continue"]);
}
#[test]
fn compress_end_stops_before_validation_and_trace_when_epilogue_fails() {
let mut dst = [0u8; 6];
let mut context = CompressEndTestContext {
continue_result: 3,
..CompressEndTestContext::default()
};
let mut consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &mut consumed_src_size, 99, 1);
let source = [0u8; 1];
let result = unsafe {
ZSTD_rust_compressEnd(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
source.as_ptr().cast(),
source.len(),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::DstSizeTooSmall));
assert_eq!(context.events, ["continue"]);
}
#[test]
fn compress_end_rejects_pledged_size_before_trace() {
let mut dst = [0u8; 32];
let mut context = CompressEndTestContext {
continue_result: 3,
..CompressEndTestContext::default()
};
let mut consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &mut consumed_src_size, 99, 1);
let source = [0u8; 1];
let result = unsafe {
ZSTD_rust_compressEnd(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
source.as_ptr().cast(),
source.len(),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!(context.events, ["continue"]);
}
#[derive(Default)]
struct CompressStreamInitTestContext {
events: Vec<&'static str>,
local_create_result: usize,
create_result: usize,
mt_result: usize,
begin_result: usize,
pledged: u64,
local_dict: *const c_void,
local_dict_buffer: *mut c_void,
local_dict_size: usize,
local_dict_content_type: c_int,
local_cdict: *mut c_void,
context_cdict: *const c_void,
prefix_dict: *const c_void,
created_local_cdict: *mut c_void,
cdict: *const c_void,
cdict_is_local: c_int,
cdict_compression_level: c_int,
cdict_dict_content_size: usize,
expected_prefix: *const c_void,
nb_workers: c_uint,
has_ext_seq_prod: c_int,
mt_context: *mut c_void,
buffer_mode: c_int,
block_size: usize,
ordinary_target: usize,
compression_level: c_int,
}
unsafe fn compress_stream_init_test_context(
context: *mut c_void,
) -> &'static mut CompressStreamInitTestContext {
unsafe { &mut *context.cast::<CompressStreamInitTestContext>() }
}
unsafe extern "C" fn compress_stream_init_test_get_local_dict(
context: *mut c_void,
local_dict: *mut ZSTD_rust_compressStreamInitLocalDictState,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("local-dict");
let local_dict = unsafe { &mut *local_dict };
local_dict.dict = context.local_dict;
local_dict.dict_buffer = context.local_dict_buffer;
local_dict.dict_size = context.local_dict_size;
local_dict.dict_content_type = context.local_dict_content_type;
local_dict.local_cdict = context.local_cdict;
local_dict.cdict = context.context_cdict;
local_dict.prefix_dict = context.prefix_dict;
local_dict.created_cdict = ptr::null_mut();
}
unsafe extern "C" fn compress_stream_init_test_create_local_dict(
context: *mut c_void,
local_dict: *mut ZSTD_rust_compressStreamInitLocalDictState,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("create-local-dict");
unsafe { &mut *local_dict }.created_cdict = context.created_local_cdict;
context.local_create_result
}
unsafe extern "C" fn compress_stream_init_test_publish_local_dict(
context: *mut c_void,
local_dict: *const ZSTD_rust_compressStreamInitLocalDictState,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("publish-local-dict");
let local_dict = unsafe { &*local_dict };
context.local_cdict = local_dict.created_cdict;
context.context_cdict = local_dict.created_cdict.cast_const();
context.cdict = local_dict.created_cdict.cast_const();
}
unsafe extern "C" fn compress_stream_init_test_refresh_cdict(
context: *mut c_void,
dictionaries: *mut ZSTD_rust_compressStreamInitDictionaryState,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("refresh-cdict");
let dictionaries = unsafe { &mut *dictionaries };
dictionaries.cdict = context.cdict;
dictionaries.cdict_is_local = context.cdict_is_local;
dictionaries.cdict_compression_level = context.cdict_compression_level;
dictionaries.cdict_dict_content_size = context.cdict_dict_content_size;
}
unsafe extern "C" fn compress_stream_init_test_clear_prefix(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("clear-prefix");
}
unsafe extern "C" fn compress_stream_init_test_assert_dictionaries(
context: *mut c_void,
prefix: *const c_void,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("assert-dictionaries");
assert_eq!(prefix, context.expected_prefix);
}
unsafe extern "C" fn compress_stream_init_test_set_level(params: *mut c_void, level: c_int) {
let context = unsafe { &mut *params.cast::<CompressStreamInitTestContext>() };
context.events.push("set-level");
context.compression_level = level;
}
unsafe extern "C" fn compress_stream_init_test_debug(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("debug");
}
unsafe extern "C" fn compress_stream_init_test_get_pledged(context: *mut c_void) -> u64 {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-pledged");
context.pledged
}
unsafe extern "C" fn compress_stream_init_test_set_pledged(
context: *mut c_void,
in_size: usize,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("set-pledged");
context.pledged = (in_size as u64).wrapping_add(1);
}
unsafe extern "C" fn compress_stream_init_test_get_cparam_mode(
params: *mut c_void,
_cdict: *const c_void,
_pledged: u64,
) -> c_int {
unsafe { compress_stream_init_test_context(params) }
.events
.push("get-cparam-mode");
0
}
unsafe extern "C" fn compress_stream_init_test_build_cparams(
params: *mut c_void,
_pledged: u64,
_dict_size: usize,
_mode: c_int,
) {
unsafe { compress_stream_init_test_context(params) }
.events
.push("build-cparams");
}
unsafe extern "C" fn compress_stream_init_test_resolve_params(
params: *mut c_void,
operation: c_int,
) {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push(match operation {
ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER => "resolve:block-splitter",
ZSTD_RUST_INIT_RESOLVE_LDM => "resolve:ldm",
ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER => "resolve:row-match-finder",
ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES => "resolve:validate-sequences",
ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE => "resolve:max-block-size",
ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH => "resolve:external-repcodes",
_ => "resolve:unknown",
});
}
unsafe extern "C" fn compress_stream_init_test_get_nb_workers(params: *mut c_void) -> c_uint {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push("get-workers");
context.nb_workers
}
unsafe extern "C" fn compress_stream_init_test_set_nb_workers(
params: *mut c_void,
nb_workers: c_uint,
) {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push("set-workers");
context.nb_workers = nb_workers;
}
unsafe extern "C" fn compress_stream_init_test_has_ext_seq_prod(params: *mut c_void) -> c_int {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push("has-ext-seq-prod");
context.has_ext_seq_prod
}
unsafe extern "C" fn compress_stream_init_test_trace(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("trace");
}
unsafe extern "C" fn compress_stream_init_test_get_mt_context(
context: *mut c_void,
) -> *mut c_void {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-mt-context");
context.mt_context
}
unsafe extern "C" fn compress_stream_init_test_create_mt_context(
context: *mut c_void,
_nb_workers: c_uint,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("create-mt-context");
if !ERR_isError(context.create_result) {
context.mt_context = ptr::dangling_mut::<c_void>();
}
context.create_result
}
unsafe extern "C" fn compress_stream_init_test_mt(
context: *mut c_void,
_mt_context: *mut c_void,
_dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState,
_params: *mut c_void,
_pledged: u64,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("init-mt");
context.mt_result
}
unsafe extern "C" fn compress_stream_init_test_commit_mt(
context: *mut c_void,
_dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState,
_params: *mut c_void,
) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("commit-mt");
}
unsafe extern "C" fn compress_stream_init_test_check_cparams(params: *mut c_void) {
unsafe { compress_stream_init_test_context(params) }
.events
.push("check-cparams");
}
unsafe extern "C" fn compress_stream_init_test_begin(
context: *mut c_void,
_dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState,
_params: *mut c_void,
_pledged: u64,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("compress-begin");
context.begin_result
}
unsafe extern "C" fn compress_stream_init_test_assert_ordinary(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("assert-ordinary");
}
unsafe extern "C" fn compress_stream_init_test_get_buffer_mode(context: *mut c_void) -> c_int {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-buffer-mode");
context.buffer_mode
}
unsafe extern "C" fn compress_stream_init_test_get_block_size(context: *mut c_void) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-block-size");
context.block_size
}
unsafe extern "C" fn compress_stream_init_test_commit_ordinary(
context: *mut c_void,
in_buff_target: usize,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("commit-ordinary");
context.ordinary_target = in_buff_target;
}
fn compress_stream_init_test_state(
context: &mut CompressStreamInitTestContext,
dictionaries: &mut ZSTD_rust_compressStreamInitDictionaryState,
end_op: c_int,
in_size: usize,
multithreaded: c_int,
) -> ZSTD_rust_compressStreamInitState {
ZSTD_rust_compressStreamInitState {
callback_context: (context as *mut CompressStreamInitTestContext).cast(),
params: (context as *mut CompressStreamInitTestContext).cast(),
dictionaries,
end_op,
in_size,
multithreaded,
mt_job_size_min: 10,
get_local_dict: compress_stream_init_test_get_local_dict,
create_local_dict: compress_stream_init_test_create_local_dict,
publish_local_dict: compress_stream_init_test_publish_local_dict,
refresh_cdict: compress_stream_init_test_refresh_cdict,
clear_prefix: compress_stream_init_test_clear_prefix,
assert_dictionaries: compress_stream_init_test_assert_dictionaries,
set_compression_level: compress_stream_init_test_set_level,
debug_init: compress_stream_init_test_debug,
get_pledged_src_size_plus_one: compress_stream_init_test_get_pledged,
set_pledged_src_size: compress_stream_init_test_set_pledged,
get_cparam_mode: compress_stream_init_test_get_cparam_mode,
build_cparams: compress_stream_init_test_build_cparams,
resolve_params: compress_stream_init_test_resolve_params,
get_nb_workers: compress_stream_init_test_get_nb_workers,
set_nb_workers: compress_stream_init_test_set_nb_workers,
has_ext_seq_prod: compress_stream_init_test_has_ext_seq_prod,
trace_begin: compress_stream_init_test_trace,
get_mt_context: compress_stream_init_test_get_mt_context,
create_mt_context: compress_stream_init_test_create_mt_context,
init_mt: compress_stream_init_test_mt,
commit_mt: compress_stream_init_test_commit_mt,
check_cparams: compress_stream_init_test_check_cparams,
compress_begin: compress_stream_init_test_begin,
assert_ordinary: compress_stream_init_test_assert_ordinary,
get_buffer_mode: compress_stream_init_test_get_buffer_mode,
get_block_size: compress_stream_init_test_get_block_size,
commit_ordinary: compress_stream_init_test_commit_ordinary,
}
}
#[test]
fn compress_stream_init_preserves_ordinary_callback_order_and_policy() {
let mut context = CompressStreamInitTestContext {
pledged: 0,
cdict: ptr::dangling::<c_void>(),
cdict_compression_level: 17,
cdict_dict_content_size: 3,
nb_workers: 0,
buffer_mode: ZSTD_BM_BUFFERED,
block_size: 4,
..CompressStreamInitTestContext::default()
};
let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState {
prefix_dict: ptr::null(),
prefix_dict_size: 0,
prefix_dict_content_type: 0,
cdict: ptr::null(),
cdict_is_local: 0,
cdict_compression_level: 0,
cdict_dict_content_size: 0,
};
let state =
compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_END, 4, 0);
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, 0);
assert_eq!(context.compression_level, 17);
assert_eq!(context.pledged, 5);
assert_eq!(context.ordinary_target, 5);
assert_eq!(
context.events,
[
"local-dict",
"refresh-cdict",
"clear-prefix",
"assert-dictionaries",
"set-level",
"debug",
"set-pledged",
"get-pledged",
"get-cparam-mode",
"build-cparams",
"resolve:block-splitter",
"resolve:ldm",
"resolve:row-match-finder",
"resolve:validate-sequences",
"resolve:max-block-size",
"resolve:external-repcodes",
"check-cparams",
"compress-begin",
"assert-ordinary",
"get-buffer-mode",
"get-block-size",
"commit-ordinary",
]
);
}
#[test]
fn compress_stream_init_orchestrates_local_dictionary_creation_and_publish() {
let mut context = CompressStreamInitTestContext {
local_dict: ptr::dangling::<c_void>(),
local_dict_size: 3,
created_local_cdict: ptr::dangling_mut::<c_void>(),
cdict_is_local: 1,
cdict_dict_content_size: 3,
buffer_mode: ZSTD_BM_BUFFERED,
block_size: 4,
..CompressStreamInitTestContext::default()
};
let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState {
prefix_dict: ptr::null(),
prefix_dict_size: 0,
prefix_dict_content_type: 0,
cdict: ptr::null(),
cdict_is_local: 0,
cdict_compression_level: 0,
cdict_dict_content_size: 0,
};
let state =
compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_CONTINUE, 0, 0);
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, 0);
assert_eq!(context.local_cdict, context.created_local_cdict);
assert_eq!(
context.context_cdict,
context.created_local_cdict.cast_const()
);
assert_eq!(
&context.events[..3],
["local-dict", "create-local-dict", "publish-local-dict"]
);
assert_eq!(context.events[3], "refresh-cdict");
assert_eq!(dictionaries.cdict, context.created_local_cdict.cast_const());
assert_eq!(dictionaries.cdict_is_local, 1);
}
#[test]
fn compress_stream_init_stops_before_later_callbacks_on_error() {
let mut context = CompressStreamInitTestContext {
local_dict: ptr::dangling::<c_void>(),
local_dict_size: 3,
local_create_result: ERROR(ZstdErrorCode::MemoryAllocation),
..CompressStreamInitTestContext::default()
};
let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState {
prefix_dict: ptr::null(),
prefix_dict_size: 0,
prefix_dict_content_type: 0,
cdict: ptr::null(),
cdict_is_local: 0,
cdict_compression_level: 0,
cdict_dict_content_size: 0,
};
let state =
compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_CONTINUE, 0, 0);
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["local-dict", "create-local-dict"]);
}
#[test]
fn compress_stream_init_preserves_mt_branch_and_init_error_order() {
let mut context = CompressStreamInitTestContext {
cdict: ptr::dangling::<c_void>(),
cdict_dict_content_size: 3,
nb_workers: 2,
mt_result: ERROR(ZstdErrorCode::MemoryAllocation),
..CompressStreamInitTestContext::default()
};
let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState {
prefix_dict: ptr::null(),
prefix_dict_size: 0,
prefix_dict_content_type: 0,
cdict: ptr::null(),
cdict_is_local: 0,
cdict_compression_level: 0,
cdict_dict_content_size: 0,
};
let state =
compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_END, 99, 1);
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(
context.events,
[
"local-dict",
"refresh-cdict",
"clear-prefix",
"assert-dictionaries",
"set-level",
"debug",
"set-pledged",
"get-pledged",
"get-cparam-mode",
"build-cparams",
"resolve:block-splitter",
"resolve:ldm",
"resolve:row-match-finder",
"resolve:validate-sequences",
"resolve:max-block-size",
"resolve:external-repcodes",
"has-ext-seq-prod",
"get-workers",
"trace",
"get-mt-context",
"create-mt-context",
"get-mt-context",
"init-mt",
]
);
}
fn system_round_trip(compressed: &[u8]) -> Option<Vec<u8>> {
let mut child = Command::new("zstd")
.args(["-q", "-d", "-c"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.ok()?;
child.stdin.take()?.write_all(compressed).ok()?;
let output = child.wait_with_output().ok()?;
if !output.status.success() {
panic!(
"system zstd rejected Rust output: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Some(output.stdout)
}
fn compress_input(input: &[u8], level: c_int) -> Vec<u8> {
let capacity = crate::zstd_compress_api::ZSTD_compressBound(input.len());
assert!(!ERR_isError(capacity));
let mut output = vec![0u8; capacity];
let written = unsafe {
ZSTD_compress(
output.as_mut_ptr().cast(),
output.len(),
input.as_ptr().cast(),
input.len(),
level,
)
};
assert!(!ERR_isError(written));
output.truncate(written);
output
}
#[derive(Default)]
struct FrameChunkTestContext {
prepare_calls: usize,
prepared_sizes: [usize; 4],
prepare_order: [u8; 16],
prepare_order_len: usize,
prepare_max_dist: c_uint,
prepare_state: Option<ZSTD_rust_frameChunkPrepareState>,
clamp_state: Option<ZSTD_rust_frameChunkClampState>,
clamp_next_to_update: c_uint,
clamp_low_limit: c_uint,
target_calls: usize,
split_calls: usize,
internal_calls: usize,
last_blocks: [c_uint; 4],
checksum_state: Option<XXH64_state_t>,
target_result: usize,
split_result: usize,
internal_result: usize,
}
unsafe fn frame_chunk_test_context(context: *mut c_void) -> &'static mut FrameChunkTestContext {
unsafe { &mut *context.cast::<FrameChunkTestContext>() }
}
unsafe fn frame_chunk_test_record_prepare(context: *mut c_void, step: u8, block_size: usize) {
let context = unsafe { frame_chunk_test_context(context) };
if context.prepare_order_len < context.prepare_order.len() {
context.prepare_order[context.prepare_order_len] = step;
}
context.prepare_order_len += 1;
if step == 1 {
if context.prepare_calls < context.prepared_sizes.len() {
context.prepared_sizes[context.prepare_calls] = block_size;
}
context.prepare_calls += 1;
}
}
unsafe extern "C" fn frame_chunk_test_correct_overflow(
context: *mut c_void,
_src: *const c_void,
block_size: usize,
) {
unsafe { frame_chunk_test_record_prepare(context, 1, block_size) };
}
unsafe extern "C" fn frame_chunk_test_check_dict_validity(
context: *mut c_void,
_src: *const c_void,
block_size: usize,
max_dist: c_uint,
) {
unsafe { frame_chunk_test_record_prepare(context, 2, block_size) };
unsafe { frame_chunk_test_context(context) }.prepare_max_dist = max_dist;
}
unsafe extern "C" fn frame_chunk_test_enforce_max_dist(
context: *mut c_void,
_src: *const c_void,
block_size: usize,
max_dist: c_uint,
) {
unsafe { frame_chunk_test_record_prepare(context, 3, block_size) };
unsafe { frame_chunk_test_context(context) }.prepare_max_dist = max_dist;
}
unsafe extern "C" fn frame_chunk_test_target(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
last_block: c_uint,
) -> usize {
let context = unsafe { frame_chunk_test_context(context) };
if context.target_calls < context.last_blocks.len() {
context.last_blocks[context.target_calls] = last_block;
}
context.target_calls += 1;
context.target_result
}
unsafe extern "C" fn frame_chunk_test_split(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
last_block: c_uint,
) -> usize {
let context = unsafe { frame_chunk_test_context(context) };
if context.split_calls < context.last_blocks.len() {
context.last_blocks[context.split_calls] = last_block;
}
context.split_calls += 1;
context.split_result
}
unsafe extern "C" fn frame_chunk_test_internal(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
last_block: c_uint,
) -> usize {
let context = unsafe { frame_chunk_test_context(context) };
if context.internal_calls < context.last_blocks.len() {
context.last_blocks[context.internal_calls] = last_block;
}
context.internal_calls += 1;
context.internal_result
}
fn frame_chunk_test_state(
context: &mut FrameChunkTestContext,
is_first_block: &mut c_int,
stage: &mut c_int,
use_target_c_block_size: c_int,
block_splitter_enabled: c_int,
checksum_flag: c_int,
) -> ZSTD_rust_frameChunkState {
let context_ptr = context as *mut FrameChunkTestContext;
let callback_context = context_ptr.cast::<c_void>();
context.checksum_state = Some(XXH64_state_t {
total_len: 0,
v: [0; 4],
mem64: [0; 4],
memsize: 0,
reserved32: 0,
reserved64: 0,
});
let checksum_state = context.checksum_state.as_mut().unwrap();
unsafe {
let _ = XXH64_reset(checksum_state, 0);
}
unsafe {
(*context_ptr).clamp_state = Some(ZSTD_rust_frameChunkClampState {
next_to_update: &mut (*context_ptr).clamp_next_to_update,
low_limit: &(*context_ptr).clamp_low_limit,
});
(*context_ptr).prepare_state = Some(ZSTD_rust_frameChunkPrepareState {
callback_context,
max_dist: 64,
correct_overflow: frame_chunk_test_correct_overflow,
check_dict_validity: frame_chunk_test_check_dict_validity,
enforce_max_dist: frame_chunk_test_enforce_max_dist,
clamp_state: (*context_ptr)
.clamp_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _),
});
}
let prepare_state = unsafe {
(*context_ptr)
.prepare_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _)
};
ZSTD_rust_frameChunkState {
callback_context,
tmp_workspace: ptr::null_mut(),
checksum_state,
is_first_block,
stage,
tmp_wksp_size: 0,
block_size_max: 4,
savings: 0,
pre_block_splitter_level: 1,
strategy: ZSTD_FAST,
use_target_c_block_size,
block_splitter_enabled,
checksum_flag,
ending_stage: 77,
prepare_state,
compress_target: Some(frame_chunk_test_target),
compress_split: Some(frame_chunk_test_split),
compress_internal: Some(frame_chunk_test_internal),
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits_state: ptr::null(),
}
}
#[test]
fn frame_chunk_internal_path_emits_blocks_and_updates_state_once() {
let mut context = FrameChunkTestContext {
internal_result: 2,
clamp_next_to_update: 1,
clamp_low_limit: 4,
..FrameChunkTestContext::default()
};
let mut is_first_block = 1;
let mut stage = 0;
let state = frame_chunk_test_state(&mut context, &mut is_first_block, &mut stage, 0, 0, 1);
let source = [0x11u8; 8];
let mut output = [0xa5u8; 16];
let result = unsafe {
compress_frame_chunk_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
)
};
assert_eq!(result, 10);
assert_eq!(context.prepare_calls, 2);
assert_eq!(context.prepared_sizes[..2], [4, 4]);
assert_eq!(context.prepare_order[..6], [1, 2, 3, 1, 2, 3]);
assert_eq!(context.prepare_max_dist, 64);
assert_eq!(context.clamp_next_to_update, 4);
assert_eq!(context.internal_calls, 2);
assert_eq!(context.last_blocks[..2], [0, 1]);
assert_eq!(
context.checksum_state.as_ref().unwrap().total_len,
source.len() as u64
);
assert_eq!(is_first_block, 0);
assert_eq!(stage, 77);
assert_ne!(output[..3], [0xa5; 3]);
assert_ne!(output[5..8], [0xa5; 3]);
}
#[test]
fn frame_chunk_dispatches_target_and_split_paths_before_internal() {
let source = [0x22u8; 4];
let mut target_context = FrameChunkTestContext {
target_result: 4,
split_result: 5,
internal_result: 6,
..FrameChunkTestContext::default()
};
let mut target_first = 1;
let mut target_stage = 0;
let target_state = frame_chunk_test_state(
&mut target_context,
&mut target_first,
&mut target_stage,
1,
1,
0,
);
let mut target_output = [0xa5u8; 8];
let target_result = unsafe {
compress_frame_chunk_body_with(
&target_state,
target_output.as_mut_ptr().cast(),
target_output.len(),
source.as_ptr().cast(),
source.len(),
0,
)
};
assert_eq!(target_result, 4);
assert_eq!(target_context.target_calls, 1);
assert_eq!(target_context.split_calls, 0);
assert_eq!(target_context.internal_calls, 0);
let mut split_context = FrameChunkTestContext {
split_result: 5,
internal_result: 6,
..FrameChunkTestContext::default()
};
let mut split_first = 1;
let mut split_stage = 0;
let split_state = frame_chunk_test_state(
&mut split_context,
&mut split_first,
&mut split_stage,
0,
1,
0,
);
let mut split_output = [0xa5u8; 8];
let split_result = unsafe {
compress_frame_chunk_body_with(
&split_state,
split_output.as_mut_ptr().cast(),
split_output.len(),
source.as_ptr().cast(),
source.len(),
0,
)
};
assert_eq!(split_result, 5);
assert_eq!(split_context.target_calls, 0);
assert_eq!(split_context.split_calls, 1);
assert_eq!(split_context.internal_calls, 0);
}
#[test]
fn frame_chunk_keeps_state_unchanged_on_early_capacity_error() {
let mut context = FrameChunkTestContext::default();
let mut is_first_block = 1;
let mut stage = 23;
let state = frame_chunk_test_state(&mut context, &mut is_first_block, &mut stage, 0, 0, 1);
let source = [0x33u8; 4];
let mut output = [0xa5u8; 5];
let result = unsafe {
compress_frame_chunk_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
)
};
assert_eq!(result, ERROR(ZstdErrorCode::DstSizeTooSmall));
assert_eq!(
context.checksum_state.as_ref().unwrap().total_len,
source.len() as u64
);
assert_eq!(context.prepare_calls, 0);
assert_eq!(context.internal_calls, 0);
assert_eq!(is_first_block, 1);
assert_eq!(stage, 23);
assert_eq!(output, [0xa5; 5]);
}
#[derive(Default)]
struct CompressContinueTestContext {
overflow_calls: usize,
frame_calls: usize,
block_calls: usize,
last_frame_chunk: c_uint,
frame_result: usize,
block_result: usize,
window: ZSTD_rust_windowUpdateState,
ldm_window: ZSTD_rust_windowUpdateState,
force_non_contiguous: c_int,
next_to_update: c_uint,
loaded_dict_end: c_uint,
dict_match_state: *const c_void,
window_projection: ZSTD_rust_compressContinueWindowProjection,
ldm_window_projection: ZSTD_rust_compressContinueWindowProjection,
overflow_state: Option<ZSTD_rust_overflowCorrectState>,
is_first_block: c_int,
frame_chunk_clamp_state: Option<ZSTD_rust_frameChunkClampState>,
frame_chunk_prepare_state: Option<ZSTD_rust_frameChunkPrepareState>,
frame_chunk_state: Option<ZSTD_rust_frameChunkState>,
}
unsafe fn compress_continue_test_context(
context: *mut c_void,
) -> &'static mut CompressContinueTestContext {
unsafe { &mut *context.cast::<CompressContinueTestContext>() }
}
unsafe extern "C" fn compress_continue_test_overflow_need(
context: *mut c_void,
_src: *const c_void,
_src_end: *const c_void,
) -> c_int {
let context = unsafe { compress_continue_test_context(context) };
context.overflow_calls += 1;
0
}
unsafe extern "C" fn compress_continue_test_overflow_correct(
_context: *mut c_void,
_src: *const c_void,
) -> c_uint {
0
}
unsafe extern "C" fn compress_continue_test_overflow_callback(_context: *mut c_void) {}
unsafe extern "C" fn compress_continue_test_overflow_reduce(
_context: *mut c_void,
_correction: c_uint,
) {
}
unsafe extern "C" fn compress_continue_test_frame_prepare_overflow(
_context: *mut c_void,
_src: *const c_void,
_block_size: usize,
) {
}
unsafe extern "C" fn compress_continue_test_frame_prepare_window(
_context: *mut c_void,
_src: *const c_void,
_block_size: usize,
_max_dist: c_uint,
) {
}
unsafe extern "C" fn compress_continue_test_frame(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
last_frame_chunk: c_uint,
) -> usize {
let context = unsafe { compress_continue_test_context(context) };
context.frame_calls += 1;
context.last_frame_chunk = last_frame_chunk;
context.frame_result
}
unsafe extern "C" fn compress_continue_test_block(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
_last_frame_chunk: c_uint,
) -> usize {
let context = unsafe { compress_continue_test_context(context) };
context.block_calls += 1;
context.block_result
}
fn compress_continue_test_state(
context: &mut CompressContinueTestContext,
stage: &mut c_int,
consumed_src_size: &mut u64,
produced_c_size: &mut u64,
pledged_src_size_plus_one: u64,
block_size_max: usize,
check_block_size: c_int,
) -> ZSTD_rust_compressContinueState {
let base = WINDOW_INIT_SENTINEL.as_ptr().cast::<c_void>();
let next_src = unsafe { base.cast::<u8>().add(2).cast::<c_void>() };
context.window = ZSTD_rust_windowUpdateState {
nextSrc: next_src,
base,
dictBase: base,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
};
context.ldm_window = context.window;
context.force_non_contiguous = 0;
context.next_to_update = ZSTD_WINDOW_START_INDEX;
context.loaded_dict_end = 0;
context.dict_match_state = ptr::null();
context.is_first_block = 1;
context.window_projection = ZSTD_rust_compressContinueWindowProjection {
next_src: ptr::addr_of_mut!(context.window.nextSrc),
base: ptr::addr_of_mut!(context.window.base),
dict_base: ptr::addr_of_mut!(context.window.dictBase),
dict_limit: ptr::addr_of_mut!(context.window.dictLimit),
low_limit: ptr::addr_of_mut!(context.window.lowLimit),
force_non_contiguous: ptr::addr_of_mut!(context.force_non_contiguous),
next_to_update: ptr::addr_of_mut!(context.next_to_update),
};
context.ldm_window_projection = ZSTD_rust_compressContinueWindowProjection {
next_src: ptr::addr_of_mut!(context.ldm_window.nextSrc),
base: ptr::addr_of_mut!(context.ldm_window.base),
dict_base: ptr::addr_of_mut!(context.ldm_window.dictBase),
dict_limit: ptr::addr_of_mut!(context.ldm_window.dictLimit),
low_limit: ptr::addr_of_mut!(context.ldm_window.lowLimit),
force_non_contiguous: ptr::null_mut(),
next_to_update: ptr::null_mut(),
};
let callback_context = (context as *mut CompressContinueTestContext).cast();
context.overflow_state = Some(ZSTD_rust_overflowCorrectState {
callback_context,
next_to_update: ptr::addr_of_mut!(context.next_to_update),
need_correction: Some(compress_continue_test_overflow_need),
correct_overflow: Some(compress_continue_test_overflow_correct),
mark_tables_dirty: Some(compress_continue_test_overflow_callback),
reduce_index: Some(compress_continue_test_overflow_reduce),
mark_tables_clean: Some(compress_continue_test_overflow_callback),
loaded_dict_end: ptr::addr_of_mut!(context.loaded_dict_end),
dict_match_state: ptr::addr_of_mut!(context.dict_match_state),
});
let overflow_state = context
.overflow_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.frame_chunk_clamp_state = Some(ZSTD_rust_frameChunkClampState {
next_to_update: ptr::addr_of_mut!(context.next_to_update),
low_limit: ptr::addr_of!(context.window.lowLimit),
});
let frame_chunk_clamp_state = context
.frame_chunk_clamp_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.frame_chunk_prepare_state = Some(ZSTD_rust_frameChunkPrepareState {
callback_context,
max_dist: 64,
correct_overflow: compress_continue_test_frame_prepare_overflow,
check_dict_validity: compress_continue_test_frame_prepare_window,
enforce_max_dist: compress_continue_test_frame_prepare_window,
clamp_state: frame_chunk_clamp_state,
});
let frame_chunk_prepare_state = context
.frame_chunk_prepare_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.frame_chunk_state = Some(ZSTD_rust_frameChunkState {
callback_context,
tmp_workspace: ptr::null_mut(),
checksum_state: ptr::null_mut(),
is_first_block: ptr::addr_of_mut!(context.is_first_block),
stage,
tmp_wksp_size: 0,
block_size_max,
savings: 0,
pre_block_splitter_level: 1,
strategy: ZSTD_FAST,
use_target_c_block_size: 1,
block_splitter_enabled: 0,
checksum_flag: 0,
ending_stage: ZSTD_COMPRESSION_STAGE_ENDING,
prepare_state: frame_chunk_prepare_state,
compress_target: Some(compress_continue_test_frame),
compress_split: Some(compress_continue_test_frame),
compress_internal: Some(compress_continue_test_frame),
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits_state: ptr::null(),
});
let frame_chunk_state = context
.frame_chunk_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
ZSTD_rust_compressContinueState {
callback_context,
window_state: &context.window_projection,
ldm_window_state: &context.ldm_window_projection,
overflow_state,
frame_chunk_state,
compress_block: compress_continue_test_block,
stage,
consumed_src_size,
produced_c_size,
pledged_src_size_plus_one,
block_size_max,
check_block_size,
no_dict_id_flag: 0,
checksum_flag: 0,
content_size_flag: 0,
format: 0,
window_log: 10,
dict_id: 0,
ldm_enabled: 1,
}
}
#[test]
fn compress_continue_block_builds_small_block_through_projected_states() {
let mut probe = BuildSeqStoreSelectProbe::default();
let mut seq_store = unsafe { MaybeUninit::<SeqStore_t>::zeroed().assume_init() };
let mut build_state = build_seq_store_select_test_state(&mut probe, 0, 0, 0, 0);
build_state.seq_store = &mut seq_store;
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = &mut prev_block as *mut _;
let mut next_c_block = &mut next_block as *mut _;
let mut seq_collector = SeqCollector {
collectSequences: 0,
seqStart: ptr::null_mut(),
seqIndex: 0,
maxSequences: 0,
};
let block_state = ZSTD_rust_blockInternalState {
seq_store: &seq_store,
prev_c_block: &mut prev_c_block,
next_c_block: &mut next_c_block,
tmp_workspace: ptr::null_mut(),
tmp_wksp_size: 0,
seq_collector: &mut seq_collector,
strategy: 0,
disable_literal_compression: 0,
bmi2: 0,
is_first_block: 1,
};
let state = ZSTD_rust_compressContinueBlockState {
build_seq_store_state: &build_state,
block_internal_state: &block_state,
};
let source = [0u8; 1];
let mut output = [0xa5u8; 8];
let result = unsafe {
ZSTD_rust_compressContinueBlock(
(&state as *const ZSTD_rust_compressContinueBlockState)
.cast_mut()
.cast(),
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
0,
)
};
assert_eq!(result, 0);
assert_eq!(output, [0xa5; 8]);
}
#[test]
fn compress_continue_starts_frame_and_updates_progression() {
let mut context = CompressContinueTestContext {
frame_result: 6,
..CompressContinueTestContext::default()
};
let mut stage = ZSTD_COMPRESSION_STAGE_INIT;
let mut consumed = 7;
let mut produced = 11;
let state = compress_continue_test_state(
&mut context,
&mut stage,
&mut consumed,
&mut produced,
0,
16,
0,
);
let source = [0x11u8; 5];
let mut output = [0xa5u8; 32];
let result = unsafe {
ZSTD_rust_compressContinue(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
1,
)
};
assert_eq!(result, 12);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ENDING);
assert_eq!(consumed, 12);
assert_eq!(produced, 23);
assert_eq!(context.window.nextSrc, unsafe {
source.as_ptr().add(source.len()).cast()
});
assert_eq!(context.ldm_window.nextSrc, context.window.nextSrc);
assert_eq!(context.force_non_contiguous, 0);
assert_eq!(context.next_to_update, context.window.dictLimit);
assert_eq!(context.overflow_calls, 0);
assert_eq!(context.frame_calls, 1);
assert_eq!(context.block_calls, 0);
assert_eq!(context.last_frame_chunk, 1);
assert_eq!(context.is_first_block, 0);
}
#[test]
fn compress_continue_empty_frame_only_writes_header() {
let mut context = CompressContinueTestContext::default();
let mut stage = ZSTD_COMPRESSION_STAGE_INIT;
let mut consumed = 7;
let mut produced = 11;
let state = compress_continue_test_state(
&mut context,
&mut stage,
&mut consumed,
&mut produced,
0,
16,
0,
);
let mut output = [0xa5u8; 18];
let result = unsafe {
ZSTD_rust_compressContinue(
&state,
output.as_mut_ptr().cast(),
output.len(),
ptr::null(),
0,
1,
0,
)
};
assert_eq!(result, 6);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!((consumed, produced), (7, 11));
assert_eq!(context.frame_calls, 0);
assert_eq!(&output[..6], &[0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x00]);
assert_eq!(&output[6..], &[0xa5; 12]);
}
#[test]
fn compress_continue_rejects_invalid_stage_and_oversized_block_before_callbacks() {
let mut context = CompressContinueTestContext::default();
let mut stage = ZSTD_COMPRESSION_STAGE_INIT;
let mut consumed = 4;
let mut produced = 9;
let mut state = compress_continue_test_state(
&mut context,
&mut stage,
&mut consumed,
&mut produced,
0,
4,
1,
);
let source = [0x22u8; 5];
let mut output = [0xa5u8; 16];
let initial_window = context.window;
let initial_ldm_window = context.ldm_window;
let oversized = unsafe {
ZSTD_rust_compressContinue(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
0,
0,
)
};
assert_eq!(oversized, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_INIT);
assert_eq!((consumed, produced), (4, 9));
assert_eq!(context.window, initial_window);
assert_eq!(context.ldm_window, initial_ldm_window);
assert_eq!(context.block_calls, 0);
state.check_block_size = 0;
stage = ZSTD_COMPRESSION_STAGE_CREATED;
let created = unsafe {
ZSTD_rust_compressContinue(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
0,
0,
)
};
assert_eq!(created, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_CREATED);
assert_eq!(context.window, initial_window);
assert_eq!(context.ldm_window, initial_ldm_window);
assert_eq!(context.block_calls, 0);
}
#[test]
fn compress_continue_propagates_block_error_without_progression() {
let mut context = CompressContinueTestContext {
block_result: ERROR(ZstdErrorCode::DstSizeTooSmall),
..CompressContinueTestContext::default()
};
let mut stage = ZSTD_COMPRESSION_STAGE_ONGOING;
let mut consumed = 4;
let mut produced = 9;
let state = compress_continue_test_state(
&mut context,
&mut stage,
&mut consumed,
&mut produced,
0,
16,
0,
);
let source = [0x33u8; 5];
let mut output = [0xa5u8; 16];
let result = unsafe {
ZSTD_rust_compressContinue(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
0,
0,
)
};
assert_eq!(result, ERROR(ZstdErrorCode::DstSizeTooSmall));
assert_eq!(
(stage, consumed, produced),
(ZSTD_COMPRESSION_STAGE_ONGOING, 4, 9)
);
assert_eq!(context.window.nextSrc, unsafe {
source.as_ptr().add(source.len()).cast()
});
assert_eq!(context.ldm_window.nextSrc, context.window.nextSrc);
assert_eq!(context.overflow_calls, 1);
assert_eq!(context.block_calls, 1);
}
#[test]
fn compress_continue_reports_pledge_overrun_after_updating_counters() {
let mut context = CompressContinueTestContext {
block_result: 3,
..CompressContinueTestContext::default()
};
let mut stage = ZSTD_COMPRESSION_STAGE_ONGOING;
let mut consumed = 0;
let mut produced = 0;
let state = compress_continue_test_state(
&mut context,
&mut stage,
&mut consumed,
&mut produced,
6,
16,
0,
);
let source = [0x44u8; 6];
let mut output = [0xa5u8; 16];
let result = unsafe {
ZSTD_rust_compressContinue(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
0,
0,
)
};
assert_eq!(result, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!((consumed, produced), (6, 3));
assert_eq!(context.window.nextSrc, unsafe {
source.as_ptr().add(source.len()).cast()
});
assert_eq!(context.block_calls, 1);
}
#[derive(Default)]
struct CompressStreamTestContext {
continue_calls: usize,
end_calls: usize,
reset_calls: usize,
continue_result: usize,
end_result: usize,
reset_result: usize,
window: ZSTD_rust_windowUpdateState,
force_non_contiguous: c_int,
next_to_update: c_uint,
window_projection: ZSTD_rust_compressContinueWindowProjection,
frame_chunk_clamp_state: Option<ZSTD_rust_frameChunkClampState>,
frame_chunk_prepare_state: Option<ZSTD_rust_frameChunkPrepareState>,
continue_frame_chunk_state: Option<ZSTD_rust_frameChunkState>,
end_frame_chunk_state: Option<ZSTD_rust_frameChunkState>,
continue_state: Option<ZSTD_rust_compressContinueState>,
end_continue_state: Option<ZSTD_rust_compressContinueState>,
end_state: Option<ZSTD_rust_compressEndState>,
compression_stage: c_int,
is_first_block: c_int,
end_is_first_block: c_int,
consumed_src_size: u64,
produced_c_size: u64,
}
unsafe fn compress_stream_test_context(
context: *mut c_void,
) -> &'static mut CompressStreamTestContext {
unsafe { &mut *context.cast::<CompressStreamTestContext>() }
}
unsafe extern "C" fn compress_stream_test_block(
context: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
_last_block: c_uint,
) -> usize {
let context = unsafe { compress_stream_test_context(context) };
context.continue_calls += 1;
if !ERR_isError(context.continue_result) && context.continue_result != 0 {
assert!(context.continue_result <= dst_capacity);
unsafe { ptr::write_bytes(dst.cast::<u8>(), 0x5a, context.continue_result) };
}
context.continue_result
}
unsafe extern "C" fn compress_stream_test_end(
context: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
_last_block: c_uint,
) -> usize {
let context = unsafe { compress_stream_test_context(context) };
context.end_calls += 1;
if !ERR_isError(context.end_result) && context.end_result != 0 {
assert!(context.end_result <= dst_capacity);
unsafe { ptr::write_bytes(dst.cast::<u8>(), 0x3c, context.end_result) };
}
context.end_result
}
unsafe extern "C" fn compress_stream_test_reset(context: *mut c_void) -> usize {
let context = unsafe { compress_stream_test_context(context) };
context.reset_calls += 1;
context.reset_result
}
unsafe extern "C" fn compress_stream_test_frame_prepare_overflow(
_context: *mut c_void,
_src: *const c_void,
_block_size: usize,
) {
}
unsafe extern "C" fn compress_stream_test_frame_prepare_window(
_context: *mut c_void,
_src: *const c_void,
_block_size: usize,
_max_dist: c_uint,
) {
}
unsafe extern "C" fn compress_stream_test_trace(_context: *mut c_void, _extra_c_size: usize) {}
#[allow(clippy::too_many_arguments)]
fn compress_stream_test_state(
context: &mut CompressStreamTestContext,
in_buffer_mode: c_int,
out_buffer_mode: c_int,
stage: &mut c_int,
block_size_max: usize,
stable_in_not_consumed: &mut usize,
in_buff: *mut c_void,
in_buff_size: usize,
in_to_compress: &mut usize,
in_buff_pos: &mut usize,
in_buff_target: &mut usize,
out_buff: *mut c_void,
out_buff_size: usize,
out_buff_content_size: &mut usize,
out_buff_flushed_size: &mut usize,
frame_ended: &mut c_uint,
) -> ZSTD_rust_compressStreamState {
let base = WINDOW_INIT_SENTINEL.as_ptr().cast::<c_void>();
context.window = ZSTD_rust_windowUpdateState {
nextSrc: base,
base,
dictBase: base,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
};
context.force_non_contiguous = 0;
context.next_to_update = ZSTD_WINDOW_START_INDEX;
context.compression_stage = ZSTD_COMPRESSION_STAGE_ONGOING;
context.is_first_block = 1;
context.end_is_first_block = 1;
context.consumed_src_size = 0;
context.produced_c_size = 0;
context.window_projection = ZSTD_rust_compressContinueWindowProjection {
next_src: ptr::addr_of_mut!(context.window.nextSrc),
base: ptr::addr_of_mut!(context.window.base),
dict_base: ptr::addr_of_mut!(context.window.dictBase),
dict_limit: ptr::addr_of_mut!(context.window.dictLimit),
low_limit: ptr::addr_of_mut!(context.window.lowLimit),
force_non_contiguous: ptr::addr_of_mut!(context.force_non_contiguous),
next_to_update: ptr::addr_of_mut!(context.next_to_update),
};
let callback_context = (context as *mut CompressStreamTestContext).cast();
context.frame_chunk_clamp_state = Some(ZSTD_rust_frameChunkClampState {
next_to_update: ptr::addr_of_mut!(context.next_to_update),
low_limit: ptr::addr_of!(context.window.lowLimit),
});
let frame_chunk_clamp_state = context
.frame_chunk_clamp_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.frame_chunk_prepare_state = Some(ZSTD_rust_frameChunkPrepareState {
callback_context,
max_dist: 64,
correct_overflow: compress_stream_test_frame_prepare_overflow,
check_dict_validity: compress_stream_test_frame_prepare_window,
enforce_max_dist: compress_stream_test_frame_prepare_window,
clamp_state: frame_chunk_clamp_state,
});
let frame_chunk_prepare_state = context
.frame_chunk_prepare_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.continue_frame_chunk_state = Some(ZSTD_rust_frameChunkState {
callback_context,
tmp_workspace: ptr::null_mut(),
checksum_state: ptr::null_mut(),
is_first_block: ptr::addr_of_mut!(context.is_first_block),
stage: ptr::addr_of_mut!(context.compression_stage),
tmp_wksp_size: 0,
block_size_max,
savings: 0,
pre_block_splitter_level: 1,
strategy: ZSTD_FAST,
use_target_c_block_size: 1,
block_splitter_enabled: 0,
checksum_flag: 0,
ending_stage: ZSTD_COMPRESSION_STAGE_ENDING,
prepare_state: frame_chunk_prepare_state,
compress_target: Some(compress_stream_test_block),
compress_split: Some(compress_stream_test_block),
compress_internal: Some(compress_stream_test_block),
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits_state: ptr::null(),
});
let continue_frame_chunk_state = context
.continue_frame_chunk_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.end_frame_chunk_state = Some(ZSTD_rust_frameChunkState {
callback_context,
tmp_workspace: ptr::null_mut(),
checksum_state: ptr::null_mut(),
is_first_block: ptr::addr_of_mut!(context.end_is_first_block),
stage: ptr::addr_of_mut!(context.compression_stage),
tmp_wksp_size: 0,
block_size_max,
savings: 0,
pre_block_splitter_level: 1,
strategy: ZSTD_FAST,
use_target_c_block_size: 1,
block_splitter_enabled: 0,
checksum_flag: 0,
ending_stage: ZSTD_COMPRESSION_STAGE_ENDING,
prepare_state: frame_chunk_prepare_state,
compress_target: Some(compress_stream_test_end),
compress_split: Some(compress_stream_test_end),
compress_internal: Some(compress_stream_test_end),
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits_state: ptr::null(),
});
let end_frame_chunk_state = context
.end_frame_chunk_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.continue_state = Some(ZSTD_rust_compressContinueState {
callback_context,
window_state: &context.window_projection,
ldm_window_state: ptr::null(),
overflow_state: ptr::null(),
frame_chunk_state: continue_frame_chunk_state,
compress_block: compress_stream_test_block,
stage: ptr::addr_of_mut!(context.compression_stage),
consumed_src_size: ptr::addr_of_mut!(context.consumed_src_size),
produced_c_size: ptr::addr_of_mut!(context.produced_c_size),
pledged_src_size_plus_one: 0,
block_size_max,
check_block_size: 0,
no_dict_id_flag: 0,
checksum_flag: 0,
content_size_flag: 0,
format: 0,
window_log: 20,
dict_id: 0,
ldm_enabled: 0,
});
let continue_state = context
.continue_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.end_continue_state = Some(ZSTD_rust_compressContinueState {
callback_context,
window_state: &context.window_projection,
ldm_window_state: ptr::null(),
overflow_state: ptr::null(),
frame_chunk_state: end_frame_chunk_state,
compress_block: compress_stream_test_end,
stage: ptr::addr_of_mut!(context.compression_stage),
consumed_src_size: ptr::addr_of_mut!(context.consumed_src_size),
produced_c_size: ptr::addr_of_mut!(context.produced_c_size),
pledged_src_size_plus_one: 0,
block_size_max,
check_block_size: 0,
no_dict_id_flag: 0,
checksum_flag: 0,
content_size_flag: 0,
format: 0,
window_log: 20,
dict_id: 0,
ldm_enabled: 0,
});
let end_continue_state = context
.end_continue_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
context.end_state = Some(ZSTD_rust_compressEndState {
callback_context,
compress_continue_state: end_continue_state,
trace: compress_stream_test_trace,
consumed_src_size: ptr::addr_of!(context.consumed_src_size),
pledged_src_size_plus_one: 0,
content_size_flag: 0,
stage: ptr::addr_of_mut!(context.compression_stage),
no_dict_id_flag: 0,
checksum_flag: 0,
format: 0,
window_log: 20,
checksum_state: &COMPRESS_END_TEST_CHECKSUM_STATE,
});
let end_state = context
.end_state
.as_ref()
.map_or(ptr::null(), |state| state as *const _);
ZSTD_rust_compressStreamState {
callback_context,
in_buffer_mode,
out_buffer_mode,
stream_stage: stage,
block_size_max,
stable_in_not_consumed,
in_buff,
in_buff_size,
in_to_compress,
in_buff_pos,
in_buff_target,
out_buff,
out_buff_size,
out_buff_content_size,
out_buff_flushed_size,
frame_ended,
compress_continue_state: continue_state,
compress_end_state: end_state,
reset_session: compress_stream_test_reset,
}
}
#[test]
fn compress_stream_stable_short_input_is_deferred_with_a_progress_hint() {
let mut context = CompressStreamTestContext::default();
let mut stage = ZSTD_CSTREAM_STAGE_LOAD;
let mut stable_in_not_consumed = 0;
let mut in_to_compress = 0;
let mut in_buff_pos = 0;
let mut in_buff_target = 0;
let mut out_buff_content_size = 0;
let mut out_buff_flushed_size = 0;
let mut frame_ended = 0;
let state = compress_stream_test_state(
&mut context,
ZSTD_BM_STABLE,
ZSTD_BM_STABLE,
&mut stage,
8,
&mut stable_in_not_consumed,
ptr::null_mut(),
0,
&mut in_to_compress,
&mut in_buff_pos,
&mut in_buff_target,
ptr::null_mut(),
0,
&mut out_buff_content_size,
&mut out_buff_flushed_size,
&mut frame_ended,
);
let source = [1u8, 2, 3];
let mut output = [0xa5u8; 8];
let mut input = ZSTD_inBuffer {
src: source.as_ptr().cast(),
size: source.len(),
pos: 0,
};
let mut output_buffer = ZSTD_outBuffer {
dst: output.as_mut_ptr().cast(),
size: output.len(),
pos: 0,
};
let result = unsafe {
ZSTD_rust_compressStreamGeneric(&state, &mut output_buffer, &mut input, ZSTD_E_CONTINUE)
};
assert_eq!(result, 5);
assert_eq!(input.pos, source.len());
assert_eq!(stable_in_not_consumed, source.len());
assert_eq!(output_buffer.pos, 0);
assert_eq!(context.continue_calls, 0);
assert_eq!(context.end_calls, 0);
assert_eq!(frame_ended, 0);
}
#[test]
fn compress_stream_buffered_output_preserves_pending_flush_and_drains_it() {
let mut context = CompressStreamTestContext {
continue_result: 3,
..CompressStreamTestContext::default()
};
let mut stage = ZSTD_CSTREAM_STAGE_LOAD;
let mut stable_in_not_consumed = 0;
let mut in_to_compress = 0;
let mut in_buff_pos = 0;
let mut in_buff_target = 4;
let mut out_buff_content_size = 0;
let mut out_buff_flushed_size = 0;
let mut frame_ended = 0;
let mut input_buffer = [0u8; 8];
let mut internal_output = [0xa5u8; 8];
let state = compress_stream_test_state(
&mut context,
ZSTD_BM_BUFFERED,
ZSTD_BM_BUFFERED,
&mut stage,
4,
&mut stable_in_not_consumed,
input_buffer.as_mut_ptr().cast(),
input_buffer.len(),
&mut in_to_compress,
&mut in_buff_pos,
&mut in_buff_target,
internal_output.as_mut_ptr().cast(),
internal_output.len(),
&mut out_buff_content_size,
&mut out_buff_flushed_size,
&mut frame_ended,
);
let source = [1u8, 2, 3, 4];
let mut output = [0xa5u8; 2];
let mut input = ZSTD_inBuffer {
src: source.as_ptr().cast(),
size: source.len(),
pos: 0,
};
let mut output_buffer = ZSTD_outBuffer {
dst: output.as_mut_ptr().cast(),
size: output.len(),
pos: 0,
};
let result = unsafe {
ZSTD_rust_compressStreamGeneric(&state, &mut output_buffer, &mut input, ZSTD_E_CONTINUE)
};
assert_eq!(result, 4);
assert_eq!(input.pos, source.len());
assert_eq!(output_buffer.pos, output.len());
assert_eq!(output, [0x5a; 2]);
assert_eq!(out_buff_content_size, 3);
assert_eq!(out_buff_flushed_size, 2);
assert_eq!(stage, ZSTD_CSTREAM_STAGE_FLUSH);
assert_eq!(context.continue_calls, 1);
let mut next_output = [0xa5u8; 2];
output_buffer = ZSTD_outBuffer {
dst: next_output.as_mut_ptr().cast(),
size: next_output.len(),
pos: 0,
};
let result = unsafe {
ZSTD_rust_compressStreamGeneric(&state, &mut output_buffer, &mut input, ZSTD_E_FLUSH)
};
assert_eq!(result, 4);
assert_eq!(next_output[0], 0x5a);
assert_eq!(output_buffer.pos, 1);
assert_eq!((out_buff_content_size, out_buff_flushed_size), (0, 0));
assert_eq!(stage, ZSTD_CSTREAM_STAGE_LOAD);
assert_eq!(context.continue_calls, 1);
}
#[test]
fn compress_stream_end_shortcut_resets_after_direct_completion() {
let mut context = CompressStreamTestContext {
end_result: 5,
..CompressStreamTestContext::default()
};
let mut stage = ZSTD_CSTREAM_STAGE_LOAD;
let mut stable_in_not_consumed = 0;
let mut in_to_compress = 0;
let mut in_buff_pos = 0;
let mut in_buff_target = 4;
let mut out_buff_content_size = 0;
let mut out_buff_flushed_size = 0;
let mut frame_ended = 0;
let mut input_buffer = [0u8; 8];
let mut internal_output = [0u8; 8];
let state = compress_stream_test_state(
&mut context,
ZSTD_BM_BUFFERED,
ZSTD_BM_STABLE,
&mut stage,
4,
&mut stable_in_not_consumed,
input_buffer.as_mut_ptr().cast(),
input_buffer.len(),
&mut in_to_compress,
&mut in_buff_pos,
&mut in_buff_target,
internal_output.as_mut_ptr().cast(),
internal_output.len(),
&mut out_buff_content_size,
&mut out_buff_flushed_size,
&mut frame_ended,
);
let source = [7u8, 8, 9];
let mut output = [0xa5u8; 8];
let mut input = ZSTD_inBuffer {
src: source.as_ptr().cast(),
size: source.len(),
pos: 0,
};
let mut output_buffer = ZSTD_outBuffer {
dst: output.as_mut_ptr().cast(),
size: output.len(),
pos: 0,
};
let result = unsafe {
ZSTD_rust_compressStreamGeneric(&state, &mut output_buffer, &mut input, ZSTD_E_END)
};
assert_eq!(result, 0);
assert_eq!(input.pos, source.len());
assert_eq!(output_buffer.pos, 5);
assert_eq!(&output[..5], &[0x3c; 5]);
assert_eq!(frame_ended, 1);
assert_eq!(context.end_calls, 1);
assert_eq!(context.continue_calls, 0);
assert_eq!(context.reset_calls, 1);
}
#[test]
fn compress_stream_stable_block_error_consumes_input_before_returning() {
let mut context = CompressStreamTestContext {
continue_result: ERROR(ZstdErrorCode::DstSizeTooSmall),
..CompressStreamTestContext::default()
};
let mut stage = ZSTD_CSTREAM_STAGE_LOAD;
let mut stable_in_not_consumed = 0;
let mut in_to_compress = 0;
let mut in_buff_pos = 0;
let mut in_buff_target = 0;
let mut out_buff_content_size = 0;
let mut out_buff_flushed_size = 0;
let mut frame_ended = 0;
let state = compress_stream_test_state(
&mut context,
ZSTD_BM_STABLE,
ZSTD_BM_STABLE,
&mut stage,
4,
&mut stable_in_not_consumed,
ptr::null_mut(),
0,
&mut in_to_compress,
&mut in_buff_pos,
&mut in_buff_target,
ptr::null_mut(),
0,
&mut out_buff_content_size,
&mut out_buff_flushed_size,
&mut frame_ended,
);
let source = [0x11u8; 4];
let mut output = [0xa5u8; 8];
let mut input = ZSTD_inBuffer {
src: source.as_ptr().cast(),
size: source.len(),
pos: 0,
};
let mut output_buffer = ZSTD_outBuffer {
dst: output.as_mut_ptr().cast(),
size: output.len(),
pos: 0,
};
let result = unsafe {
ZSTD_rust_compressStreamGeneric(&state, &mut output_buffer, &mut input, ZSTD_E_CONTINUE)
};
assert_eq!(result, ERROR(ZstdErrorCode::DstSizeTooSmall));
assert_eq!(input.pos, source.len());
assert_eq!(output_buffer.pos, 0);
assert_eq!(context.continue_calls, 1);
assert_eq!(context.end_calls, 0);
assert_eq!(context.reset_calls, 0);
assert_eq!(frame_ended, 0);
}
#[test]
fn compress_stream_rejects_missing_initialization_before_callbacks() {
let mut context = CompressStreamTestContext::default();
let mut stage = ZSTD_CSTREAM_STAGE_INIT;
let mut stable_in_not_consumed = 0;
let mut in_to_compress = 0;
let mut in_buff_pos = 0;
let mut in_buff_target = 0;
let mut out_buff_content_size = 0;
let mut out_buff_flushed_size = 0;
let mut frame_ended = 0;
let state = compress_stream_test_state(
&mut context,
ZSTD_BM_STABLE,
ZSTD_BM_STABLE,
&mut stage,
4,
&mut stable_in_not_consumed,
ptr::null_mut(),
0,
&mut in_to_compress,
&mut in_buff_pos,
&mut in_buff_target,
ptr::null_mut(),
0,
&mut out_buff_content_size,
&mut out_buff_flushed_size,
&mut frame_ended,
);
let mut input = ZSTD_inBuffer {
src: ptr::null(),
size: 0,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: ptr::null_mut(),
size: 0,
pos: 0,
};
let result = unsafe {
ZSTD_rust_compressStreamGeneric(&state, &mut output, &mut input, ZSTD_E_FLUSH)
};
assert_eq!(result, ERROR(ZstdErrorCode::InitMissing));
assert_eq!(context.continue_calls, 0);
assert_eq!(context.end_calls, 0);
assert_eq!(context.reset_calls, 0);
}
#[test]
fn simple_strategy_follows_source_size_tiers() {
assert_eq!(ZSTD_rust_compressCCtxStrategy(0, 1), ZSTD_FAST);
assert_eq!(ZSTD_rust_compressCCtxStrategy(16 * 1024, 5), ZSTD_LAZY);
assert_eq!(
ZSTD_rust_compressCCtxStrategy(16 * 1024 + 1, 5),
ZSTD_GREEDY
);
assert_eq!(ZSTD_rust_compressCCtxStrategy(128 * 1024, 16), ZSTD_BTULTRA);
assert_eq!(ZSTD_rust_compressCCtxStrategy(256 * 1024, 16), ZSTD_BTULTRA);
assert_eq!(
ZSTD_rust_compressCCtxStrategy(256 * 1024 + 1, 16),
ZSTD_BTOPT
);
}
#[test]
fn simple_strategy_preserves_default_and_fast_level_selection() {
assert_eq!(ZSTD_rust_compressCCtxStrategy(64 * 1024, 0), ZSTD_DFAST);
assert_eq!(ZSTD_rust_compressCCtxStrategy(1024 * 1024, -5), ZSTD_FAST);
assert_eq!(
ZSTD_rust_compressCCtxStrategy(1024 * 1024, 22),
ZSTD_BTULTRA2
);
}
#[derive(Default)]
struct SimpleStreamPolicyTestContext {
calls: usize,
src_size: usize,
}
unsafe extern "C" fn simple_stream_policy_test_level(
context: *const c_void,
src_size: usize,
) -> c_int {
let context = unsafe { &mut *context.cast_mut().cast::<SimpleStreamPolicyTestContext>() };
context.calls += 1;
context.src_size = src_size;
7
}
#[test]
fn simple_stream_policy_calls_the_level_leaf_only_for_a_new_frame() {
let mut context = SimpleStreamPolicyTestContext::default();
let cctx = (&mut context as *mut SimpleStreamPolicyTestContext).cast();
let projection = ZSTD_rust_simpleCompressStream2Projection {
stream_stage: ZSTD_CSTREAM_STAGE_INIT,
pledged_src_size_plus_one: 0,
rust_simple_compress2_completed: 0,
};
let result = unsafe {
ZSTD_rust_simpleCompressStream2Policy(
&projection,
cctx,
123,
Some(simple_stream_policy_test_level),
)
};
assert_eq!(result, 7);
assert_eq!(context.calls, 1);
assert_eq!(context.src_size, 123);
}
#[test]
fn simple_stream_policy_rejects_non_initial_or_completed_frames() {
let mut context = SimpleStreamPolicyTestContext::default();
let cctx = (&mut context as *mut SimpleStreamPolicyTestContext).cast();
for projection in [
ZSTD_rust_simpleCompressStream2Projection {
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
..Default::default()
},
ZSTD_rust_simpleCompressStream2Projection {
pledged_src_size_plus_one: 1,
..Default::default()
},
ZSTD_rust_simpleCompressStream2Projection {
rust_simple_compress2_completed: 1,
..Default::default()
},
] {
let result = unsafe {
ZSTD_rust_simpleCompressStream2Policy(
&projection,
cctx,
123,
Some(simple_stream_policy_test_level),
)
};
assert_eq!(result, c_int::MIN);
}
assert_eq!(context.calls, 0);
}
#[test]
fn reduce_table_applies_threshold_and_wrapping_subtraction() {
let mut table = [0, 1, 2, 3, 4, 5, 6, u32::MAX, 0, 0, 0, 0, 0, 0, 0, 0];
reduce_table_internal(&mut table, 3, false);
assert_eq!(&table[..8], &[0, 0, 0, 0, 0, 2, 3, u32::MAX - 3]);
let mut wrapped_threshold = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
reduce_table_internal(&mut wrapped_threshold, u32::MAX, false);
assert_eq!(wrapped_threshold[0], 0);
assert_eq!(wrapped_threshold[1], 2);
assert_eq!(wrapped_threshold[15], 16);
}
#[test]
fn reduce_table_preserves_only_the_btlazy2_mark() {
let mut ordinary = [0u32; ZSTD_ROWSIZE];
ordinary[..3].copy_from_slice(&[ZSTD_DUBT_UNSORTED_MARK, 5, 6]);
reduce_table_internal(&mut ordinary, 3, false);
assert_eq!(&ordinary[..3], &[0, 2, 3]);
let mut btlazy2 = [0u32; ZSTD_ROWSIZE];
btlazy2[..3].copy_from_slice(&[ZSTD_DUBT_UNSORTED_MARK, 5, 6]);
reduce_table_internal(&mut btlazy2, 3, true);
assert_eq!(&btlazy2[..3], &[ZSTD_DUBT_UNSORTED_MARK, 2, 3]);
}
#[test]
fn reduce_table_processes_every_cell_in_multiple_rows() {
let mut table = [0u32; ZSTD_ROWSIZE * 2];
table[0] = 2;
table[ZSTD_ROWSIZE - 1] = 8;
table[ZSTD_ROWSIZE] = 1;
table[ZSTD_ROWSIZE * 2 - 1] = u32::MAX;
reduce_table_internal(&mut table, 4, false);
assert_eq!(table[0], 0);
assert_eq!(table[ZSTD_ROWSIZE - 1], 4);
assert_eq!(table[ZSTD_ROWSIZE], 0);
assert_eq!(table[ZSTD_ROWSIZE * 2 - 1], u32::MAX - 4);
}
#[test]
fn reduce_index_accepts_zero_sized_optional_tables() {
let mut hash_table = [0u32; ZSTD_ROWSIZE];
hash_table[..4].copy_from_slice(&[1, 2, 5, u32::MAX]);
unsafe {
ZSTD_rust_reduceIndex(
hash_table.as_mut_ptr(),
hash_table.len() as u32,
ptr::null_mut(),
0,
ptr::null_mut(),
0,
3,
0,
);
ZSTD_rust_reduceIndex(
ptr::null_mut(),
0,
ptr::null_mut(),
0,
ptr::null_mut(),
0,
u32::MAX,
1,
);
}
assert_eq!(&hash_table[..4], &[0, 0, 2, u32::MAX - 3]);
}
#[test]
fn reduce_index_preserves_the_marker_only_in_the_chain_table() {
let mut hash_table = [0u32; ZSTD_ROWSIZE];
let mut chain_table = [0u32; ZSTD_ROWSIZE];
let mut hash_table3 = [0u32; ZSTD_ROWSIZE];
hash_table[0] = ZSTD_DUBT_UNSORTED_MARK;
chain_table[0] = ZSTD_DUBT_UNSORTED_MARK;
hash_table3[0] = ZSTD_DUBT_UNSORTED_MARK;
unsafe {
ZSTD_rust_reduceIndex(
hash_table.as_mut_ptr(),
hash_table.len() as u32,
chain_table.as_mut_ptr(),
chain_table.len() as u32,
hash_table3.as_mut_ptr(),
hash_table3.len() as u32,
3,
1,
);
}
assert_eq!(hash_table[0], 0);
assert_eq!(chain_table[0], ZSTD_DUBT_UNSORTED_MARK);
assert_eq!(hash_table3[0], 0);
}
#[test]
fn reduce_index_for_match_state_selects_chain_and_hash3_tables() {
let mut hash_table = [0u32; ZSTD_ROWSIZE];
let mut chain_table = [0u32; ZSTD_ROWSIZE];
let mut hash_table3 = [0u32; ZSTD_ROWSIZE];
hash_table[0] = ZSTD_DUBT_UNSORTED_MARK;
chain_table[0] = ZSTD_DUBT_UNSORTED_MARK;
hash_table3[0] = ZSTD_DUBT_UNSORTED_MARK;
unsafe {
ZSTD_rust_reduceIndexForMatchState(
hash_table.as_mut_ptr(),
4,
chain_table.as_mut_ptr(),
4,
hash_table3.as_mut_ptr(),
4,
ZSTD_BTLAZY2,
ZSTD_RUST_PS_DISABLE,
0,
3,
);
}
assert_eq!(hash_table[0], 0);
assert_eq!(chain_table[0], ZSTD_DUBT_UNSORTED_MARK);
assert_eq!(hash_table3[0], 0);
}
#[test]
fn copy_cdict_table_removes_short_cache_tags() {
let source = [0x1234_56ff, 0xdead_beef, 0x0000_0100, u32::MAX];
let mut destination = [0u32; 4];
unsafe {
ZSTD_rust_copyCDictTableIntoCCtx(
destination.as_mut_ptr(),
source.as_ptr(),
source.len(),
1,
);
}
assert_eq!(destination, [0x0012_3456, 0x00de_adbe, 1, 0x00ff_ffff]);
}
#[test]
fn copy_cdict_table_preserves_untagged_entries() {
let source = [0, 2, 0x1234_5678, u32::MAX];
let mut destination = [0xa5a5_a5a5; 4];
unsafe {
ZSTD_rust_copyCDictTableIntoCCtx(
destination.as_mut_ptr(),
source.as_ptr(),
source.len(),
0,
);
}
assert_eq!(destination, source);
}
#[test]
fn bitmix_and_hash_salt_match_the_c_arithmetic() {
assert_eq!(bitmix(0x0123_4567_89ab_cdef, 8), 0xd498_d855_4e8d_d8cb);
assert_eq!(
advance_hash_salt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210),
0xe5ee_f172_e5ff_3e57
);
assert_eq!(
ZSTD_rust_advanceHashSalt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210),
0xe5ee_f172_e5ff_3e57
);
let mut hash_salt = 0x0123_4567_89ab_cdef;
unsafe { ZSTD_rust_advanceHashSaltInPlace(&mut hash_salt, 0xfedc_ba98_7654_3210) };
assert_eq!(hash_salt, 0xe5ee_f172_e5ff_3e57);
}
#[test]
fn index_too_close_to_max_uses_a_strict_margin_boundary() {
let threshold = ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN;
assert!(!index_too_close_to_max(threshold));
assert!(index_too_close_to_max(threshold + 1));
assert_eq!(ZSTD_indexTooCloseToMax(threshold), 0);
assert_eq!(ZSTD_indexTooCloseToMax(threshold + 1), 1);
}
#[derive(Default)]
struct OverflowCorrectionTestContext {
events: Vec<&'static str>,
should_correct: c_int,
correction: c_uint,
loaded_dict_end: c_uint,
dict_match_state: *const c_void,
}
unsafe fn overflow_correction_test_context(
context: *mut c_void,
) -> &'static mut OverflowCorrectionTestContext {
unsafe { &mut *context.cast::<OverflowCorrectionTestContext>() }
}
unsafe extern "C" fn overflow_correction_test_need(
context: *mut c_void,
_src: *const c_void,
_src_end: *const c_void,
) -> c_int {
let context = unsafe { overflow_correction_test_context(context) };
context.events.push("need");
context.should_correct
}
unsafe extern "C" fn overflow_correction_test_correct(
context: *mut c_void,
_src: *const c_void,
) -> c_uint {
let context = unsafe { overflow_correction_test_context(context) };
context.events.push("correct");
context.correction
}
unsafe extern "C" fn overflow_correction_test_mark_dirty(context: *mut c_void) {
unsafe { overflow_correction_test_context(context) }
.events
.push("dirty");
}
unsafe extern "C" fn overflow_correction_test_reduce(
context: *mut c_void,
_correction: c_uint,
) {
unsafe { overflow_correction_test_context(context) }
.events
.push("reduce");
}
unsafe extern "C" fn overflow_correction_test_mark_clean(context: *mut c_void) {
unsafe { overflow_correction_test_context(context) }
.events
.push("clean");
}
fn overflow_correction_test_state(
context: &mut OverflowCorrectionTestContext,
next_to_update: &mut c_uint,
) -> ZSTD_rust_overflowCorrectState {
ZSTD_rust_overflowCorrectState {
callback_context: (context as *mut OverflowCorrectionTestContext).cast(),
next_to_update,
need_correction: Some(overflow_correction_test_need),
correct_overflow: Some(overflow_correction_test_correct),
mark_tables_dirty: Some(overflow_correction_test_mark_dirty),
reduce_index: Some(overflow_correction_test_reduce),
mark_tables_clean: Some(overflow_correction_test_mark_clean),
loaded_dict_end: &mut context.loaded_dict_end,
dict_match_state: &mut context.dict_match_state,
}
}
#[test]
fn overflow_correction_preserves_order_and_saturates_next_to_update() {
let mut context = OverflowCorrectionTestContext {
should_correct: 1,
correction: 10,
loaded_dict_end: 33,
dict_match_state: ptr::dangling(),
..Default::default()
};
let mut next_to_update = 7;
let state = overflow_correction_test_state(&mut context, &mut next_to_update);
unsafe {
ZSTD_rust_overflowCorrectIfNeeded(&state, ptr::null(), ptr::null());
}
assert_eq!(next_to_update, 0);
assert_eq!(
context.events,
["need", "correct", "dirty", "reduce", "clean"]
);
assert_eq!(context.loaded_dict_end, 0);
assert!(context.dict_match_state.is_null());
}
#[test]
fn overflow_correction_skips_callbacks_when_window_is_safe() {
let mut context = OverflowCorrectionTestContext::default();
let mut next_to_update = 123;
let state = overflow_correction_test_state(&mut context, &mut next_to_update);
unsafe {
ZSTD_rust_overflowCorrectIfNeeded(&state, ptr::null(), ptr::null());
}
assert_eq!(next_to_update, 123);
assert_eq!(context.events, ["need"]);
}
#[test]
fn window_correction_handles_current_cycle_start_boundary() {
assert_eq!(window_correct_overflow(0x100, 3, 8), 0xf0);
assert_eq!(window_correct_overflow(0x101, 3, 8), 0xf0);
assert_eq!(window_correct_overflow(0x102, 3, 8), 0xf8);
assert_eq!(
ZSTD_rust_windowCorrectOverflow(0x100, 3, 8),
window_correct_overflow(0x100, 3, 8)
);
}
#[test]
fn window_correction_selects_larger_of_cycle_and_max_distance() {
assert_eq!(window_correct_overflow(0x100, 5, 8), 0xc0);
assert_eq!(window_correct_overflow(0x200, 5, 64), 0x1a0);
}
#[test]
fn window_correction_wraps_u32_intermediates() {
assert_eq!(window_correct_overflow(0, 0, 1), u32::MAX - 2);
assert_eq!(
window_correct_overflow(0x8000_0000, 31, 0x8000_0000),
0x8000_0000
);
assert_eq!(
ZSTD_rust_windowCorrectOverflow(0x8000_0000, 31, 0x8000_0000),
0x8000_0000
);
}
#[test]
fn window_overflow_need_uses_the_explicit_current_max_boundary() {
assert!(!window_need_overflow_correction(1024, false, 1024, false));
assert!(window_need_overflow_correction(1025, false, 1024, false));
assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1024, 1, 1024, 0), 0);
assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1025, 0, 1024, 0), 1);
}
#[test]
fn window_overflow_need_honors_frequent_dictionary_policy() {
let can_correct = window_can_overflow_correct(27, 3, 16, 0, 0);
let dictionary_still_valid = window_can_overflow_correct(27, 3, 16, 20, 0);
assert!(can_correct);
assert!(!dictionary_still_valid);
assert!(window_need_overflow_correction(27, can_correct, 1024, true));
assert!(!window_need_overflow_correction(
27,
dictionary_still_valid,
1024,
true
));
assert_eq!(
ZSTD_rust_windowNeedOverflowCorrection(27, can_correct as u32, 1024, 1),
1
);
assert_eq!(
ZSTD_rust_windowNeedOverflowCorrection(27, dictionary_still_valid as u32, 1024, 1,),
0
);
assert!(!window_need_overflow_correction(27, true, 1024, false));
}
#[test]
fn limit_next_to_update_preserves_threshold_and_bounded_catch_up() {
assert_eq!(limit_next_to_update(884, 500), 500);
assert_eq!(limit_next_to_update(885, 500), 884);
assert_eq!(limit_next_to_update(1000, 500), 884);
assert_eq!(limit_next_to_update(1200, 500), 1008);
assert_eq!(ZSTD_rust_limitNextToUpdate(300, u32::MAX - 100), 283);
}
#[test]
fn window_overflow_need_scales_the_frequency_with_correction_count() {
assert!(window_can_overflow_correct(27, 3, 16, 0, 0));
assert!(!window_can_overflow_correct(27, 3, 16, 0, 1));
assert!(window_can_overflow_correct(53, 3, 16, 0, 1));
assert_eq!(ZSTD_rust_windowCanOverflowCorrect(53, 3, 16, 0, 1), 1);
}
#[test]
fn window_overflow_need_preserves_u32_wrap_boundaries() {
assert!(window_can_overflow_correct(
3,
31,
0x8000_0000,
0x8000_0000,
0,
));
assert!(window_can_overflow_correct(27, 3, 16, 0, u32::MAX,));
assert_eq!(
ZSTD_rust_windowCanOverflowCorrect(3, 31, 0x8000_0000, 0x8000_0000, 0),
1
);
}
#[test]
fn dict_too_big_uses_a_strict_chunk_size_boundary() {
assert!(!dict_too_big(0));
assert!(!dict_too_big(ZSTD_CHUNKSIZE_MAX));
assert!(dict_too_big(ZSTD_CHUNKSIZE_MAX + 1));
assert_eq!(ZSTD_dictTooBig(ZSTD_CHUNKSIZE_MAX), 0);
assert_eq!(ZSTD_dictTooBig(ZSTD_CHUNKSIZE_MAX + 1), 1);
}
#[test]
fn target_block_policy_requires_compression_and_nonfirst_rle() {
assert_eq!(
target_c_block_size_action(0, 0, 1, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Rle
);
assert_eq!(
target_c_block_size_action(0, 1, 1, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(0, 0, 0, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(0, 0, 1, 0, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
ZSTD_rust_targetCBlockSizeAction(0, 0, 1, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Rle as c_int
);
}
#[test]
fn target_block_policy_falls_back_for_uncompressed_and_small_results() {
assert_eq!(
target_c_block_size_action(1, 0, 0, 0, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
ERROR(ZstdErrorCode::DstSizeTooSmall),
128,
ZSTD_FAST,
),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
ERROR(ZstdErrorCode::Generic),
128,
ZSTD_FAST,
),
TargetCBlockAction::Error
);
assert_eq!(
target_c_block_size_action(ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, 0, 128, ZSTD_FAST,),
TargetCBlockAction::Raw
);
}
#[test]
fn target_block_policy_uses_a_strict_three_byte_header_boundary() {
let src_size = 128;
let max_c_size = src_size - min_gain(src_size, ZSTD_FAST);
let compressed = max_c_size + ZSTD_BLOCK_HEADER_SIZE - 1;
let raw = max_c_size + ZSTD_BLOCK_HEADER_SIZE;
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
compressed,
src_size,
ZSTD_FAST,
),
TargetCBlockAction::Compressed
);
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
raw,
src_size,
ZSTD_FAST,
),
TargetCBlockAction::Raw
);
assert_eq!(
ZSTD_rust_targetCBlockSizeAction(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
compressed,
src_size,
ZSTD_FAST,
),
TargetCBlockAction::Compressed as c_int
);
}
#[allow(clippy::too_many_arguments)]
unsafe extern "C" fn target_block_test_superblock_error(
_seq_store: *const c_void,
_prev_cblock: *const c_void,
_next_cblock: *mut c_void,
_strategy: c_int,
_disable_literal_compression: c_int,
_workspace: *mut c_void,
_wksp_size: usize,
_bmi2: c_int,
_window_log: c_uint,
_target_cblock_size: usize,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
_last_block: c_uint,
) -> usize {
ERROR(ZstdErrorCode::Generic)
}
#[allow(clippy::too_many_arguments)]
unsafe extern "C" fn target_block_test_superblock_empty(
_seq_store: *const c_void,
_prev_cblock: *const c_void,
_next_cblock: *mut c_void,
_strategy: c_int,
_disable_literal_compression: c_int,
_workspace: *mut c_void,
_wksp_size: usize,
_bmi2: c_int,
_window_log: c_uint,
_target_cblock_size: usize,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
_last_block: c_uint,
) -> usize {
0
}
#[allow(clippy::too_many_arguments)]
unsafe extern "C" fn target_block_test_superblock_too_small(
_seq_store: *const c_void,
_prev_cblock: *const c_void,
_next_cblock: *mut c_void,
_strategy: c_int,
_disable_literal_compression: c_int,
_workspace: *mut c_void,
_wksp_size: usize,
_bmi2: c_int,
_window_log: c_uint,
_target_cblock_size: usize,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
_last_block: c_uint,
) -> usize {
ERROR(ZstdErrorCode::DstSizeTooSmall)
}
#[allow(clippy::too_many_arguments)]
unsafe extern "C" fn target_block_test_superblock_compressed(
_seq_store: *const c_void,
_prev_cblock: *const c_void,
_next_cblock: *mut c_void,
_strategy: c_int,
_disable_literal_compression: c_int,
_workspace: *mut c_void,
_wksp_size: usize,
_bmi2: c_int,
_window_log: c_uint,
_target_cblock_size: usize,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
_last_block: c_uint,
) -> usize {
1
}
fn target_block_test_state(
seq_store: &mut SeqStore_t,
prev_block: &mut ZSTD_compressedBlockState_t,
next_block: &mut ZSTD_compressedBlockState_t,
prev_c_block: &mut *mut ZSTD_compressedBlockState_t,
next_c_block: &mut *mut ZSTD_compressedBlockState_t,
is_first_block: c_int,
) -> ZSTD_rust_targetCBlockSizeState {
*prev_c_block = prev_block as *mut ZSTD_compressedBlockState_t;
*next_c_block = next_block as *mut ZSTD_compressedBlockState_t;
ZSTD_rust_targetCBlockSizeState {
seq_store,
prev_c_block,
next_c_block,
tmp_workspace: ptr::null_mut(),
tmp_wksp_size: 0,
strategy: ZSTD_FAST,
disable_literal_compression: 0,
bmi2: 0,
window_log: 20,
target_c_block_size: 0,
is_first_block,
}
}
fn target_block_test_seq_store() -> (SeqStore_t, [SeqDef; 1], [u8; 16]) {
let mut sequences = [SeqDef::default(); 1];
let mut literals = [0u8; 16];
let sequences_start = sequences.as_mut_ptr();
let literals_start = literals.as_mut_ptr();
let seq_store = SeqStore_t {
sequencesStart: sequences_start,
sequences: sequences_start,
litStart: literals_start,
lit: literals_start,
llCode: ptr::null_mut(),
mlCode: ptr::null_mut(),
ofCode: ptr::null_mut(),
maxNbSeq: sequences.len(),
maxNbLit: literals.len(),
longLengthType: 0,
longLengthPos: 0,
};
(seq_store, sequences, literals)
}
#[test]
fn target_block_body_emits_rle_only_for_nonfirst_repeated_blocks() {
let (mut seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let state = target_block_test_state(
&mut seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
0,
);
let source = [0x5au8; 16];
let mut output = [0xa5u8; 4];
let result = unsafe {
compress_block_target_c_block_size_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
1,
target_block_test_superblock_error,
)
};
assert_eq!(result, 4);
assert_eq!(output[3], source[0]);
}
#[test]
fn target_block_body_suppresses_first_block_rle_and_propagates_superblock_error() {
let (mut seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let state = target_block_test_state(
&mut seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
1,
);
let source = [0x5au8; 16];
let mut output = [0xa5u8; 16];
let result = unsafe {
compress_block_target_c_block_size_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
1,
target_block_test_superblock_error,
)
};
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
assert_eq!(output, [0xa5u8; 16]);
}
#[test]
fn target_block_body_falls_back_to_raw_for_empty_or_small_superblocks() {
let (mut seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let state = target_block_test_state(
&mut seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
1,
);
let source = *b"raw fallback";
for superblock in [
target_block_test_superblock_empty as TargetCBlockSuperBlockFn,
target_block_test_superblock_too_small,
] {
let mut output = [0xa5u8; 32];
let result = unsafe {
compress_block_target_c_block_size_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
superblock,
)
};
assert_eq!(result, source.len() + ZSTD_BLOCK_HEADER_SIZE);
assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..result], &source);
}
}
#[test]
fn target_block_body_swaps_state_only_for_compressed_output() {
let (mut seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = &mut prev_block as *mut ZSTD_compressedBlockState_t;
let mut next_c_block = &mut next_block as *mut ZSTD_compressedBlockState_t;
let state = target_block_test_state(
&mut seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
1,
);
let prev_ptr = prev_c_block;
let next_ptr = next_c_block;
let source = [0x3cu8; 128];
let mut output = [0xa5u8; 128];
let result = unsafe {
compress_block_target_c_block_size_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
target_block_test_superblock_compressed,
)
};
assert_eq!(result, 1);
assert_eq!(unsafe { *state.prev_c_block }, next_ptr);
assert_eq!(unsafe { *state.next_c_block }, prev_ptr);
assert_eq!(output, [0xa5u8; 128]);
}
#[test]
fn frame_progress_unknown_pledge_updates_counters() {
let mut consumed = 7;
let mut produced = 11;
let result =
unsafe { ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 0, 5, 13, 2) };
assert_eq!(result, 0);
assert_eq!(consumed, 12);
assert_eq!(produced, 26);
}
#[test]
fn frame_progress_exact_pledge_is_accepted() {
let mut consumed = 7;
let mut produced = 11;
let result =
unsafe { ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 13, 5, 13, 2) };
assert_eq!(result, 0);
assert_eq!(consumed, 12);
assert_eq!(produced, 26);
}
#[test]
fn frame_progress_one_byte_overrun_is_reported() {
let mut consumed = 7;
let mut produced = 11;
let result =
unsafe { ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 13, 6, 13, 2) };
assert_eq!(result, 1);
assert_eq!(consumed, 13);
}
#[test]
fn frame_progress_counters_update_before_overrun_result() {
let mut consumed = 100;
let mut produced = 200;
let result = unsafe {
ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 106, 7, 17, 3)
};
assert_eq!(result, 1);
assert_eq!(consumed, 107);
assert_eq!(produced, 220);
}
#[test]
fn frame_progression_constructs_single_thread_values() {
let consumed = 11_u64;
let buffered = 37_usize;
let produced = 53_u64;
let expected = ZSTD_frameProgression {
ingested: consumed + buffered as u64,
consumed,
produced,
flushed: produced,
currentJobID: 0,
nbActiveWorkers: 0,
};
assert_eq!(frame_progression(consumed, buffered, produced), expected);
assert_eq!(
ZSTD_rust_frameProgression(consumed, buffered, produced),
expected
);
}
#[test]
fn frame_progression_wraps_buffered_input_to_u64() {
let consumed = u64::MAX - 3;
let buffered = 8_usize;
let progression = ZSTD_rust_frameProgression(consumed, buffered, 17);
assert_eq!(progression.ingested, consumed.wrapping_add(buffered as u64));
assert_eq!(progression.consumed, consumed);
assert_eq!(progression.produced, 17);
assert_eq!(progression.flushed, 17);
assert_eq!(progression.currentJobID, 0);
assert_eq!(progression.nbActiveWorkers, 0);
}
unsafe extern "C" fn to_flush_now_test_callback(context: *mut c_void) -> usize {
let calls = unsafe { &mut *context.cast::<usize>() };
*calls += 1;
17
}
#[test]
fn to_flush_now_only_calls_mt_callback_for_workers() {
let mut calls: usize = 0;
let mut state = ZSTD_rust_toFlushNowState {
callback_context: &mut calls as *mut usize as *mut c_void,
nb_workers: 0,
to_flush_now: to_flush_now_test_callback,
};
assert_eq!(unsafe { ZSTD_rust_toFlushNow(&state) }, 0);
assert_eq!(calls, 0);
state.nb_workers = 1;
assert_eq!(unsafe { ZSTD_rust_toFlushNow(&state) }, 17);
assert_eq!(calls, 1);
}
#[test]
fn to_flush_now_rejects_a_null_state() {
assert_eq!(
unsafe { ZSTD_rust_toFlushNow(ptr::null()) },
ERROR(ZstdErrorCode::Generic)
);
}
#[test]
fn next_input_size_hint_uses_remaining_stable_block_capacity() {
let hint = ZSTD_rust_nextInputSizeHint(ZSTD_BM_STABLE, 256, 37, 99, 12);
assert_eq!(next_input_size_hint(ZSTD_BM_STABLE, 256, 37, 99, 12), 219);
assert_eq!(hint, 219);
}
#[test]
fn next_input_size_hint_replaces_empty_buffered_hint_with_block_size() {
assert_eq!(
ZSTD_rust_nextInputSizeHint(ZSTD_BM_BUFFERED, 256, 37, 128, 128),
256
);
}
#[test]
fn next_input_size_hint_returns_nonzero_buffered_hint() {
assert_eq!(
ZSTD_rust_nextInputSizeHint(ZSTD_BM_BUFFERED, 256, 37, 128, 32),
96
);
}
unsafe extern "C" fn next_input_size_hint_mt_or_st_test_callback(
context: *mut c_void,
) -> usize {
let calls = unsafe { &mut *context.cast::<usize>() };
*calls += 1;
777
}
fn next_input_size_hint_mt_or_st_test_state(
nb_workers: c_int,
calls: &mut usize,
) -> ZSTD_rust_nextInputSizeHintMTorSTState {
ZSTD_rust_nextInputSizeHintMTorSTState {
block_size_max: 256,
stable_in_not_consumed: 37,
in_buff_target: 128,
in_buff_pos: 32,
nb_workers,
in_buffer_mode: ZSTD_BM_BUFFERED,
mt_context: calls as *mut usize as *mut c_void,
mt_next_input_size_hint: Some(next_input_size_hint_mt_or_st_test_callback),
}
}
#[test]
fn next_input_size_hint_mt_or_st_dispatches_to_mt_callback_for_workers() {
let mut calls = 0;
let state = next_input_size_hint_mt_or_st_test_state(1, &mut calls);
assert_eq!(unsafe { ZSTD_rust_nextInputSizeHintMTorST(&state) }, 777);
assert_eq!(calls, 1);
}
#[test]
fn next_input_size_hint_mt_or_st_uses_single_thread_leaf_without_workers() {
let mut calls = 0;
let state = next_input_size_hint_mt_or_st_test_state(0, &mut calls);
assert_eq!(unsafe { ZSTD_rust_nextInputSizeHintMTorST(&state) }, 96);
assert_eq!(calls, 0);
}
#[test]
fn mt_next_input_size_hint_handles_empty_input_buffer() {
assert_eq!(mt_next_input_size_hint(128, 0), 128);
assert_eq!(ZSTDMT_rust_nextInputSizeHint(128, 0), 128);
}
#[test]
fn mt_next_input_size_hint_returns_remaining_capacity() {
assert_eq!(mt_next_input_size_hint(128, 37), 91);
assert_eq!(ZSTDMT_rust_nextInputSizeHint(128, 37), 91);
}
#[test]
fn mt_next_input_size_hint_replaces_full_buffer_with_target_size() {
assert_eq!(ZSTDMT_rust_nextInputSizeHint(128, 128), 128);
}
#[test]
fn mt_next_input_size_hint_preserves_zero_target_behavior() {
assert_eq!(ZSTDMT_rust_nextInputSizeHint(0, 0), 0);
}
#[test]
fn mt_next_input_size_hint_preserves_wrapped_overfill() {
assert_eq!(ZSTDMT_rust_nextInputSizeHint(3, 4), usize::MAX);
}
#[test]
fn mt_sizeof_cctx_handles_zero_components() {
assert_eq!(mt_sizeof_cctx(0, 0, 0, 0, 0, 0, 0, 0), 0);
assert_eq!(ZSTDMT_rust_sizeofCCtx(0, 0, 0, 0, 0, 0, 0, 0), 0);
}
#[test]
fn mt_sizeof_cctx_adds_all_components_in_order() {
assert_eq!(mt_sizeof_cctx(1, 2, 3, 4, 5, 6, 7, 8), 36);
assert_eq!(ZSTDMT_rust_sizeofCCtx(1, 2, 3, 4, 5, 6, 7, 8), 36);
}
#[test]
fn mt_sizeof_cctx_wraps_like_c_size_t_addition() {
assert_eq!(mt_sizeof_cctx(usize::MAX, 1, 2, 3, 4, 5, 6, 7), 27);
assert_eq!(ZSTDMT_rust_sizeofCCtx(usize::MAX, 1, 2, 3, 4, 5, 6, 7), 27);
}
#[test]
fn in_buffer_for_end_flush_returns_stable_expected_buffer() {
let expected_src = b"input".as_ptr().cast::<c_void>();
let result = ZSTD_rust_inBufferForEndFlush(ZSTD_BM_STABLE, expected_src, 37, 11);
assert_eq!(result.src, expected_src);
assert_eq!(result.size, 37);
assert_eq!(result.pos, 11);
let null_result = ZSTD_rust_inBufferForEndFlush(ZSTD_BM_STABLE, ptr::null(), 37, 11);
assert!(null_result.src.is_null());
assert_eq!(null_result.size, 37);
assert_eq!(null_result.pos, 11);
}
#[test]
fn in_buffer_for_end_flush_clears_buffered_and_other_modes() {
let expected_src = b"input".as_ptr().cast::<c_void>();
for mode in [ZSTD_BM_BUFFERED, 42] {
let result = ZSTD_rust_inBufferForEndFlush(mode, expected_src, 37, 11);
assert!(result.src.is_null());
assert_eq!(result.size, 0);
assert_eq!(result.pos, 0);
}
}
#[test]
fn end_stream_remaining_ignores_estimate_components_after_frame_end() {
assert_eq!(end_stream_remaining(17, 1, 1), 17);
assert_eq!(ZSTD_rust_endStreamRemaining(17, 1, 1), 17);
}
#[test]
fn end_stream_remaining_adds_block_header_without_checksum() {
assert_eq!(end_stream_remaining(17, 0, 0), 20);
assert_eq!(ZSTD_rust_endStreamRemaining(17, 0, 0), 20);
}
#[test]
fn end_stream_remaining_adds_block_header_and_checksum() {
assert_eq!(end_stream_remaining(17, 0, 1), 24);
assert_eq!(ZSTD_rust_endStreamRemaining(17, 0, 1), 24);
}
#[test]
fn end_stream_remaining_wraps_size_t_additions() {
assert_eq!(end_stream_remaining(usize::MAX, 0, 1), 6);
assert_eq!(ZSTD_rust_endStreamRemaining(usize::MAX, 0, 1), 6);
}
#[test]
fn check_buffer_stability_accepts_matching_stable_input() {
let expected_src = b"input".as_ptr().cast::<c_void>();
assert_eq!(
check_buffer_stability(
ZSTD_BM_STABLE,
ZSTD_BM_BUFFERED,
expected_src,
11,
expected_src,
11,
0,
37,
5,
),
0
);
assert_eq!(
ZSTD_rust_checkBufferStability(
ZSTD_BM_STABLE,
ZSTD_BM_BUFFERED,
expected_src,
11,
expected_src,
11,
0,
37,
5,
),
0
);
}
#[test]
fn check_buffer_stability_rejects_changed_input_pointer_or_position() {
let expected_src = b"input".as_ptr().cast::<c_void>();
let other_src = b"other".as_ptr().cast::<c_void>();
let error = ERROR(ZstdErrorCode::StabilityConditionNotRespected);
assert_eq!(
ZSTD_rust_checkBufferStability(
ZSTD_BM_STABLE,
ZSTD_BM_BUFFERED,
expected_src,
11,
other_src,
11,
0,
37,
5,
),
error
);
assert_eq!(
ZSTD_rust_checkBufferStability(
ZSTD_BM_STABLE,
ZSTD_BM_BUFFERED,
expected_src,
11,
expected_src,
12,
0,
37,
5,
),
error
);
}
#[test]
fn check_buffer_stability_ignores_input_changes_in_buffered_mode() {
let expected_src = b"input".as_ptr().cast::<c_void>();
let other_src = b"other".as_ptr().cast::<c_void>();
assert_eq!(
ZSTD_rust_checkBufferStability(
ZSTD_BM_BUFFERED,
ZSTD_BM_BUFFERED,
expected_src,
11,
other_src,
12,
0,
37,
5,
),
0
);
}
#[test]
fn check_buffer_stability_validates_stable_output_remainder() {
let expected_src = b"input".as_ptr().cast::<c_void>();
let error = ERROR(ZstdErrorCode::StabilityConditionNotRespected);
assert_eq!(
ZSTD_rust_checkBufferStability(
ZSTD_BM_BUFFERED,
ZSTD_BM_STABLE,
expected_src,
0,
ptr::null(),
0,
32,
40,
8,
),
0
);
assert_eq!(
ZSTD_rust_checkBufferStability(
ZSTD_BM_BUFFERED,
ZSTD_BM_STABLE,
expected_src,
0,
ptr::null(),
0,
32,
40,
7,
),
error
);
assert_eq!(
ZSTD_rust_checkBufferStability(
ZSTD_BM_BUFFERED,
ZSTD_BM_STABLE,
expected_src,
0,
ptr::null(),
0,
usize::MAX,
3,
4,
),
0
);
}
#[test]
fn set_buffer_expectations_updates_only_enabled_stable_modes() {
let expected_src = b"expected".as_ptr().cast::<c_void>();
let input_src = b"input".as_ptr().cast::<c_void>();
let mut expected_input = ZSTD_inBuffer {
src: expected_src,
size: 3,
pos: 1,
};
let input = ZSTD_inBuffer {
src: input_src,
size: 11,
pos: 7,
};
let output = ZSTD_outBuffer {
dst: ptr::null_mut(),
size: 40,
pos: 9,
};
let mut expected_output_size = 5;
unsafe {
ZSTD_rust_setBufferExpectations(
ZSTD_BM_STABLE,
ZSTD_BM_STABLE,
&mut expected_input,
&mut expected_output_size,
&output,
&input,
);
}
assert_eq!(expected_input.src, input.src);
assert_eq!(expected_input.size, input.size);
assert_eq!(expected_input.pos, input.pos);
assert_eq!(expected_output_size, 31);
let original_input_src = expected_input.src;
let original_input_size = expected_input.size;
let original_input_pos = expected_input.pos;
let original_output_size = expected_output_size;
let other_input = ZSTD_inBuffer {
src: expected_src,
size: 99,
pos: 22,
};
let other_output = ZSTD_outBuffer {
dst: ptr::null_mut(),
size: 3,
pos: 8,
};
unsafe {
ZSTD_rust_setBufferExpectations(
ZSTD_BM_BUFFERED,
ZSTD_BM_BUFFERED,
&mut expected_input,
&mut expected_output_size,
&other_output,
&other_input,
);
}
assert_eq!(expected_input.src, original_input_src);
assert_eq!(expected_input.size, original_input_size);
assert_eq!(expected_input.pos, original_input_pos);
assert_eq!(expected_output_size, original_output_size);
}
#[test]
fn sequence_api_plan_places_input_and_frame_checksums_around_blocks() {
assert_eq!(
sequence_api_plan(false, ZSTD_SF_NO_BLOCK_DELIMITERS, 1, 1),
Ok(SequenceApiPlan {
update_input_checksum: true,
append_frame_checksum: true,
})
);
}
#[test]
fn sequence_api_plan_disables_checksums_for_literals_variant() {
assert_eq!(
sequence_api_plan(true, ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS, 0, 0),
Ok(SequenceApiPlan {
update_input_checksum: false,
append_frame_checksum: false,
})
);
}
#[test]
fn sequence_api_plan_preserves_literals_validation_precedence() {
assert_eq!(
ERR_getErrorCode(
sequence_api_plan(true, ZSTD_SF_NO_BLOCK_DELIMITERS, 1, 1).unwrap_err()
),
ZstdErrorCode::FrameParameterUnsupported as i32
);
assert_eq!(
ERR_getErrorCode(
sequence_api_plan(true, ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS, 1, 1).unwrap_err()
),
ZstdErrorCode::ParameterUnsupported as i32
);
assert_eq!(
ERR_getErrorCode(
sequence_api_plan(true, ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS, 0, 1).unwrap_err()
),
ZstdErrorCode::FrameParameterUnsupported as i32
);
}
#[test]
fn sequence_api_literal_capacity_check_precedes_initialization() {
assert_eq!(
sequence_api_validate_literal_capacity(9, 8),
ERROR(ZstdErrorCode::WorkSpaceTooSmall)
);
assert_eq!(sequence_api_validate_literal_capacity(8, 9), 0);
}
#[test]
fn sequence_copier_selector_returns_no_delimiters_mode() {
assert_eq!(
select_sequence_copier(ZSTD_SF_NO_BLOCK_DELIMITERS),
ZSTD_SF_NO_BLOCK_DELIMITERS
);
assert_eq!(
ZSTD_rust_selectSequenceCopier(ZSTD_SF_NO_BLOCK_DELIMITERS),
ZSTD_SF_NO_BLOCK_DELIMITERS
);
}
#[test]
fn sequence_copier_selector_returns_explicit_delimiters_mode() {
assert_eq!(
select_sequence_copier(ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS),
ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS
);
assert_eq!(
ZSTD_rust_selectSequenceCopier(ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS),
ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS
);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn sequence_copier_selector_rejects_invalid_mode_in_debug() {
let _ = select_sequence_copier(-1);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn sequence_copier_selector_rejects_unsupported_mode_in_debug() {
let _ = select_sequence_copier(2);
}
#[cfg(not(debug_assertions))]
#[test]
fn sequence_copier_selector_falls_back_to_no_delimiters_in_release() {
assert_eq!(
ZSTD_rust_selectSequenceCopier(-1),
ZSTD_SF_NO_BLOCK_DELIMITERS
);
assert_eq!(
ZSTD_rust_selectSequenceCopier(2),
ZSTD_SF_NO_BLOCK_DELIMITERS
);
}
#[test]
fn sequence_block_action_keeps_first_block_from_using_rle() {
assert_eq!(
sequence_block_action(1, 1, 1, 2),
SequenceBlockAction::Compressed
);
assert_eq!(sequence_block_action(0, 1, 1, 2), SequenceBlockAction::Rle);
}
#[test]
fn sequence_block_action_maps_entropy_fallbacks() {
assert_eq!(sequence_block_action(0, 0, 0, 0), SequenceBlockAction::Raw);
assert_eq!(sequence_block_action(0, 0, 0, 1), SequenceBlockAction::Rle);
assert_eq!(
sequence_block_action(0, 0, 0, 2),
SequenceBlockAction::Compressed
);
}
#[allow(clippy::too_many_arguments)]
unsafe extern "C" fn single_block_test_entropy_compress(
_seq_store: *const SeqStore_t,
_prev_entropy: *const ZSTD_entropyCTables_t,
_next_entropy: *mut ZSTD_entropyCTables_t,
strategy: c_int,
_disable_literal_compression: c_int,
dst: *mut c_void,
dst_capacity: usize,
_src_size: usize,
_workspace: *mut c_void,
_workspace_size: usize,
_bmi2: c_int,
) -> usize {
match strategy {
0 => 0,
1 => {
assert!(dst_capacity >= 2);
unsafe {
*dst.cast::<u8>() = 0x11;
*dst.cast::<u8>().add(1) = 0x22;
}
2
}
_ => ERROR(ZstdErrorCode::Generic),
}
}
fn single_block_test_state(
seq_store: &SeqStore_t,
d_rep: &mut [u32; ZSTD_REP_NUM],
c_rep: &mut [u32; ZSTD_REP_NUM],
prev_block: &mut ZSTD_compressedBlockState_t,
next_block: &mut ZSTD_compressedBlockState_t,
prev_c_block: &mut *mut ZSTD_compressedBlockState_t,
next_c_block: &mut *mut ZSTD_compressedBlockState_t,
seq_collector: &mut SeqCollector,
strategy: c_int,
is_first_block: c_int,
) -> ZSTD_rust_seqStoreSingleBlockState {
*prev_c_block = prev_block;
*next_c_block = next_block;
ZSTD_rust_seqStoreSingleBlockState {
seq_store,
d_rep: d_rep.as_mut_ptr(),
c_rep: c_rep.as_mut_ptr(),
prev_c_block,
next_c_block,
tmp_workspace: ptr::null_mut(),
tmp_wksp_size: 0,
seq_collector,
strategy,
disable_literal_compression: 0,
bmi2: 0,
is_first_block,
}
}
fn single_block_test_seams() -> SingleBlockSeams {
SingleBlockSeams {
entropy_compress: single_block_test_entropy_compress,
..SingleBlockSeams::production()
}
}
fn block_internal_test_state(
seq_store: &mut SeqStore_t,
prev_block: &mut ZSTD_compressedBlockState_t,
next_block: &mut ZSTD_compressedBlockState_t,
prev_c_block: &mut *mut ZSTD_compressedBlockState_t,
next_c_block: &mut *mut ZSTD_compressedBlockState_t,
seq_collector: &mut SeqCollector,
strategy: c_int,
is_first_block: c_int,
) -> ZSTD_rust_blockInternalState {
*prev_c_block = prev_block;
*next_c_block = next_block;
ZSTD_rust_blockInternalState {
seq_store,
prev_c_block,
next_c_block,
tmp_workspace: ptr::null_mut(),
tmp_wksp_size: 0,
seq_collector,
strategy,
disable_literal_compression: 0,
bmi2: 0,
is_first_block,
}
}
struct SplitBlockTestStore {
seq_store: SeqStore_t,
_sequences: Box<[SeqDef; 2]>,
_ll_code: Box<[u8; 2]>,
_ml_code: Box<[u8; 2]>,
_of_code: Box<[u8; 2]>,
_literals: Box<[u8; 6]>,
}
fn split_block_test_seq_store() -> SplitBlockTestStore {
let mut sequences = Box::new([
SeqDef {
offBase: 4,
litLength: 2,
mlBase: 0,
},
SeqDef {
offBase: 5,
litLength: 2,
mlBase: 0,
},
]);
let mut ll_code = Box::new([1u8; 2]);
let mut ml_code = Box::new([1u8; 2]);
let mut of_code = Box::new([1u8; 2]);
let mut literals = Box::new([0u8; 6]);
let sequences_start = sequences.as_mut_ptr();
let literals_start = literals.as_mut_ptr();
let seq_store = SeqStore_t {
sequencesStart: sequences_start,
sequences: unsafe { sequences_start.add(sequences.len()) },
litStart: literals_start,
lit: unsafe { literals_start.add(literals.len()) },
llCode: ll_code.as_mut_ptr(),
mlCode: ml_code.as_mut_ptr(),
ofCode: of_code.as_mut_ptr(),
maxNbSeq: sequences.len(),
maxNbLit: literals.len(),
longLengthType: 0,
longLengthPos: 0,
};
SplitBlockTestStore {
seq_store,
_sequences: sequences,
_ll_code: ll_code,
_ml_code: ml_code,
_of_code: of_code,
_literals: literals,
}
}
#[allow(clippy::too_many_arguments)]
fn split_block_test_state(
seq_store: &SeqStore_t,
partitions: &[u32],
next_seq_store: &mut SeqStore_t,
curr_seq_store: &mut SeqStore_t,
prev_block: &mut ZSTD_compressedBlockState_t,
next_block: &mut ZSTD_compressedBlockState_t,
prev_c_block: &mut *mut ZSTD_compressedBlockState_t,
next_c_block: &mut *mut ZSTD_compressedBlockState_t,
seq_collector: &mut SeqCollector,
) -> ZSTD_rust_splitBlockState {
*prev_c_block = prev_block;
*next_c_block = next_block;
ZSTD_rust_splitBlockState {
seq_store,
partitions: partitions.as_ptr(),
next_seq_store,
curr_seq_store,
prev_c_block,
next_c_block,
tmp_workspace: ptr::null_mut(),
tmp_wksp_size: 0,
seq_collector,
block_size_max: ZSTD_BLOCKSIZE_MAX,
strategy: 0,
disable_literal_compression: 0,
bmi2: 0,
is_first_block: 1,
}
}
#[test]
fn split_block_body_emits_partitions_and_final_literals() {
let fixture = split_block_test_seq_store();
let partitions = [1, 2];
let mut next_seq_store = unsafe { MaybeUninit::<SeqStore_t>::zeroed().assume_init() };
let mut curr_seq_store = unsafe { MaybeUninit::<SeqStore_t>::zeroed().assume_init() };
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let mut seq_collector = SeqCollector {
collectSequences: 0,
seqStart: ptr::null_mut(),
seqIndex: 0,
maxSequences: 0,
};
let state = split_block_test_state(
&fixture.seq_store,
&partitions,
&mut next_seq_store,
&mut curr_seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
&mut seq_collector,
);
let source: Vec<u8> = (0..12).collect();
let mut output = [0xa5u8; 32];
let result = unsafe {
compress_block_split_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
1,
single_block_test_seams(),
)
};
assert_eq!(result, 18);
assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..8], &source[..5]);
assert_eq!(&output[8 + ZSTD_BLOCK_HEADER_SIZE..result], &source[5..]);
assert!(std::ptr::eq(prev_c_block, &prev_block));
}
#[test]
fn block_internal_body_collects_sequences_before_entropy_emission() {
let (mut seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut prev_block = zeroed_state();
prev_block.entropy.fse.offcode_repeatMode = 2;
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let mut output_sequences = [ZSTD_Sequence {
offset: 0xa5,
litLength: 0xa5,
matchLength: 0xa5,
rep: 0xa5,
}];
let mut seq_collector = SeqCollector {
collectSequences: 1,
seqStart: output_sequences.as_mut_ptr(),
seqIndex: 0,
maxSequences: output_sequences.len(),
};
let state = block_internal_test_state(
&mut seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
&mut seq_collector,
1,
0,
);
let source = [0x5au8; 16];
let mut output = [0xa5u8; 16];
let result = unsafe {
compress_block_internal_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
single_block_test_seams(),
)
};
assert_eq!(result, 0);
assert_eq!(seq_collector.seqIndex, 1);
assert_eq!(output_sequences[0].litLength, 0);
assert!(std::ptr::eq(prev_c_block, &next_block));
assert!(std::ptr::eq(next_c_block, &prev_block));
assert_eq!(prev_block.entropy.fse.offcode_repeatMode, 2);
}
#[test]
fn block_internal_body_gates_rle_and_cleans_repeat_mode() {
let (mut seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut prev_block = zeroed_state();
prev_block.entropy.fse.offcode_repeatMode = 2;
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let mut seq_collector = SeqCollector {
collectSequences: 0,
seqStart: ptr::null_mut(),
seqIndex: 0,
maxSequences: 0,
};
let state = block_internal_test_state(
&mut seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
&mut seq_collector,
1,
0,
);
let source = [0x5au8; 16];
let mut output = [0xa5u8; 32];
let result = unsafe {
compress_block_internal_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
single_block_test_seams(),
)
};
assert_eq!(result, 1);
assert_eq!(output[0], source[0]);
assert_eq!(prev_block.entropy.fse.offcode_repeatMode, 1);
}
#[test]
fn block_internal_body_propagates_entropy_error_through_repeat_cleanup() {
let (mut seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut prev_block = zeroed_state();
prev_block.entropy.fse.offcode_repeatMode = 2;
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let mut seq_collector = SeqCollector {
collectSequences: 0,
seqStart: ptr::null_mut(),
seqIndex: 0,
maxSequences: 0,
};
let state = block_internal_test_state(
&mut seq_store,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
&mut seq_collector,
-1,
0,
);
let source = [0x5au8; 16];
let mut output = [0xa5u8; 32];
let result = unsafe {
compress_block_internal_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
single_block_test_seams(),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
assert_eq!(output, [0xa5u8; 32]);
assert_eq!(prev_block.entropy.fse.offcode_repeatMode, 1);
}
#[test]
fn single_block_body_propagates_entropy_errors_without_serializing() {
let (seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut d_rep = [1, 4, 8];
let mut c_rep = d_rep;
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let mut seq_collector = SeqCollector {
collectSequences: 0,
seqStart: ptr::null_mut(),
seqIndex: 0,
maxSequences: 0,
};
let state = single_block_test_state(
&seq_store,
&mut d_rep,
&mut c_rep,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
&mut seq_collector,
-1,
1,
);
let source = *b"entropy error";
let mut output = [0xa5u8; 32];
let result = unsafe {
compress_seq_store_single_block_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
0,
single_block_test_seams(),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
assert_eq!(output, [0xa5u8; 32]);
assert_eq!(d_rep, [1, 4, 8]);
assert_eq!(c_rep, [1, 4, 8]);
}
#[test]
fn single_block_body_serializes_raw_entropy_fallback() {
let (seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut d_rep = [1, 4, 8];
let mut c_rep = d_rep;
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let mut seq_collector = SeqCollector {
collectSequences: 0,
seqStart: ptr::null_mut(),
seqIndex: 0,
maxSequences: 0,
};
let state = single_block_test_state(
&seq_store,
&mut d_rep,
&mut c_rep,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
&mut seq_collector,
0,
1,
);
let source = *b"raw fallback";
let mut output = [0xa5u8; 32];
let result = unsafe {
compress_seq_store_single_block_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
0,
single_block_test_seams(),
)
};
assert_eq!(result, source.len() + ZSTD_BLOCK_HEADER_SIZE);
assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..result], &source);
}
#[test]
fn single_block_body_gates_rle_on_first_block_and_confirms_compressed_state() {
for is_first_block in [0, 1] {
let (seq_store, _sequences, _literals) = target_block_test_seq_store();
let mut d_rep = [1, 4, 8];
let mut c_rep = d_rep;
let mut prev_block = zeroed_state();
let mut next_block = zeroed_state();
next_block.entropy.fse.offcode_repeatMode = 2;
let mut prev_c_block = ptr::null_mut();
let mut next_c_block = ptr::null_mut();
let mut seq_collector = SeqCollector {
collectSequences: 0,
seqStart: ptr::null_mut(),
seqIndex: 0,
maxSequences: 0,
};
let state = single_block_test_state(
&seq_store,
&mut d_rep,
&mut c_rep,
&mut prev_block,
&mut next_block,
&mut prev_c_block,
&mut next_c_block,
&mut seq_collector,
1,
is_first_block,
);
let source = [0x5au8; 16];
let mut output = [0xa5u8; 32];
let result = unsafe {
compress_seq_store_single_block_body_with(
&state,
output.as_mut_ptr().cast(),
output.len(),
source.as_ptr().cast(),
source.len(),
1,
0,
single_block_test_seams(),
)
};
if is_first_block == 0 {
assert_eq!(result, 4);
assert_eq!(output[3], source[0]);
assert!(std::ptr::eq(prev_c_block, &prev_block));
} else {
assert_eq!(result, ZSTD_BLOCK_HEADER_SIZE + 2);
assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..result], &[0x11, 0x22]);
assert!(std::ptr::eq(prev_c_block, &next_block));
assert!(std::ptr::eq(next_c_block, &prev_block));
}
let expected_repeat_mode = if is_first_block == 0 { 0 } else { 1 };
assert_eq!(
unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode },
expected_repeat_mode
);
}
}
#[test]
fn empty_sequence_block_preserves_header_and_capacity_contract() {
let mut output = [0xa5; 4];
assert_eq!(
unsafe { write_empty_sequence_block(output.as_mut_ptr(), output.len()) },
ZSTD_BLOCK_HEADER_SIZE
);
assert_eq!(output, [1, 0, 0, 0]);
let mut short_output = [0xa5; 3];
assert_eq!(
unsafe { write_empty_sequence_block(short_output.as_mut_ptr(), short_output.len()) },
ERROR(ZstdErrorCode::DstSizeTooSmall)
);
assert_eq!(short_output, [0xa5; 3]);
}
#[test]
fn empty_sequence_literals_block_writes_only_the_three_byte_header() {
let mut output = [0xa5; 4];
assert_eq!(
unsafe { write_empty_sequence_literals_block(output.as_mut_ptr(), output.len()) },
ZSTD_BLOCK_HEADER_SIZE
);
assert_eq!(output, [1, 0, 0, 0xa5]);
let mut short_output = [0xa5; 2];
assert_eq!(
unsafe {
write_empty_sequence_literals_block(short_output.as_mut_ptr(), short_output.len())
},
ERROR(ZstdErrorCode::DstSizeTooSmall)
);
assert_eq!(short_output, [0xa5; 2]);
}
#[test]
fn invalidate_rep_codes_clears_all_entries() {
let mut rep = [11u32, 22, 33];
unsafe { ZSTD_rust_invalidateRepCodes(rep.as_mut_ptr()) };
assert_eq!(rep, [0; ZSTD_REP_NUM]);
}
#[test]
fn window_clear_writes_the_same_end_to_both_limits() {
let mut low_limit = 11u32;
let mut dict_limit = 22u32;
unsafe { ZSTD_rust_windowClear(0x1234_5678, &mut low_limit, &mut dict_limit) };
assert_eq!(low_limit, 0x1234_5678);
assert_eq!(dict_limit, 0x1234_5678);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn window_clear_truncates_a_large_size_t_like_c_u32_cast() {
let mut low_limit = 11u32;
let mut dict_limit = 22u32;
let end_t = 0x1_0000_0000usize + 0x89ab_cdef;
unsafe { ZSTD_rust_windowClear(end_t, &mut low_limit, &mut dict_limit) };
assert_eq!(low_limit, 0x89ab_cdef);
assert_eq!(dict_limit, 0x89ab_cdef);
}
#[test]
fn invalidate_match_state_clears_window_and_private_fields() {
let mut low_limit = 11u32;
let mut dict_limit = 22u32;
let mut next_to_update = 33u32;
let mut loaded_dict_end = 44u32;
let mut lit_length_sum = 55u32;
let marker = 0x5au8;
let mut dict_match_state = (&marker as *const u8).cast::<c_void>();
unsafe {
ZSTD_rust_invalidateMatchState(
0x1234_5678,
&mut low_limit,
&mut dict_limit,
&mut next_to_update,
&mut loaded_dict_end,
&mut lit_length_sum,
&mut dict_match_state,
)
};
assert_eq!(low_limit, 0x1234_5678);
assert_eq!(dict_limit, 0x1234_5678);
assert_eq!(next_to_update, 0x1234_5678);
assert_eq!(loaded_dict_end, 0);
assert_eq!(lit_length_sum, 0);
assert!(dict_match_state.is_null());
}
#[test]
fn window_update_leaves_empty_input_untouched() {
let mut state = ZSTD_rust_windowUpdateState {
nextSrc: ptr::null(),
base: ptr::null(),
dictBase: ptr::null(),
dictLimit: 2,
lowLimit: 2,
};
let original = state;
let result = unsafe { ZSTD_rust_windowUpdate(&mut state, ptr::null(), 0, 0) };
assert_eq!(result, 1);
assert_eq!(state, original);
}
#[test]
fn window_init_sets_empty_sentinel_and_dictionary_predicates() {
let mut state = ZSTD_rust_windowUpdateState {
nextSrc: ptr::null(),
base: ptr::null(),
dictBase: ptr::null(),
dictLimit: 0,
lowLimit: 0,
};
unsafe { ZSTD_rust_windowInit(&mut state) };
assert_eq!(state.dictLimit, ZSTD_WINDOW_START_INDEX);
assert_eq!(state.lowLimit, ZSTD_WINDOW_START_INDEX);
assert_eq!(state.dictBase as usize, state.base as usize);
assert_eq!(
(state.nextSrc as usize).wrapping_sub(state.base as usize),
ZSTD_WINDOW_START_INDEX as usize
);
assert_eq!(
ZSTD_rust_windowIsEmpty(state.nextSrc, state.base, state.dictLimit, state.lowLimit),
1
);
assert_eq!(ZSTD_rust_windowHasExtDict(10, 2), 1);
assert_eq!(ZSTD_rust_windowHasExtDict(10, 10), 0);
}
#[test]
fn window_enforce_max_dist_updates_limits_and_invalidates_dictionary() {
let state = ZSTD_rust_windowEnforceMaxDist(
100,
64,
ZSTD_rust_windowDictState {
lowLimit: 2,
dictLimit: 10,
loadedDictEnd: 8,
invalidate: 0,
},
);
assert_eq!(state.lowLimit, 36);
assert_eq!(state.dictLimit, 36);
assert_eq!(state.loadedDictEnd, 0);
assert_eq!(state.invalidate, 1);
}
#[test]
fn window_dictionary_policy_preserves_valid_state_and_detects_mismatch() {
let state = ZSTD_rust_windowEnforceMaxDist(
70,
64,
ZSTD_rust_windowDictState {
lowLimit: 2,
dictLimit: 10,
loadedDictEnd: 8,
invalidate: 1,
},
);
assert_eq!(state.lowLimit, 2);
assert_eq!(state.dictLimit, 10);
assert_eq!(state.loadedDictEnd, 8);
assert_eq!(state.invalidate, 0);
assert_eq!(ZSTD_rust_windowCheckDictValidity(70, 64, 8, 8), 0);
assert_eq!(ZSTD_rust_windowCheckDictValidity(100, 64, 8, 8), 1);
assert_eq!(ZSTD_rust_windowCheckDictValidity(70, 64, 8, 10), 1);
}
#[test]
fn window_update_preserves_contiguous_input_and_clips_overlapping_extdict() {
let storage = [0u8; 64];
let storage_start = storage.as_ptr() as usize;
let base = unsafe { storage.as_ptr().add(16) };
let next_src = unsafe { base.add(2) };
let mut contiguous_state = ZSTD_rust_windowUpdateState {
nextSrc: next_src.cast(),
base: base.cast(),
dictBase: base.cast(),
dictLimit: 2,
lowLimit: 2,
};
let contiguous =
unsafe { ZSTD_rust_windowUpdate(&mut contiguous_state, next_src.cast(), 3, 0) };
assert_eq!(contiguous, 1);
assert_eq!(contiguous_state.nextSrc as usize, storage_start + 21);
assert_eq!(contiguous_state.base as usize, storage_start + 16);
assert_eq!(contiguous_state.dictBase as usize, storage_start + 16);
assert_eq!(contiguous_state.dictLimit, 2);
assert_eq!(contiguous_state.lowLimit, 2);
let next_src = unsafe { base.add(6) };
let mut forced_state = ZSTD_rust_windowUpdateState {
nextSrc: next_src.cast(),
base: base.cast(),
dictBase: base.cast(),
dictLimit: 5,
lowLimit: 2,
};
let forced = unsafe { ZSTD_rust_windowUpdate(&mut forced_state, next_src.cast(), 3, 1) };
assert_eq!(forced, 0);
assert_eq!(forced_state.base as usize, storage_start + 16);
assert_eq!(forced_state.nextSrc as usize, storage_start + 25);
assert_eq!(forced_state.dictBase as usize, storage_start + 16);
assert_eq!(forced_state.dictLimit, 6);
assert_eq!(forced_state.lowLimit, 6);
let next_src = unsafe { base.add(20) };
let src = unsafe { base.add(4) };
let mut non_contiguous_state = ZSTD_rust_windowUpdateState {
nextSrc: next_src.cast(),
base: base.cast(),
dictBase: base.cast(),
dictLimit: 5,
lowLimit: 2,
};
let non_contiguous =
unsafe { ZSTD_rust_windowUpdate(&mut non_contiguous_state, src.cast(), 10, 0) };
assert_eq!(non_contiguous, 0);
assert_eq!(non_contiguous_state.base as usize, storage_start);
assert_eq!(non_contiguous_state.nextSrc as usize, storage_start + 30);
assert_eq!(non_contiguous_state.dictBase as usize, storage_start + 16);
assert_eq!(non_contiguous_state.dictLimit, 20);
assert_eq!(non_contiguous_state.lowLimit, 14);
}
#[derive(Default)]
struct ClearAllDictsTestContext {
events: Vec<&'static str>,
}
unsafe fn clear_all_dicts_test_context(
context: *mut c_void,
) -> &'static mut ClearAllDictsTestContext {
unsafe { &mut *context.cast::<ClearAllDictsTestContext>() }
}
unsafe extern "C" fn clear_all_dicts_test_free_local_dict_buffer(context: *mut c_void) {
unsafe { clear_all_dicts_test_context(context) }
.events
.push("free-local-buffer");
}
unsafe extern "C" fn clear_all_dicts_test_free_local_cdict(context: *mut c_void) {
unsafe { clear_all_dicts_test_context(context) }
.events
.push("free-local-cdict");
}
unsafe extern "C" fn clear_all_dicts_test_clear_local_dict(context: *mut c_void) {
unsafe { clear_all_dicts_test_context(context) }
.events
.push("clear-local");
}
unsafe extern "C" fn clear_all_dicts_test_clear_prefix_dict(context: *mut c_void) {
unsafe { clear_all_dicts_test_context(context) }
.events
.push("clear-prefix");
}
unsafe extern "C" fn clear_all_dicts_test_clear_cdict(context: *mut c_void) {
unsafe { clear_all_dicts_test_context(context) }
.events
.push("clear-cdict");
}
fn clear_all_dicts_test_state(
context: &mut ClearAllDictsTestContext,
) -> ZSTD_rust_clearAllDictsState {
ZSTD_rust_clearAllDictsState {
callback_context: (context as *mut ClearAllDictsTestContext).cast(),
free_local_dict_buffer: clear_all_dicts_test_free_local_dict_buffer,
free_local_cdict: clear_all_dicts_test_free_local_cdict,
clear_local_dict: clear_all_dicts_test_clear_local_dict,
clear_prefix_dict: clear_all_dicts_test_clear_prefix_dict,
clear_cdict: clear_all_dicts_test_clear_cdict,
}
}
#[test]
fn clear_all_dicts_preserves_dictionary_teardown_order() {
let mut context = ClearAllDictsTestContext::default();
let state = clear_all_dicts_test_state(&mut context);
unsafe { ZSTD_rust_clearAllDicts(&state) };
assert_eq!(
context.events,
[
"free-local-buffer",
"free-local-cdict",
"clear-local",
"clear-prefix",
"clear-cdict"
]
);
}
#[derive(Default)]
struct ResetCCtxTestContext {
events: Vec<&'static str>,
rust_simple_compress2_completed: c_uint,
stream_stage: c_int,
pledged_src_size_plus_one: u64,
rust_simple_compress2_max_block_size_set: c_uint,
reset_params_result: usize,
}
unsafe fn reset_cctx_test_context(context: *mut c_void) -> &'static mut ResetCCtxTestContext {
unsafe { &mut *context.cast::<ResetCCtxTestContext>() }
}
unsafe extern "C" fn reset_cctx_test_clear_all_dicts(context: *mut c_void) {
unsafe { reset_cctx_test_context(context) }
.events
.push("clear-dicts");
}
unsafe extern "C" fn reset_cctx_test_reset_params(context: *mut c_void) -> usize {
let context = unsafe { reset_cctx_test_context(context) };
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
context.events.push("reset-params");
context.reset_params_result
}
fn reset_cctx_test_state(context: &mut ResetCCtxTestContext) -> ZSTD_rust_resetCCtxState {
ZSTD_rust_resetCCtxState {
callback_context: (context as *mut ResetCCtxTestContext).cast(),
rust_simple_compress2_completed: &mut context.rust_simple_compress2_completed,
stream_stage: &mut context.stream_stage,
pledged_src_size_plus_one: &mut context.pledged_src_size_plus_one,
rust_simple_compress2_max_block_size_set: &mut context
.rust_simple_compress2_max_block_size_set,
clear_all_dicts: reset_cctx_test_clear_all_dicts,
reset_params: reset_cctx_test_reset_params,
}
}
#[test]
fn reset_cctx_session_only_clears_session_state() {
let mut context = ResetCCtxTestContext {
rust_simple_compress2_completed: 1,
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
pledged_src_size_plus_one: 123,
rust_simple_compress2_max_block_size_set: 1,
..ResetCCtxTestContext::default()
};
let state = reset_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_SESSION_ONLY) };
assert_eq!(result, 0);
assert_eq!(context.rust_simple_compress2_completed, 0);
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_INIT);
assert_eq!(context.pledged_src_size_plus_one, 0);
assert_eq!(context.rust_simple_compress2_max_block_size_set, 1);
assert!(context.events.is_empty());
}
#[test]
fn reset_cctx_parameters_runs_callbacks_in_order_and_clears_max_block_flag() {
let mut context = ResetCCtxTestContext {
rust_simple_compress2_completed: 1,
stream_stage: ZSTD_CSTREAM_STAGE_INIT,
pledged_src_size_plus_one: 123,
rust_simple_compress2_max_block_size_set: 1,
..ResetCCtxTestContext::default()
};
let state = reset_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_PARAMETERS) };
assert_eq!(result, 0);
assert_eq!(context.rust_simple_compress2_completed, 0);
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_INIT);
assert_eq!(context.pledged_src_size_plus_one, 123);
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
assert_eq!(context.events, ["clear-dicts", "reset-params"]);
}
#[test]
fn reset_cctx_session_and_parameters_resets_session_before_callbacks() {
let mut context = ResetCCtxTestContext {
rust_simple_compress2_completed: 1,
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
pledged_src_size_plus_one: 123,
rust_simple_compress2_max_block_size_set: 1,
..ResetCCtxTestContext::default()
};
let state = reset_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_SESSION_AND_PARAMETERS) };
assert_eq!(result, 0);
assert_eq!(context.rust_simple_compress2_completed, 0);
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_INIT);
assert_eq!(context.pledged_src_size_plus_one, 0);
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
assert_eq!(context.events, ["clear-dicts", "reset-params"]);
}
#[test]
fn reset_cctx_parameters_rejects_non_init_stage_before_mutating_parameters() {
let mut context = ResetCCtxTestContext {
rust_simple_compress2_completed: 1,
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
pledged_src_size_plus_one: 123,
rust_simple_compress2_max_block_size_set: 1,
..ResetCCtxTestContext::default()
};
let state = reset_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_PARAMETERS) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.rust_simple_compress2_completed, 0);
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_LOAD);
assert_eq!(context.pledged_src_size_plus_one, 123);
assert_eq!(context.rust_simple_compress2_max_block_size_set, 1);
assert!(context.events.is_empty());
}
#[test]
fn reset_cctx_propagates_parameter_reset_error_after_clearing_state() {
let mut context = ResetCCtxTestContext {
rust_simple_compress2_completed: 1,
stream_stage: ZSTD_CSTREAM_STAGE_INIT,
pledged_src_size_plus_one: 123,
rust_simple_compress2_max_block_size_set: 1,
reset_params_result: ERROR(ZstdErrorCode::MemoryAllocation),
..ResetCCtxTestContext::default()
};
let state = reset_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_PARAMETERS) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.rust_simple_compress2_completed, 0);
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
assert_eq!(context.events, ["clear-dicts", "reset-params"]);
}
#[test]
fn reset_cctx_ignores_unknown_directives_after_clearing_completion() {
let mut context = ResetCCtxTestContext {
rust_simple_compress2_completed: 1,
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
pledged_src_size_plus_one: 123,
rust_simple_compress2_max_block_size_set: 1,
..ResetCCtxTestContext::default()
};
let state = reset_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCCtx(&state, 99) };
assert_eq!(result, 0);
assert_eq!(context.rust_simple_compress2_completed, 0);
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_LOAD);
assert_eq!(context.pledged_src_size_plus_one, 123);
assert_eq!(context.rust_simple_compress2_max_block_size_set, 1);
assert!(context.events.is_empty());
}
#[derive(Default)]
struct CopyCCtxTestContext {
events: Vec<&'static str>,
src_cctx: *const c_void,
frame_params: ZSTD_frameParameters,
pledged_src_size: u64,
zbuff: c_int,
result: usize,
}
unsafe extern "C" fn copy_cctx_test_internal(
context: *mut c_void,
src_cctx: *const c_void,
f_params: *const ZSTD_frameParameters,
pledged_src_size: u64,
zbuff: c_int,
) -> usize {
let context = unsafe { &mut *context.cast::<CopyCCtxTestContext>() };
context.events.push("copy");
context.src_cctx = src_cctx;
context.frame_params = unsafe { *f_params };
context.pledged_src_size = pledged_src_size;
context.zbuff = zbuff;
context.result
}
fn copy_cctx_test_state(
context: &mut CopyCCtxTestContext,
src_cctx: *const c_void,
f_params: &ZSTD_frameParameters,
pledged_src_size: &u64,
zbuff: &c_int,
) -> ZSTD_rust_copyCCtxState {
ZSTD_rust_copyCCtxState {
callback_context: (context as *mut CopyCCtxTestContext).cast(),
src_cctx,
f_params,
pledged_src_size,
zbuff,
copy_internal: Some(copy_cctx_test_internal),
}
}
#[test]
fn copy_cctx_converts_zero_pledge_to_unknown_and_clears_content_flag() {
let mut context = CopyCCtxTestContext::default();
let f_params = ZSTD_frameParameters {
contentSizeFlag: 1,
checksumFlag: 1,
noDictIDFlag: 1,
};
let pledged_src_size = 0;
let zbuff = 1;
let src_cctx = 0x4000usize as *const c_void;
let state =
copy_cctx_test_state(&mut context, src_cctx, &f_params, &pledged_src_size, &zbuff);
let result = unsafe { ZSTD_rust_copyCCtx(&state) };
assert_eq!(result, 0);
assert_eq!(context.events, ["copy"]);
assert_eq!(context.src_cctx, src_cctx);
assert_eq!(context.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN);
assert_eq!(context.zbuff, zbuff);
assert_eq!(context.frame_params.contentSizeFlag, 0);
assert_eq!(context.frame_params.checksumFlag, 1);
assert_eq!(context.frame_params.noDictIDFlag, 1);
}
#[test]
fn copy_cctx_forwards_nonzero_pledge_and_propagates_internal_error() {
let mut context = CopyCCtxTestContext {
result: ERROR(ZstdErrorCode::MemoryAllocation),
..CopyCCtxTestContext::default()
};
let f_params = ZSTD_frameParameters {
contentSizeFlag: 0,
checksumFlag: 1,
noDictIDFlag: 1,
};
let pledged_src_size = 123;
let zbuff = 7;
let state = copy_cctx_test_state(
&mut context,
0x4000usize as *const c_void,
&f_params,
&pledged_src_size,
&zbuff,
);
let result = unsafe { ZSTD_rust_copyCCtx(&state) };
assert_eq!(result, context.result);
assert_eq!(context.events, ["copy"]);
assert_eq!(context.pledged_src_size, pledged_src_size);
assert_eq!(context.frame_params.contentSizeFlag, 1);
assert_eq!(context.frame_params.checksumFlag, 1);
assert_eq!(context.frame_params.noDictIDFlag, 1);
}
#[derive(Default)]
struct CopyCCtxInternalTestContext {
events: Vec<&'static str>,
reset_result: usize,
frame_params: ZSTD_frameParameters,
pledged_src_size: u64,
zbuff: c_int,
}
unsafe fn copy_cctx_internal_test_context(
context: *mut c_void,
) -> &'static mut CopyCCtxInternalTestContext {
unsafe { &mut *context.cast::<CopyCCtxInternalTestContext>() }
}
unsafe extern "C" fn copy_cctx_internal_test_reset(
context: *mut c_void,
_src_cctx: *const c_void,
f_params: *const ZSTD_frameParameters,
pledged_src_size: u64,
zbuff: c_int,
) -> usize {
let context = unsafe { copy_cctx_internal_test_context(context) };
context.events.push("reset");
context.frame_params = unsafe { *f_params };
context.pledged_src_size = pledged_src_size;
context.zbuff = zbuff;
context.reset_result
}
unsafe extern "C" fn copy_cctx_internal_test_mark_tables_dirty(context: *mut c_void) {
unsafe { copy_cctx_internal_test_context(context) }
.events
.push("dirty");
}
unsafe extern "C" fn copy_cctx_internal_test_mark_tables_clean(context: *mut c_void) {
unsafe { copy_cctx_internal_test_context(context) }
.events
.push("clean");
}
fn copy_cctx_internal_test_state(
context: &mut CopyCCtxInternalTestContext,
src_cctx: *const c_void,
f_params: &ZSTD_frameParameters,
source_stage: *const c_int,
destination_custom_mem: *mut c_void,
source_custom_mem: *const c_void,
destination_hash_table: *mut *mut c_uint,
source_hash_table: *const c_uint,
source_hash_log: *const c_uint,
destination_chain_table: *mut *mut c_uint,
source_chain_table: *const c_uint,
source_chain_log: *const c_uint,
destination_hash_table3: *mut *mut c_uint,
source_hash_table3: *const c_uint,
source_hash_log3: *const c_uint,
source_strategy: *const c_int,
source_use_row_match_finder: *const c_int,
destination_window: *mut c_void,
source_window: *const c_void,
destination_next_to_update: *mut c_uint,
source_next_to_update: *const c_uint,
destination_loaded_dict_end: *mut c_uint,
source_loaded_dict_end: *const c_uint,
destination_dict_id: *mut c_uint,
source_dict_id: *const c_uint,
destination_dict_content_size: *mut usize,
source_dict_content_size: *const usize,
destination_block_state: *mut *mut ZSTD_compressedBlockState_t,
source_block_state: *const ZSTD_compressedBlockState_t,
) -> ZSTD_rust_copyCCtxInternalState {
ZSTD_rust_copyCCtxInternalState {
callback_context: (context as *mut CopyCCtxInternalTestContext).cast(),
src_cctx,
f_params,
pledged_src_size: 123,
source_stage,
destination_custom_mem,
source_custom_mem,
reset: Some(copy_cctx_internal_test_reset),
mark_tables_dirty: Some(copy_cctx_internal_test_mark_tables_dirty),
mark_tables_clean: Some(copy_cctx_internal_test_mark_tables_clean),
destination_hash_table,
source_hash_table,
source_hash_log,
destination_chain_table,
source_chain_table,
source_chain_log,
destination_hash_table3,
source_hash_table3,
source_hash_log3,
source_strategy,
source_use_row_match_finder,
destination_window,
source_window,
destination_next_to_update,
source_next_to_update,
destination_loaded_dict_end,
source_loaded_dict_end,
destination_dict_id,
source_dict_id,
destination_dict_content_size,
source_dict_content_size,
destination_block_state,
source_block_state,
zbuff: 7,
}
}
#[test]
fn copy_cctx_internal_runs_private_callbacks_in_original_order() {
let mut context = CopyCCtxInternalTestContext::default();
let f_params = ZSTD_frameParameters {
contentSizeFlag: 1,
checksumFlag: 1,
noDictIDFlag: 1,
};
let source_stage = ZSTD_COMPRESSION_STAGE_INIT;
let source_custom_mem = [0x11usize, 0x22, 0x33];
let mut destination_custom_mem = [0usize; 3];
let source_hash_table = [11u32, 12, 13, 14];
let mut destination_hash_table = [0u32; 4];
let mut destination_hash_table_ptr = destination_hash_table.as_mut_ptr();
let source_hash_log = 2;
let source_chain_table = [21u32, 22];
let mut destination_chain_table = [0u32; 2];
let mut destination_chain_table_ptr = destination_chain_table.as_mut_ptr();
let source_chain_log = 1;
let source_hash_table3 = [31u32, 32];
let mut destination_hash_table3 = [0u32; 2];
let mut destination_hash_table3_ptr = destination_hash_table3.as_mut_ptr();
let source_hash_log3 = 1;
let source_strategy = ZSTD_DFAST;
let source_use_row_match_finder = ZSTD_RUST_PS_DISABLE;
let mut source_block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
source_block_state.rep = [15, 16, 17];
source_block_state.entropy.huf.repeatMode = 2;
source_block_state.entropy.fse.offcode_repeatMode = FSE_REPEAT_VALID;
let source_window = ZSTD_rust_copyWindowState {
next_src: 0x1000usize as *const c_void,
base: 0x2000usize as *const c_void,
dict_base: 0x3000usize as *const c_void,
dict_limit: 21,
low_limit: 13,
nb_overflow_corrections: 8,
};
let mut destination_window = ZSTD_rust_copyWindowState {
next_src: ptr::null(),
base: ptr::null(),
dict_base: ptr::null(),
dict_limit: 0,
low_limit: 0,
nb_overflow_corrections: 0,
};
let source_next_to_update = 34;
let mut destination_next_to_update = 0;
let source_loaded_dict_end = 55;
let mut destination_loaded_dict_end = 0;
let mut destination_block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut destination_block_state_slot =
&mut destination_block_state as *mut ZSTD_compressedBlockState_t;
let source_dict_id = 0x1234_5678;
let mut destination_dict_id = 0;
let source_dict_content_size = 9876;
let mut destination_dict_content_size = 0;
let state = copy_cctx_internal_test_state(
&mut context,
0x5000usize as *const c_void,
&f_params,
&source_stage,
(&mut destination_custom_mem as *mut [usize; 3]).cast(),
(&source_custom_mem as *const [usize; 3]).cast(),
&mut destination_hash_table_ptr,
source_hash_table.as_ptr(),
&source_hash_log,
&mut destination_chain_table_ptr,
source_chain_table.as_ptr(),
&source_chain_log,
&mut destination_hash_table3_ptr,
source_hash_table3.as_ptr(),
&source_hash_log3,
&source_strategy,
&source_use_row_match_finder,
(&mut destination_window as *mut ZSTD_rust_copyWindowState).cast(),
(&source_window as *const ZSTD_rust_copyWindowState).cast(),
&mut destination_next_to_update,
&source_next_to_update,
&mut destination_loaded_dict_end,
&source_loaded_dict_end,
&mut destination_dict_id,
&source_dict_id,
&mut destination_dict_content_size,
&source_dict_content_size,
&mut destination_block_state_slot,
&source_block_state,
);
let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "dirty", "clean"]);
assert_eq!(context.frame_params, f_params);
assert_eq!(context.pledged_src_size, 123);
assert_eq!(context.zbuff, 7);
assert_eq!(destination_custom_mem, source_custom_mem);
assert_eq!(destination_hash_table, source_hash_table);
assert_eq!(destination_chain_table, source_chain_table);
assert_eq!(destination_hash_table3, source_hash_table3);
assert_eq!(destination_window, source_window);
assert_eq!(destination_next_to_update, source_next_to_update);
assert_eq!(destination_loaded_dict_end, source_loaded_dict_end);
assert_eq!(destination_dict_id, source_dict_id);
assert_eq!(destination_dict_content_size, source_dict_content_size);
assert_eq!(destination_block_state.rep, source_block_state.rep);
assert_eq!(
destination_block_state.entropy.huf.repeatMode,
source_block_state.entropy.huf.repeatMode
);
assert_eq!(
destination_block_state.entropy.fse.offcode_repeatMode,
source_block_state.entropy.fse.offcode_repeatMode
);
}
#[test]
fn copy_cctx_internal_rejects_stage_before_mutation() {
let mut context = CopyCCtxInternalTestContext::default();
let f_params = ZSTD_frameParameters::default();
let source_stage = ZSTD_COMPRESSION_STAGE_ONGOING;
let source_custom_mem = [0usize; 3];
let mut destination_custom_mem = [0usize; 3];
let mut destination_block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut destination_block_state_slot =
&mut destination_block_state as *mut ZSTD_compressedBlockState_t;
let source_block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let source_dict_id = 0x1234_5678;
let mut destination_dict_id = 0;
let source_dict_content_size = 9876;
let mut destination_dict_content_size = 0;
let state = copy_cctx_internal_test_state(
&mut context,
0x5000usize as *const c_void,
&f_params,
&source_stage,
(&mut destination_custom_mem as *mut [usize; 3]).cast(),
(&source_custom_mem as *const [usize; 3]).cast(),
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling(),
ptr::dangling(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
&mut destination_dict_id,
&source_dict_id,
&mut destination_dict_content_size,
&source_dict_content_size,
&mut destination_block_state_slot,
&source_block_state,
);
let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert!(context.events.is_empty());
}
#[test]
fn copy_cctx_internal_propagates_reset_error_before_table_mutation() {
let mut context = CopyCCtxInternalTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..CopyCCtxInternalTestContext::default()
};
let f_params = ZSTD_frameParameters::default();
let source_stage = ZSTD_COMPRESSION_STAGE_INIT;
let source_custom_mem = [0usize; 3];
let mut destination_custom_mem = [0usize; 3];
let source_hash_log = 0;
let source_chain_log = 0;
let source_hash_log3 = 0;
let source_strategy = ZSTD_DFAST;
let source_use_row_match_finder = ZSTD_RUST_PS_DISABLE;
let mut destination_block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut destination_block_state_slot =
&mut destination_block_state as *mut ZSTD_compressedBlockState_t;
let source_block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let source_dict_id = 0x1234_5678;
let mut destination_dict_id = 0;
let source_dict_content_size = 9876;
let mut destination_dict_content_size = 0;
let state = copy_cctx_internal_test_state(
&mut context,
0x5000usize as *const c_void,
&f_params,
&source_stage,
(&mut destination_custom_mem as *mut [usize; 3]).cast(),
(&source_custom_mem as *const [usize; 3]).cast(),
ptr::dangling_mut(),
ptr::dangling(),
&source_hash_log,
ptr::dangling_mut(),
ptr::dangling(),
&source_chain_log,
ptr::dangling_mut(),
ptr::dangling(),
&source_hash_log3,
&source_strategy,
&source_use_row_match_finder,
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
&mut destination_dict_id,
&source_dict_id,
&mut destination_dict_content_size,
&source_dict_content_size,
&mut destination_block_state_slot,
&source_block_state,
);
let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
}
#[derive(Default)]
struct CompressAdvancedTestContext {
events: Vec<&'static str>,
init_params: ZSTD_parameters,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
result: usize,
}
unsafe extern "C" fn compress_advanced_test_init(
context: *mut c_void,
params: *const ZSTD_parameters,
) {
let context = unsafe { &mut *context.cast::<CompressAdvancedTestContext>() };
context.events.push("init");
context.init_params = unsafe { *params };
}
unsafe extern "C" fn compress_advanced_test_internal(
context: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
) -> usize {
let context = unsafe { &mut *context.cast::<CompressAdvancedTestContext>() };
context.events.push("compress");
context.dst = dst;
context.dst_capacity = dst_capacity;
context.src = src;
context.src_size = src_size;
context.dict = dict;
context.dict_size = dict_size;
context.result
}
fn compress_advanced_test_state(
context: &mut CompressAdvancedTestContext,
) -> ZSTD_rust_compressAdvancedState {
ZSTD_rust_compressAdvancedState {
callback_context: (context as *mut CompressAdvancedTestContext).cast(),
init_params: Some(compress_advanced_test_init),
compress_internal: Some(compress_advanced_test_internal),
}
}
fn compress_advanced_test_params() -> ZSTD_parameters {
ZSTD_parameters {
cParams: ZSTD_compressionParameters {
windowLog: 10,
chainLog: 11,
hashLog: 12,
searchLog: 13,
minMatch: 4,
targetLength: 5,
strategy: 1,
},
fParams: ZSTD_frameParameters {
contentSizeFlag: 1,
checksumFlag: 1,
noDictIDFlag: 0,
},
}
}
#[test]
fn compress_advanced_validates_then_initializes_and_compresses() {
let mut context = CompressAdvancedTestContext {
result: 17,
..CompressAdvancedTestContext::default()
};
let state = compress_advanced_test_state(&mut context);
let params = compress_advanced_test_params();
let mut dst = [0u8; 8];
let src = [1u8, 2, 3];
let dict = [4u8, 5];
let result = unsafe {
ZSTD_rust_compressAdvanced(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
dict.as_ptr().cast(),
dict.len(),
&params,
)
};
assert_eq!(result, context.result);
assert_eq!(context.events, ["init", "compress"]);
assert_eq!(context.init_params, params);
assert_eq!(context.dst, dst.as_mut_ptr().cast());
assert_eq!(context.dst_capacity, dst.len());
assert_eq!(context.src, src.as_ptr().cast());
assert_eq!(context.src_size, src.len());
assert_eq!(context.dict, dict.as_ptr().cast());
assert_eq!(context.dict_size, dict.len());
}
#[test]
fn compress_advanced_stops_before_callbacks_on_invalid_parameters() {
let mut context = CompressAdvancedTestContext::default();
let state = compress_advanced_test_state(&mut context);
let mut params = compress_advanced_test_params();
params.cParams.windowLog = 0;
let result = unsafe {
ZSTD_rust_compressAdvanced(
&state,
ptr::null_mut(),
0,
ptr::null(),
0,
ptr::null(),
0,
&params,
)
};
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert!(context.events.is_empty());
}
#[derive(Default)]
struct CompressBeginAdvancedTestContext {
events: Vec<&'static str>,
params: ZSTD_parameters,
dict: *const c_void,
dict_size: usize,
cctx_params: *const c_void,
pledged_src_size: u64,
result: usize,
}
unsafe extern "C" fn compress_begin_advanced_test_init(
context: *mut c_void,
params: *const ZSTD_parameters,
) {
let context = unsafe { &mut *context.cast::<CompressBeginAdvancedTestContext>() };
context.events.push("init");
context.params = unsafe { *params };
}
unsafe extern "C" fn compress_begin_advanced_test_begin(
context: *mut c_void,
dict: *const c_void,
dict_size: usize,
cctx_params: *const c_void,
pledged_src_size: u64,
) -> usize {
let context = unsafe { &mut *context.cast::<CompressBeginAdvancedTestContext>() };
context.events.push("begin");
context.dict = dict;
context.dict_size = dict_size;
context.cctx_params = cctx_params;
context.pledged_src_size = pledged_src_size;
context.result
}
fn compress_begin_advanced_test_state(
context: &mut CompressBeginAdvancedTestContext,
) -> ZSTD_rust_compressBeginAdvancedState {
ZSTD_rust_compressBeginAdvancedState {
cctx: (context as *mut CompressBeginAdvancedTestContext).cast(),
cctx_params: (context as *mut CompressBeginAdvancedTestContext).cast(),
init_params: compress_begin_advanced_test_init,
begin: compress_begin_advanced_test_begin,
}
}
#[test]
fn compress_begin_advanced_validates_and_preserves_init_begin_order() {
let mut context = CompressBeginAdvancedTestContext {
result: 29,
..CompressBeginAdvancedTestContext::default()
};
let state = compress_begin_advanced_test_state(&mut context);
let params = compress_advanced_test_params();
let dict = [1u8, 2, 3];
let result = unsafe {
ZSTD_rust_compressBeginAdvanced(&state, dict.as_ptr().cast(), dict.len(), &params, 123)
};
assert_eq!(result, context.result);
assert_eq!(context.events, ["init", "begin"]);
assert_eq!(context.params, params);
assert_eq!(context.dict, dict.as_ptr().cast());
assert_eq!(context.dict_size, dict.len());
assert_eq!(
context.cctx_params,
(&context as *const CompressBeginAdvancedTestContext).cast()
);
assert_eq!(context.pledged_src_size, 123);
}
#[test]
fn compress_begin_advanced_stops_before_callbacks_on_invalid_parameters() {
let mut context = CompressBeginAdvancedTestContext::default();
let state = compress_begin_advanced_test_state(&mut context);
let mut params = compress_advanced_test_params();
params.cParams.windowLog = 0;
let result = unsafe { ZSTD_rust_compressBeginAdvanced(&state, ptr::null(), 0, &params, 0) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert!(context.events.is_empty());
}
#[derive(Default)]
struct CompressUsingDictTestContext {
events: Vec<&'static str>,
init_params: ZSTD_parameters,
init_level: c_int,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
result: usize,
}
unsafe extern "C" fn compress_using_dict_test_init(
context: *mut c_void,
params: *const ZSTD_parameters,
compression_level: c_int,
) {
let context = unsafe { &mut *context.cast::<CompressUsingDictTestContext>() };
context.events.push("init");
context.init_params = unsafe { *params };
context.init_level = compression_level;
}
unsafe extern "C" fn compress_using_dict_test_internal(
context: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
) -> usize {
let context = unsafe { &mut *context.cast::<CompressUsingDictTestContext>() };
context.events.push("compress");
context.dst = dst;
context.dst_capacity = dst_capacity;
context.src = src;
context.src_size = src_size;
context.dict = dict;
context.dict_size = dict_size;
context.result
}
fn compress_using_dict_test_state(
context: &mut CompressUsingDictTestContext,
exclusion_mask: &u32,
) -> ZSTD_rust_compressUsingDictState {
ZSTD_rust_compressUsingDictState {
callback_context: (context as *mut CompressUsingDictTestContext).cast(),
exclusion_mask,
init_params: Some(compress_using_dict_test_init),
compress_internal: Some(compress_using_dict_test_internal),
}
}
#[test]
fn compress_using_dict_selects_params_and_normalizes_default_level() {
let mut context = CompressUsingDictTestContext {
result: 23,
..CompressUsingDictTestContext::default()
};
let exclusion_mask = 0;
let state = compress_using_dict_test_state(&mut context, &exclusion_mask);
let mut dst = [0u8; 8];
let src = [1u8, 2, 3];
let dict = [4u8, 5];
let result = unsafe {
ZSTD_rust_compressUsingDict(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
dict.as_ptr().cast(),
dict.len(),
0,
)
};
assert_eq!(result, context.result);
assert_eq!(context.events, ["init", "compress"]);
assert_eq!(
context.init_params,
ZSTD_rust_params_getParamsInternal(
0,
src.len() as u64,
dict.len(),
ZSTD_RUST_CPM_NO_ATTACH_DICT,
exclusion_mask,
)
);
assert_eq!(context.init_level, ZSTD_rust_params_defaultCLevel());
assert_eq!(context.dst, dst.as_mut_ptr().cast());
assert_eq!(context.dst_capacity, dst.len());
assert_eq!(context.src, src.as_ptr().cast());
assert_eq!(context.src_size, src.len());
assert_eq!(context.dict, dict.as_ptr().cast());
assert_eq!(context.dict_size, dict.len());
}
#[test]
fn compress_using_dict_ignores_size_without_a_dictionary_buffer_for_params() {
let mut context = CompressUsingDictTestContext::default();
let exclusion_mask = 0;
let state = compress_using_dict_test_state(&mut context, &exclusion_mask);
let expected = ZSTD_rust_params_getParamsInternal(
3,
0,
0,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
exclusion_mask,
);
let result = unsafe {
ZSTD_rust_compressUsingDict(
&state,
ptr::null_mut(),
0,
ptr::null(),
0,
ptr::null(),
17,
3,
)
};
assert_eq!(result, 0);
assert_eq!(context.events, ["init", "compress"]);
assert_eq!(context.init_params, expected);
assert_eq!(context.dict, ptr::null());
assert_eq!(context.dict_size, 17);
}
#[derive(Default)]
struct ResetCStreamTestContext {
events: Vec<&'static str>,
reset_result: usize,
pledged_result: usize,
pledged_src_size: u64,
}
unsafe extern "C" fn reset_cstream_test_reset(context: *mut c_void) -> usize {
let context = unsafe { &mut *context.cast::<ResetCStreamTestContext>() };
context.events.push("reset");
context.reset_result
}
unsafe extern "C" fn reset_cstream_test_set_pledged_src_size(
context: *mut c_void,
pledged_src_size: u64,
) -> usize {
let context = unsafe { &mut *context.cast::<ResetCStreamTestContext>() };
context.events.push("pledged");
context.pledged_src_size = pledged_src_size;
context.pledged_result
}
fn reset_cstream_test_state(
context: &mut ResetCStreamTestContext,
) -> ZSTD_rust_resetCStreamState {
ZSTD_rust_resetCStreamState {
callback_context: (context as *mut ResetCStreamTestContext).cast(),
reset_session: reset_cstream_test_reset,
set_pledged_src_size: reset_cstream_test_set_pledged_src_size,
}
}
#[test]
fn reset_cstream_converts_zero_pledge_to_unknown_after_reset() {
let mut context = ResetCStreamTestContext::default();
let state = reset_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCStream(&state, 0) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "pledged"]);
assert_eq!(context.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN);
}
#[test]
fn reset_cstream_forwards_nonzero_pledge() {
let mut context = ResetCStreamTestContext::default();
let state = reset_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCStream(&state, 123) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "pledged"]);
assert_eq!(context.pledged_src_size, 123);
}
#[test]
fn reset_cstream_stops_before_pledge_after_reset_error() {
let mut context = ResetCStreamTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..ResetCStreamTestContext::default()
};
let state = reset_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCStream(&state, 123) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
assert_eq!(context.pledged_src_size, 0);
}
#[test]
fn reset_cstream_propagates_pledge_error_after_reset() {
let mut context = ResetCStreamTestContext {
pledged_result: ERROR(ZstdErrorCode::StageWrong),
..ResetCStreamTestContext::default()
};
let state = reset_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_resetCStream(&state, 123) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["reset", "pledged"]);
assert_eq!(context.pledged_src_size, 123);
}
#[derive(Default)]
struct InitCStreamUsingCDictAdvancedTestContext {
events: Vec<&'static str>,
reset_result: usize,
pledged_result: usize,
set_level_result: usize,
load_dict_result: usize,
check_params_result: usize,
ref_result: usize,
pledged_src_size: u64,
compression_level: c_int,
frame_params: [c_uint; 3],
cdict: *const c_void,
dict_size: usize,
zstd_params: ZSTD_parameters,
}
unsafe fn init_cstream_using_cdict_advanced_test_context(
context: *mut c_void,
) -> &'static mut InitCStreamUsingCDictAdvancedTestContext {
unsafe { &mut *context.cast::<InitCStreamUsingCDictAdvancedTestContext>() }
}
unsafe extern "C" fn init_cstream_using_cdict_advanced_test_reset(
context: *mut c_void,
) -> usize {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("reset");
context.reset_result
}
unsafe extern "C" fn init_cstream_using_cdict_advanced_test_set_pledged(
context: *mut c_void,
pledged_src_size: u64,
) -> usize {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("pledged");
context.pledged_src_size = pledged_src_size;
context.pledged_result
}
unsafe extern "C" fn init_cstream_src_size_test_set_level(
context: *mut c_void,
compression_level: c_int,
) -> usize {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("level");
context.compression_level = compression_level;
context.set_level_result
}
unsafe extern "C" fn init_cstream_using_dict_test_load_dictionary(
context: *mut c_void,
dict: *const c_void,
dict_size: usize,
) -> usize {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("load-dict");
context.cdict = dict;
context.dict_size = dict_size;
context.load_dict_result
}
unsafe extern "C" fn init_cstream_advanced_test_check_c_params(
context: *mut c_void,
_c_params: ZSTD_compressionParameters,
) -> usize {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("check");
context.check_params_result
}
unsafe extern "C" fn init_cstream_advanced_test_set_zstd_params(
context: *mut c_void,
params: *const ZSTD_parameters,
) {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("params");
context.zstd_params = unsafe { *params };
}
unsafe extern "C" fn init_cstream_using_cdict_advanced_test_set_frame_params(
context: *mut c_void,
content_size_flag: c_uint,
checksum_flag: c_uint,
no_dict_id_flag: c_uint,
) {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("frame");
context.frame_params = [content_size_flag, checksum_flag, no_dict_id_flag];
}
unsafe extern "C" fn init_cstream_using_cdict_advanced_test_ref_cdict(
context: *mut c_void,
cdict: *const c_void,
) -> usize {
let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) };
context.events.push("ref-cdict");
context.cdict = cdict;
context.ref_result
}
fn init_cstream_using_cdict_advanced_test_state(
context: &mut InitCStreamUsingCDictAdvancedTestContext,
) -> ZSTD_rust_initCStreamUsingCDictAdvancedState {
ZSTD_rust_initCStreamUsingCDictAdvancedState {
callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(),
reset_session: init_cstream_using_cdict_advanced_test_reset,
set_pledged_src_size: init_cstream_using_cdict_advanced_test_set_pledged,
set_frame_params: init_cstream_using_cdict_advanced_test_set_frame_params,
ref_cdict: init_cstream_using_cdict_advanced_test_ref_cdict,
}
}
#[test]
fn init_cstream_using_cdict_advanced_preserves_order_and_scalars() {
let cdict = ptr::dangling::<c_void>();
let mut context = InitCStreamUsingCDictAdvancedTestContext::default();
let state = init_cstream_using_cdict_advanced_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingCDictAdvanced(&state, 77, 1, 2, 3, cdict) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "pledged", "frame", "ref-cdict"]);
assert_eq!(context.pledged_src_size, 77);
assert_eq!(context.frame_params, [1, 2, 3]);
assert_eq!(context.cdict, cdict);
}
#[test]
fn init_cstream_using_cdict_advanced_stops_after_reset_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_cdict_advanced_test_state(&mut context);
let result =
unsafe { ZSTD_rust_initCStreamUsingCDictAdvanced(&state, 77, 1, 2, 3, ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
}
#[test]
fn init_cstream_using_cdict_advanced_stops_after_pledged_size_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
pledged_result: ERROR(ZstdErrorCode::StageWrong),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_cdict_advanced_test_state(&mut context);
let result =
unsafe { ZSTD_rust_initCStreamUsingCDictAdvanced(&state, 77, 1, 2, 3, ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["reset", "pledged"]);
}
#[test]
fn init_cstream_using_cdict_advanced_propagates_ref_error_after_frame_params() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
ref_result: ERROR(ZstdErrorCode::DictionaryCreationFailed),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_cdict_advanced_test_state(&mut context);
let result =
unsafe { ZSTD_rust_initCStreamUsingCDictAdvanced(&state, 77, 1, 2, 3, ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed));
assert_eq!(context.events, ["reset", "pledged", "frame", "ref-cdict"]);
assert_eq!(context.frame_params, [1, 2, 3]);
}
fn init_cstream_using_cdict_test_state(
context: &mut InitCStreamUsingCDictAdvancedTestContext,
) -> ZSTD_rust_initCStreamUsingCDictState {
ZSTD_rust_initCStreamUsingCDictState {
callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(),
reset_session: init_cstream_using_cdict_advanced_test_reset,
ref_cdict: init_cstream_using_cdict_advanced_test_ref_cdict,
}
}
#[test]
fn init_cstream_using_cdict_preserves_reset_then_reference_order() {
let cdict = ptr::dangling::<c_void>();
let mut context = InitCStreamUsingCDictAdvancedTestContext::default();
let state = init_cstream_using_cdict_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingCDict(&state, cdict) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "ref-cdict"]);
assert_eq!(context.cdict, cdict);
}
#[test]
fn init_cstream_using_cdict_stops_after_reset_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_cdict_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingCDict(&state, ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
}
#[test]
fn init_cstream_using_cdict_propagates_reference_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
ref_result: ERROR(ZstdErrorCode::DictionaryCreationFailed),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_cdict_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingCDict(&state, ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed));
assert_eq!(context.events, ["reset", "ref-cdict"]);
}
fn init_cstream_src_size_test_state(
context: &mut InitCStreamUsingCDictAdvancedTestContext,
) -> ZSTD_rust_initCStreamSrcSizeState {
ZSTD_rust_initCStreamSrcSizeState {
callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(),
reset_session: init_cstream_using_cdict_advanced_test_reset,
ref_cdict: init_cstream_using_cdict_advanced_test_ref_cdict,
set_level: init_cstream_src_size_test_set_level,
set_pledged_src_size: init_cstream_using_cdict_advanced_test_set_pledged,
}
}
#[test]
fn init_cstream_src_size_preserves_order_and_normalizes_unknown_pledge() {
let mut context = InitCStreamUsingCDictAdvancedTestContext::default();
let state = init_cstream_src_size_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 0, -3) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "ref-cdict", "level", "pledged"]);
assert_eq!(context.compression_level, -3);
assert_eq!(context.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN);
assert_eq!(context.cdict, ptr::null());
}
#[test]
fn init_cstream_src_size_stops_after_reset_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_src_size_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
}
#[test]
fn init_cstream_src_size_stops_after_cdict_clear_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
ref_result: ERROR(ZstdErrorCode::DictionaryCreationFailed),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_src_size_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed));
assert_eq!(context.events, ["reset", "ref-cdict"]);
}
#[test]
fn init_cstream_src_size_stops_after_level_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
set_level_result: ERROR(ZstdErrorCode::ParameterOutOfBound),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_src_size_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(context.events, ["reset", "ref-cdict", "level"]);
}
#[test]
fn init_cstream_src_size_propagates_pledge_error_last() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
pledged_result: ERROR(ZstdErrorCode::StageWrong),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_src_size_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["reset", "ref-cdict", "level", "pledged"]);
}
fn init_cstream_test_state(
context: &mut InitCStreamUsingCDictAdvancedTestContext,
) -> ZSTD_rust_initCStreamState {
ZSTD_rust_initCStreamState {
callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(),
reset_session: init_cstream_using_cdict_advanced_test_reset,
ref_cdict: init_cstream_using_cdict_advanced_test_ref_cdict,
set_level: init_cstream_src_size_test_set_level,
}
}
#[test]
fn init_cstream_preserves_reset_clear_and_level_order() {
let mut context = InitCStreamUsingCDictAdvancedTestContext::default();
let state = init_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStream(&state, -3) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "ref-cdict", "level"]);
assert_eq!(context.cdict, ptr::null());
assert_eq!(context.compression_level, -3);
}
#[test]
fn init_cstream_stops_after_reset_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStream(&state, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
}
#[test]
fn init_cstream_stops_after_cdict_clear_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
ref_result: ERROR(ZstdErrorCode::DictionaryCreationFailed),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStream(&state, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed));
assert_eq!(context.events, ["reset", "ref-cdict"]);
}
#[test]
fn init_cstream_propagates_level_error_last() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
set_level_result: ERROR(ZstdErrorCode::ParameterOutOfBound),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStream(&state, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(context.events, ["reset", "ref-cdict", "level"]);
}
fn init_cstream_using_dict_test_state(
context: &mut InitCStreamUsingCDictAdvancedTestContext,
) -> ZSTD_rust_initCStreamUsingDictState {
ZSTD_rust_initCStreamUsingDictState {
callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(),
reset_session: init_cstream_using_cdict_advanced_test_reset,
set_level: init_cstream_src_size_test_set_level,
load_dictionary: init_cstream_using_dict_test_load_dictionary,
}
}
#[test]
fn init_cstream_using_dict_preserves_order_and_dictionary_arguments() {
let dict = ptr::dangling::<c_void>();
let mut context = InitCStreamUsingCDictAdvancedTestContext::default();
let state = init_cstream_using_dict_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, dict, 123, -3) };
assert_eq!(result, 0);
assert_eq!(context.events, ["reset", "level", "load-dict"]);
assert_eq!(context.compression_level, -3);
assert_eq!(context.cdict, dict);
assert_eq!(context.dict_size, 123);
}
#[test]
fn init_cstream_using_dict_stops_after_reset_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_dict_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, ptr::null(), 0, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
}
#[test]
fn init_cstream_using_dict_stops_after_level_error() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
set_level_result: ERROR(ZstdErrorCode::ParameterOutOfBound),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_dict_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, ptr::null(), 0, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(context.events, ["reset", "level"]);
}
#[test]
fn init_cstream_using_dict_propagates_dictionary_error_last() {
let mut context = InitCStreamUsingCDictAdvancedTestContext {
load_dict_result: ERROR(ZstdErrorCode::DictionaryCreationFailed),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_using_dict_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, ptr::null(), 0, 4) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed));
assert_eq!(context.events, ["reset", "level", "load-dict"]);
}
fn init_cstream_advanced_test_state(
context: &mut InitCStreamUsingCDictAdvancedTestContext,
) -> ZSTD_rust_initCStreamAdvancedState {
ZSTD_rust_initCStreamAdvancedState {
callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(),
reset_session: init_cstream_using_cdict_advanced_test_reset,
set_pledged_src_size: init_cstream_using_cdict_advanced_test_set_pledged,
check_c_params: init_cstream_advanced_test_check_c_params,
set_zstd_params: init_cstream_advanced_test_set_zstd_params,
load_dictionary: init_cstream_using_dict_test_load_dictionary,
}
}
fn init_cstream_advanced_test_params(content_size_flag: c_int) -> ZSTD_parameters {
ZSTD_parameters {
cParams: ZSTD_compressionParameters {
windowLog: 10,
chainLog: 11,
hashLog: 12,
searchLog: 13,
minMatch: 4,
targetLength: 5,
strategy: 1,
},
fParams: ZSTD_frameParameters {
contentSizeFlag: content_size_flag,
checksumFlag: 1,
noDictIDFlag: 2,
},
}
}
#[test]
fn init_cstream_advanced_preserves_order_and_normalizes_unknown_pledge() {
let params = init_cstream_advanced_test_params(0);
let dict = ptr::dangling::<c_void>();
let mut context = InitCStreamUsingCDictAdvancedTestContext::default();
let state = init_cstream_advanced_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamAdvanced(&state, &params, 0, dict, 123) };
assert_eq!(result, 0);
assert_eq!(
context.events,
["reset", "pledged", "check", "params", "load-dict"]
);
assert_eq!(context.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN);
assert_eq!(context.zstd_params, params);
assert_eq!(context.cdict, dict);
assert_eq!(context.dict_size, 123);
}
#[test]
fn init_cstream_advanced_keeps_zero_pledge_for_known_empty_frame() {
let params = init_cstream_advanced_test_params(1);
let mut context = InitCStreamUsingCDictAdvancedTestContext::default();
let state = init_cstream_advanced_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamAdvanced(&state, &params, 0, ptr::null(), 0) };
assert_eq!(result, 0);
assert_eq!(context.pledged_src_size, 0);
assert_eq!(
context.events,
["reset", "pledged", "check", "params", "load-dict"]
);
}
#[test]
fn init_cstream_advanced_stops_after_reset_error() {
let params = init_cstream_advanced_test_params(0);
let mut context = InitCStreamUsingCDictAdvancedTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_advanced_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamAdvanced(&state, &params, 77, ptr::null(), 0) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["reset"]);
}
#[test]
fn init_cstream_advanced_stops_after_pledged_size_error() {
let params = init_cstream_advanced_test_params(0);
let mut context = InitCStreamUsingCDictAdvancedTestContext {
pledged_result: ERROR(ZstdErrorCode::StageWrong),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_advanced_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamAdvanced(&state, &params, 77, ptr::null(), 0) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["reset", "pledged"]);
}
#[test]
fn init_cstream_advanced_stops_after_parameter_error() {
let params = init_cstream_advanced_test_params(0);
let mut context = InitCStreamUsingCDictAdvancedTestContext {
check_params_result: ERROR(ZstdErrorCode::ParameterOutOfBound),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_advanced_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamAdvanced(&state, &params, 77, ptr::null(), 0) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(context.events, ["reset", "pledged", "check"]);
}
#[test]
fn init_cstream_advanced_propagates_dictionary_error_last() {
let params = init_cstream_advanced_test_params(0);
let mut context = InitCStreamUsingCDictAdvancedTestContext {
load_dict_result: ERROR(ZstdErrorCode::DictionaryCreationFailed),
..InitCStreamUsingCDictAdvancedTestContext::default()
};
let state = init_cstream_advanced_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCStreamAdvanced(&state, &params, 77, ptr::null(), 0) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed));
assert_eq!(
context.events,
["reset", "pledged", "check", "params", "load-dict"]
);
}
fn set_parameters_using_cctx_params_test_value(params: &ZSTD_CCtx_params) -> c_int {
let mut value = -1;
let result = unsafe { ZSTD_CCtxParams_getParameter(params, 10, &mut value) };
assert_eq!(result, 0);
value
}
fn set_parameters_using_cctx_params_test_state(
requested_params: &mut ZSTD_CCtx_params,
source_params: &ZSTD_CCtx_params,
stream_stage: c_int,
cdict: *const c_void,
) -> ZSTD_rust_setParametersUsingCCtxParamsState {
ZSTD_rust_setParametersUsingCCtxParamsState {
requested_params,
source_params,
stream_stage,
cdict,
}
}
#[test]
fn set_parameters_using_cctx_params_copies_source_after_policy_checks() {
let mut source_params = unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
assert_eq!(
unsafe { ZSTD_CCtxParams_setParameter(&mut source_params, 10, 1) },
1
);
let state = set_parameters_using_cctx_params_test_state(
&mut requested_params,
&source_params,
ZSTD_CSTREAM_STAGE_INIT,
ptr::null(),
);
let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(&state) };
assert_eq!(result, 0);
assert_eq!(
set_parameters_using_cctx_params_test_value(&requested_params),
1
);
}
#[test]
fn set_parameters_using_cctx_params_rejects_non_init_stage_without_copying() {
let mut source_params = unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
assert_eq!(
unsafe { ZSTD_CCtxParams_setParameter(&mut source_params, 10, 1) },
1
);
let state = set_parameters_using_cctx_params_test_state(
&mut requested_params,
&source_params,
ZSTD_CSTREAM_STAGE_LOAD,
ptr::null(),
);
let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(
set_parameters_using_cctx_params_test_value(&requested_params),
0
);
}
#[test]
fn set_parameters_using_cctx_params_rejects_cdict_without_copying() {
let mut source_params = unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
assert_eq!(
unsafe { ZSTD_CCtxParams_setParameter(&mut source_params, 10, 1) },
1
);
let state = set_parameters_using_cctx_params_test_state(
&mut requested_params,
&source_params,
ZSTD_CSTREAM_STAGE_INIT,
ptr::dangling(),
);
let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(
set_parameters_using_cctx_params_test_value(&requested_params),
0
);
}
#[test]
fn set_parameters_using_cctx_params_rejects_null_state() {
let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
}
fn set_parameter_test_state(
requested_params: &mut ZSTD_CCtx_params,
stream_stage: c_int,
c_params_changed: &mut c_int,
static_size: usize,
max_block_size_set: &mut c_uint,
) -> ZSTD_rust_setParameterState {
ZSTD_rust_setParameterState {
requested_params,
stream_stage,
c_params_changed,
static_size,
rust_simple_compress2_max_block_size_set: max_block_size_set,
}
}
fn set_parameter_test_get(requested_params: &ZSTD_CCtx_params, param: c_int) -> (usize, c_int) {
let mut value = -1;
let result = unsafe { ZSTD_CCtxParams_getParameter(requested_params, param, &mut value) };
(result, value)
}
#[test]
fn set_parameter_authorizes_in_flight_updates_and_marks_cparams_changed() {
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut c_params_changed = 0;
let mut max_block_size_set = 0;
let state = set_parameter_test_state(
&mut requested_params,
ZSTD_CSTREAM_STAGE_LOAD,
&mut c_params_changed,
0,
&mut max_block_size_set,
);
let result = unsafe { ZSTD_rust_setParameter(&state, ZSTD_C_COMPRESSION_LEVEL, 5) };
assert_eq!(result, 5);
assert_eq!(c_params_changed, 1);
assert_eq!(max_block_size_set, 0);
assert_eq!(
set_parameter_test_get(&requested_params, ZSTD_C_COMPRESSION_LEVEL),
(0, 5)
);
}
#[test]
fn set_parameter_rejects_unauthorized_stage_updates_without_mutation() {
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut c_params_changed = 0;
let mut max_block_size_set = 0;
let state = set_parameter_test_state(
&mut requested_params,
ZSTD_CSTREAM_STAGE_LOAD,
&mut c_params_changed,
0,
&mut max_block_size_set,
);
let result = unsafe { ZSTD_rust_setParameter(&state, ZSTD_C_FORMAT, 1) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(c_params_changed, 0);
assert_eq!(
set_parameter_test_get(&requested_params, ZSTD_C_FORMAT),
(0, 0)
);
}
#[test]
fn set_parameter_rejects_nonzero_workers_for_static_contexts() {
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut c_params_changed = 0;
let mut max_block_size_set = 0;
let state = set_parameter_test_state(
&mut requested_params,
ZSTD_CSTREAM_STAGE_INIT,
&mut c_params_changed,
1,
&mut max_block_size_set,
);
let result = unsafe { ZSTD_rust_setParameter(&state, ZSTD_C_NB_WORKERS, 1) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterUnsupported));
assert_eq!(c_params_changed, 0);
assert_eq!(
set_parameter_test_get(&requested_params, ZSTD_C_FORMAT),
(0, 0)
);
}
#[test]
fn set_parameter_rejects_unknown_parameters() {
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut c_params_changed = 0;
let mut max_block_size_set = 0;
let state = set_parameter_test_state(
&mut requested_params,
ZSTD_CSTREAM_STAGE_INIT,
&mut c_params_changed,
0,
&mut max_block_size_set,
);
let result = unsafe { ZSTD_rust_setParameter(&state, 12345, 1) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterUnsupported));
assert_eq!(
set_parameter_test_get(&requested_params, ZSTD_C_FORMAT),
(0, 0)
);
}
#[test]
fn set_parameter_marks_max_block_size_after_success() {
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut c_params_changed = 0;
let mut max_block_size_set = 0;
let state = set_parameter_test_state(
&mut requested_params,
ZSTD_CSTREAM_STAGE_INIT,
&mut c_params_changed,
0,
&mut max_block_size_set,
);
let result = unsafe { ZSTD_rust_setParameter(&state, ZSTD_C_MAX_BLOCK_SIZE, 0) };
assert_eq!(result, 0);
assert_eq!(max_block_size_set, 1);
}
#[test]
fn set_parameter_does_not_mark_max_block_size_after_underlying_error() {
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut c_params_changed = 0;
let mut max_block_size_set = 0;
let state = set_parameter_test_state(
&mut requested_params,
ZSTD_CSTREAM_STAGE_INIT,
&mut c_params_changed,
0,
&mut max_block_size_set,
);
let result = unsafe { ZSTD_rust_setParameter(&state, ZSTD_C_MAX_BLOCK_SIZE, 1) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(max_block_size_set, 0);
}
fn ref_thread_pool_test_state(
pool: &mut *mut c_void,
requested_pool: *mut c_void,
stream_stage: c_int,
) -> ZSTD_rust_refThreadPoolState {
ZSTD_rust_refThreadPoolState {
pool,
requested_pool,
stream_stage,
}
}
#[test]
fn ref_thread_pool_assigns_the_requested_pool_at_init_stage() {
let mut pool = ptr::null_mut::<c_void>();
let requested_pool = ptr::dangling_mut::<c_void>();
let state = ref_thread_pool_test_state(&mut pool, requested_pool, ZSTD_CSTREAM_STAGE_INIT);
let result = unsafe { ZSTD_rust_refThreadPool(&state) };
assert_eq!(result, 0);
assert_eq!(pool, requested_pool);
}
#[test]
fn ref_thread_pool_rejects_non_init_stage_without_mutating_the_pool() {
let original_pool = ptr::dangling_mut::<c_void>();
let mut pool = original_pool;
let state = ref_thread_pool_test_state(&mut pool, ptr::null_mut(), ZSTD_CSTREAM_STAGE_LOAD);
let result = unsafe { ZSTD_rust_refThreadPool(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(pool, original_pool);
}
#[test]
fn ref_thread_pool_rejects_null_state() {
let result = unsafe { ZSTD_rust_refThreadPool(ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
}
#[derive(Default)]
struct CreateCCtxTestContext {
events: Vec<&'static str>,
valid: c_int,
allocation: *mut c_void,
requested_size: usize,
initialized: *mut c_void,
}
unsafe extern "C" fn create_cctx_test_validate(context: *mut c_void) -> c_int {
let context = unsafe { &mut *context.cast::<CreateCCtxTestContext>() };
context.events.push("validate");
context.valid
}
unsafe extern "C" fn create_cctx_test_allocate(
context: *mut c_void,
size: usize,
) -> *mut c_void {
let context = unsafe { &mut *context.cast::<CreateCCtxTestContext>() };
context.events.push("allocate");
context.requested_size = size;
context.allocation
}
unsafe extern "C" fn create_cctx_test_init(context: *mut c_void, cctx: *mut c_void) {
let context = unsafe { &mut *context.cast::<CreateCCtxTestContext>() };
context.events.push("init");
context.initialized = cctx;
}
fn create_cctx_test_state(context: &mut CreateCCtxTestContext) -> ZSTD_rust_createCCtxState {
ZSTD_rust_createCCtxState {
callback_context: (context as *mut CreateCCtxTestContext).cast(),
cctx_size: 123,
validate_custom_mem: create_cctx_test_validate,
allocate: create_cctx_test_allocate,
init: create_cctx_test_init,
}
}
#[test]
fn create_cctx_validates_allocates_and_initializes_in_order() {
let allocation = ptr::dangling_mut::<c_void>();
let mut context = CreateCCtxTestContext {
valid: 1,
allocation,
..Default::default()
};
let state = create_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_createCCtx(&state) };
assert_eq!(result, allocation);
assert_eq!(context.events, ["validate", "allocate", "init"]);
assert_eq!(context.requested_size, 123);
assert_eq!(context.initialized, allocation);
}
#[test]
fn create_cctx_stops_before_allocation_for_invalid_memory_callbacks() {
let mut context = CreateCCtxTestContext {
valid: 0,
allocation: ptr::dangling_mut(),
..Default::default()
};
let state = create_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_createCCtx(&state) };
assert!(result.is_null());
assert_eq!(context.events, ["validate"]);
assert_eq!(context.requested_size, 0);
assert!(context.initialized.is_null());
}
#[test]
fn create_cctx_stops_before_initialization_after_allocation_failure() {
let mut context = CreateCCtxTestContext {
valid: 1,
..Default::default()
};
let state = create_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_createCCtx(&state) };
assert!(result.is_null());
assert_eq!(context.events, ["validate", "allocate"]);
assert_eq!(context.requested_size, 123);
assert!(context.initialized.is_null());
}
#[derive(Default)]
struct CreateCDictAdvancedWrapperTestContext {
events: Vec<&'static str>,
init_context: *mut c_void,
init_cctx_params: *mut c_void,
init_c_params: *const ZSTD_compressionParameters,
init_custom_mem: *const c_void,
init_dict_size: usize,
init_dict_content_type: c_int,
create_context: *mut c_void,
create_dict: *const c_void,
create_dict_size: usize,
create_dict_load_method: c_int,
create_dict_content_type: c_int,
create_cctx_params: *const c_void,
create_custom_mem: *const c_void,
result: *mut c_void,
}
unsafe extern "C" fn create_cdict_advanced_wrapper_test_init(
context: *mut c_void,
cctx_params: *mut c_void,
c_params: *const ZSTD_compressionParameters,
custom_mem: *const c_void,
dict_size: usize,
dict_content_type: c_int,
) {
let context = unsafe { &mut *context.cast::<CreateCDictAdvancedWrapperTestContext>() };
context.events.push("init");
context.init_context = context as *mut _ as *mut c_void;
context.init_cctx_params = cctx_params;
context.init_c_params = c_params;
context.init_custom_mem = custom_mem;
context.init_dict_size = dict_size;
context.init_dict_content_type = dict_content_type;
}
unsafe extern "C" fn create_cdict_advanced_wrapper_test_create(
context: *mut c_void,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
cctx_params: *const c_void,
custom_mem: *const c_void,
) -> *mut c_void {
let context = unsafe { &mut *context.cast::<CreateCDictAdvancedWrapperTestContext>() };
context.events.push("create");
context.create_context = context as *mut _ as *mut c_void;
context.create_dict = dict;
context.create_dict_size = dict_size;
context.create_dict_load_method = dict_load_method;
context.create_dict_content_type = dict_content_type;
context.create_cctx_params = cctx_params;
context.create_custom_mem = custom_mem;
context.result
}
fn create_cdict_advanced_wrapper_test_state(
context: &mut CreateCDictAdvancedWrapperTestContext,
cctx_params: *mut c_void,
c_params: *const ZSTD_compressionParameters,
custom_mem: *const c_void,
) -> ZSTD_rust_createCDictAdvancedWrapperState {
ZSTD_rust_createCDictAdvancedWrapperState {
callback_context: context as *mut _ as *mut c_void,
cctx_params,
c_params,
custom_mem,
init_params: create_cdict_advanced_wrapper_test_init,
create: create_cdict_advanced_wrapper_test_create,
}
}
#[test]
fn create_cdict_advanced_wrapper_preserves_prepare_then_create_order() {
let mut context = CreateCDictAdvancedWrapperTestContext {
result: ptr::dangling_mut(),
..Default::default()
};
let cctx_params = ptr::dangling_mut::<c_void>();
let c_params = ptr::dangling::<ZSTD_compressionParameters>();
let custom_mem = ptr::dangling::<c_void>();
let dict = ptr::dangling::<u8>().cast::<c_void>();
let state = create_cdict_advanced_wrapper_test_state(
&mut context,
cctx_params,
c_params,
custom_mem,
);
let result = unsafe { ZSTD_rust_createCDictAdvancedWrapper(&state, dict, 123, 1, 2) };
assert_eq!(result, context.result);
assert_eq!(context.events, ["init", "create"]);
assert_eq!(context.init_context, state.callback_context);
assert_eq!(context.init_cctx_params, cctx_params);
assert_eq!(context.init_c_params, c_params);
assert_eq!(context.init_custom_mem, custom_mem);
assert_eq!(context.init_dict_size, 123);
assert_eq!(context.init_dict_content_type, 2);
assert_eq!(context.create_context, state.callback_context);
assert_eq!(context.create_dict, dict);
assert_eq!(context.create_dict_size, 123);
assert_eq!(context.create_dict_load_method, 1);
assert_eq!(context.create_dict_content_type, 2);
assert_eq!(context.create_cctx_params, cctx_params);
assert_eq!(context.create_custom_mem, custom_mem);
}
#[test]
fn create_cdict_advanced_wrapper_rejects_missing_parameter_storage() {
let mut context = CreateCDictAdvancedWrapperTestContext::default();
let c_params = ptr::dangling::<ZSTD_compressionParameters>();
let custom_mem = ptr::dangling::<c_void>();
let state = create_cdict_advanced_wrapper_test_state(
&mut context,
ptr::null_mut(),
c_params,
custom_mem,
);
let result = unsafe { ZSTD_rust_createCDictAdvancedWrapper(&state, ptr::null(), 0, 0, 0) };
assert!(result.is_null());
assert!(context.events.is_empty());
}
#[derive(Default)]
struct InitCCtxTestContext {
events: Vec<&'static str>,
reset_result: usize,
}
unsafe fn init_cctx_test_context(context: *mut c_void) -> &'static mut InitCCtxTestContext {
unsafe { &mut *context.cast::<InitCCtxTestContext>() }
}
unsafe extern "C" fn init_cctx_test_zero(context: *mut c_void) {
unsafe { init_cctx_test_context(context) }
.events
.push("zero");
}
unsafe extern "C" fn init_cctx_test_set_custom_mem(
context: *mut c_void,
_custom_mem: *const c_void,
) {
unsafe { init_cctx_test_context(context) }
.events
.push("set-custom-mem");
}
unsafe extern "C" fn init_cctx_test_set_bmi2(context: *mut c_void) {
unsafe { init_cctx_test_context(context) }
.events
.push("set-bmi2");
}
unsafe extern "C" fn init_cctx_test_reset(context: *mut c_void) -> usize {
let context = unsafe { init_cctx_test_context(context) };
context.events.push("reset");
context.reset_result
}
fn init_cctx_test_state(context: &mut InitCCtxTestContext) -> ZSTD_rust_initCCtxState {
ZSTD_rust_initCCtxState {
callback_context: (context as *mut InitCCtxTestContext).cast(),
custom_mem: ptr::dangling(),
zero: init_cctx_test_zero,
set_custom_mem: init_cctx_test_set_custom_mem,
set_bmi2: init_cctx_test_set_bmi2,
reset: init_cctx_test_reset,
}
}
#[test]
fn init_cctx_runs_private_initialization_steps_in_order() {
let mut context = InitCCtxTestContext::default();
let state = init_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCCtx(&state) };
assert_eq!(result, 0);
assert_eq!(
context.events,
["zero", "set-custom-mem", "set-bmi2", "reset"]
);
}
#[test]
fn init_cctx_propagates_reset_errors_after_initialization_steps() {
let mut context = InitCCtxTestContext {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..Default::default()
};
let state = init_cctx_test_state(&mut context);
let result = unsafe { ZSTD_rust_initCCtx(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(
context.events,
["zero", "set-custom-mem", "set-bmi2", "reset"]
);
}
#[derive(Default)]
struct InitStaticCCtxTestContext {
events: Vec<&'static str>,
reserved_cctx: *mut c_void,
check_available_result: c_int,
reserve_block_state_failure: Option<usize>,
reserve_block_state_count: usize,
reserve_tmp_workspace_result: *mut c_void,
}
unsafe fn init_static_cctx_test_context(
context: *mut c_void,
) -> &'static mut InitStaticCCtxTestContext {
unsafe { &mut *context.cast::<InitStaticCCtxTestContext>() }
}
unsafe extern "C" fn init_static_cctx_test_create_workspace(context: *mut c_void) {
unsafe { init_static_cctx_test_context(context) }
.events
.push("create-workspace");
}
unsafe extern "C" fn init_static_cctx_test_reserve_object(context: *mut c_void) -> *mut c_void {
let context = unsafe { init_static_cctx_test_context(context) };
context.events.push("reserve-object");
context.reserved_cctx
}
unsafe extern "C" fn init_static_cctx_test_zero(context: *mut c_void, _cctx: *mut c_void) {
unsafe { init_static_cctx_test_context(context) }
.events
.push("zero");
}
unsafe extern "C" fn init_static_cctx_test_move_workspace(
context: *mut c_void,
_cctx: *mut c_void,
) {
unsafe { init_static_cctx_test_context(context) }
.events
.push("move-workspace");
}
unsafe extern "C" fn init_static_cctx_test_check_available(context: *mut c_void) -> c_int {
let context = unsafe { init_static_cctx_test_context(context) };
context.events.push("check-available");
context.check_available_result
}
unsafe extern "C" fn init_static_cctx_test_reserve_block_state(
context: *mut c_void,
block_state: c_int,
) -> *mut c_void {
let context = unsafe { &mut *context.cast::<InitStaticCCtxTestContext>() };
context.events.push(match block_state {
INIT_STATIC_CCTX_PREV_CBLOCK => "reserve-prev-block-state",
INIT_STATIC_CCTX_NEXT_CBLOCK => "reserve-next-block-state",
_ => "reserve-unknown-block-state",
});
let result =
if context.reserve_block_state_failure == Some(context.reserve_block_state_count) {
ptr::null_mut()
} else {
ptr::dangling_mut()
};
context.reserve_block_state_count += 1;
result
}
unsafe extern "C" fn init_static_cctx_test_reserve_tmp_workspace(
context: *mut c_void,
) -> *mut c_void {
let context = unsafe { init_static_cctx_test_context(context) };
context.events.push("reserve-tmp-workspace");
context.reserve_tmp_workspace_result
}
unsafe extern "C" fn init_static_cctx_test_set_bmi2(context: *mut c_void) {
unsafe { init_static_cctx_test_context(context) }
.events
.push("set-bmi2");
}
fn init_static_cctx_test_state(
context: &mut InitStaticCCtxTestContext,
workspace: *mut c_void,
workspace_size: usize,
cctx_size: usize,
) -> ZSTD_rust_initStaticCCtxState {
ZSTD_rust_initStaticCCtxState {
callback_context: (context as *mut InitStaticCCtxTestContext).cast(),
workspace,
workspace_size,
cctx_size,
create_workspace: init_static_cctx_test_create_workspace,
reserve_object: init_static_cctx_test_reserve_object,
zero: init_static_cctx_test_zero,
move_workspace: init_static_cctx_test_move_workspace,
check_available: init_static_cctx_test_check_available,
reserve_block_state: init_static_cctx_test_reserve_block_state,
reserve_tmp_workspace: init_static_cctx_test_reserve_tmp_workspace,
set_bmi2: init_static_cctx_test_set_bmi2,
}
}
#[test]
fn init_static_cctx_runs_private_construction_steps_in_order() {
let mut context = InitStaticCCtxTestContext {
reserved_cctx: ptr::dangling_mut(),
check_available_result: 1,
reserve_tmp_workspace_result: ptr::dangling_mut(),
..Default::default()
};
let mut workspace = [0usize; 1];
let state = init_static_cctx_test_state(
&mut context,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace) - 1,
);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert_eq!(result, context.reserved_cctx);
assert_eq!(
context.events,
[
"create-workspace",
"reserve-object",
"zero",
"move-workspace",
"check-available",
"reserve-prev-block-state",
"reserve-next-block-state",
"reserve-tmp-workspace",
"set-bmi2",
]
);
}
#[test]
fn init_static_cctx_stops_after_cctx_reservation_failure() {
let mut context = InitStaticCCtxTestContext {
check_available_result: 1,
reserve_tmp_workspace_result: ptr::dangling_mut(),
..Default::default()
};
let mut workspace = [0usize; 1];
let state = init_static_cctx_test_state(
&mut context,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace),
);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert!(result.is_null());
assert_eq!(context.events, ["create-workspace", "reserve-object"]);
}
#[test]
fn init_static_cctx_stops_after_workspace_capacity_failure() {
let mut context = InitStaticCCtxTestContext {
reserved_cctx: ptr::dangling_mut(),
reserve_tmp_workspace_result: ptr::dangling_mut(),
..Default::default()
};
let mut workspace = [0usize; 1];
let state = init_static_cctx_test_state(
&mut context,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace) - 1,
);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert!(result.is_null());
assert_eq!(
context.events,
[
"create-workspace",
"reserve-object",
"zero",
"move-workspace",
"check-available",
]
);
}
#[test]
fn init_static_cctx_stops_after_block_state_reservation_failure() {
let mut context = InitStaticCCtxTestContext {
reserved_cctx: ptr::dangling_mut(),
check_available_result: 1,
reserve_block_state_failure: Some(1),
reserve_tmp_workspace_result: ptr::dangling_mut(),
..Default::default()
};
let mut workspace = [0usize; 1];
let state = init_static_cctx_test_state(
&mut context,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace) - 1,
);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert!(result.is_null());
assert_eq!(
context.events,
[
"create-workspace",
"reserve-object",
"zero",
"move-workspace",
"check-available",
"reserve-prev-block-state",
"reserve-next-block-state",
]
);
}
#[test]
fn init_static_cctx_stops_after_tmp_workspace_reservation_failure() {
let mut context = InitStaticCCtxTestContext {
reserved_cctx: ptr::dangling_mut(),
check_available_result: 1,
reserve_tmp_workspace_result: ptr::null_mut(),
..Default::default()
};
let mut workspace = [0usize; 1];
let state = init_static_cctx_test_state(
&mut context,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace) - 1,
);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert!(result.is_null());
assert_eq!(
context.events,
[
"create-workspace",
"reserve-object",
"zero",
"move-workspace",
"check-available",
"reserve-prev-block-state",
"reserve-next-block-state",
"reserve-tmp-workspace",
]
);
}
#[test]
fn init_static_cctx_rejects_a_null_state() {
let result = unsafe { ZSTD_rust_initStaticCCtx(ptr::null()) };
assert!(result.is_null());
}
#[test]
fn init_static_cctx_rejects_a_null_workspace() {
let mut context = InitStaticCCtxTestContext {
reserved_cctx: ptr::dangling_mut(),
check_available_result: 1,
reserve_tmp_workspace_result: ptr::dangling_mut(),
..Default::default()
};
let state = init_static_cctx_test_state(&mut context, ptr::null_mut(), usize::MAX, 0);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert!(result.is_null());
assert!(context.events.is_empty());
}
#[test]
fn init_static_cctx_rejects_an_unaligned_workspace_before_callback() {
let mut context = InitStaticCCtxTestContext::default();
let mut workspace = [0usize; 2];
let unaligned_workspace = workspace.as_mut_ptr().cast::<u8>().wrapping_add(1).cast();
let state = init_static_cctx_test_state(&mut context, unaligned_workspace, usize::MAX, 0);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert!(result.is_null());
assert!(context.events.is_empty());
}
#[test]
fn init_static_cctx_rejects_a_small_workspace_before_callback() {
let mut context = InitStaticCCtxTestContext::default();
let mut workspace = [0usize; 1];
let state = init_static_cctx_test_state(
&mut context,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace),
);
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
assert!(result.is_null());
assert!(context.events.is_empty());
}
#[derive(Default)]
struct FreeCCtxTestContext {
events: Vec<&'static str>,
}
unsafe extern "C" fn free_cctx_test_content(context: *mut c_void) {
let context = unsafe { &mut *context.cast::<FreeCCtxTestContext>() };
context.events.push("content");
}
unsafe extern "C" fn free_cctx_test_object(context: *mut c_void) {
let context = unsafe { &mut *context.cast::<FreeCCtxTestContext>() };
context.events.push("object");
}
fn free_cctx_test_state(
context: *mut c_void,
static_size: usize,
cctx_in_workspace: c_int,
) -> ZSTD_rust_freeCCtxState {
ZSTD_rust_freeCCtxState {
callback_context: context,
static_size,
cctx_in_workspace,
free_content: free_cctx_test_content,
free_object: free_cctx_test_object,
}
}
#[test]
fn free_cctx_frees_content_before_an_external_object() {
let mut context = FreeCCtxTestContext::default();
let state = free_cctx_test_state((&mut context as *mut FreeCCtxTestContext).cast(), 0, 0);
let result = unsafe { ZSTD_rust_freeCCtx(&state) };
assert_eq!(result, 0);
assert_eq!(context.events, ["content", "object"]);
}
#[test]
fn free_cctx_skips_the_external_object_when_embedded_in_workspace() {
let mut context = FreeCCtxTestContext::default();
let state = free_cctx_test_state((&mut context as *mut FreeCCtxTestContext).cast(), 0, 1);
let result = unsafe { ZSTD_rust_freeCCtx(&state) };
assert_eq!(result, 0);
assert_eq!(context.events, ["content"]);
}
#[test]
fn free_cctx_rejects_static_contexts_before_any_callback() {
let mut context = FreeCCtxTestContext::default();
let state = free_cctx_test_state((&mut context as *mut FreeCCtxTestContext).cast(), 1, 0);
let result = unsafe { ZSTD_rust_freeCCtx(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert!(context.events.is_empty());
}
#[test]
fn free_cctx_accepts_a_null_context() {
let state = free_cctx_test_state(ptr::null_mut(), 0, 0);
let result = unsafe { ZSTD_rust_freeCCtx(&state) };
assert_eq!(result, 0);
}
#[derive(Default)]
struct SetCParamsTestContext {
events: Vec<&'static str>,
parameter_calls: Vec<(c_int, c_int)>,
check_result: usize,
set_result: usize,
fail_at: usize,
}
unsafe fn set_cparams_test_context(context: *mut c_void) -> &'static mut SetCParamsTestContext {
unsafe { &mut *context.cast::<SetCParamsTestContext>() }
}
unsafe extern "C" fn set_cparams_test_check(
context: *mut c_void,
_cparams: ZSTD_compressionParameters,
) -> usize {
let context = unsafe { set_cparams_test_context(context) };
context.events.push("check");
context.check_result
}
unsafe extern "C" fn set_cparams_test_set_parameter(
context: *mut c_void,
param: c_int,
value: c_int,
) -> usize {
let context = unsafe { set_cparams_test_context(context) };
context.events.push("set");
context.parameter_calls.push((param, value));
if context.fail_at != 0 && context.parameter_calls.len() == context.fail_at {
context.set_result
} else {
0
}
}
fn set_cparams_test_state(context: &mut SetCParamsTestContext) -> ZSTD_rust_setCParamsState {
ZSTD_rust_setCParamsState {
callback_context: (context as *mut SetCParamsTestContext).cast(),
check_c_params: set_cparams_test_check,
set_parameter: set_cparams_test_set_parameter,
}
}
fn set_cparams_test_params() -> ZSTD_compressionParameters {
ZSTD_compressionParameters {
windowLog: 10,
chainLog: 11,
hashLog: 12,
searchLog: 13,
minMatch: 4,
targetLength: 5,
strategy: 6,
}
}
#[test]
fn set_cparams_preserves_validation_and_parameter_order() {
let cparams = set_cparams_test_params();
let mut context = SetCParamsTestContext::default();
let state = set_cparams_test_state(&mut context);
let result = unsafe { ZSTD_rust_setCParams(&state, cparams) };
assert_eq!(result, 0);
assert_eq!(
context.events,
["check", "set", "set", "set", "set", "set", "set", "set"]
);
assert_eq!(
context.parameter_calls,
[
(ZSTD_C_WINDOW_LOG, 10),
(ZSTD_C_CHAIN_LOG, 11),
(ZSTD_C_HASH_LOG, 12),
(ZSTD_C_SEARCH_LOG, 13),
(ZSTD_C_MIN_MATCH, 4),
(ZSTD_C_TARGET_LENGTH, 5),
(ZSTD_C_STRATEGY, 6),
]
);
}
#[test]
fn set_cparams_stops_before_updates_after_validation_error() {
let cparams = set_cparams_test_params();
let mut context = SetCParamsTestContext {
check_result: ERROR(ZstdErrorCode::ParameterOutOfBound),
..SetCParamsTestContext::default()
};
let state = set_cparams_test_state(&mut context);
let result = unsafe { ZSTD_rust_setCParams(&state, cparams) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(context.events, ["check"]);
assert!(context.parameter_calls.is_empty());
}
#[test]
fn set_cparams_propagates_parameter_error_after_prior_updates() {
let cparams = set_cparams_test_params();
let mut context = SetCParamsTestContext {
set_result: ERROR(ZstdErrorCode::StageWrong),
fail_at: 4,
..SetCParamsTestContext::default()
};
let state = set_cparams_test_state(&mut context);
let result = unsafe { ZSTD_rust_setCParams(&state, cparams) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["check", "set", "set", "set", "set"]);
assert_eq!(context.parameter_calls.len(), 4);
}
#[derive(Default)]
struct SetFParamsTestContext {
parameter_calls: Vec<(c_int, c_int)>,
set_result: usize,
fail_at: usize,
}
unsafe fn set_fparams_test_context(context: *mut c_void) -> &'static mut SetFParamsTestContext {
unsafe { &mut *context.cast::<SetFParamsTestContext>() }
}
unsafe extern "C" fn set_fparams_test_set_parameter(
context: *mut c_void,
param: c_int,
value: c_int,
) -> usize {
let context = unsafe { set_fparams_test_context(context) };
context.parameter_calls.push((param, value));
if context.fail_at != 0 && context.parameter_calls.len() == context.fail_at {
context.set_result
} else {
0
}
}
fn set_fparams_test_state(context: &mut SetFParamsTestContext) -> ZSTD_rust_setFParamsState {
ZSTD_rust_setFParamsState {
callback_context: (context as *mut SetFParamsTestContext).cast(),
set_parameter: set_fparams_test_set_parameter,
}
}
#[test]
fn set_fparams_preserves_parameter_order_and_boolean_conversions() {
let fparams = ZSTD_frameParameters {
contentSizeFlag: 0,
checksumFlag: 2,
noDictIDFlag: 7,
};
let mut context = SetFParamsTestContext::default();
let state = set_fparams_test_state(&mut context);
let result = unsafe { ZSTD_rust_setFParams(&state, fparams) };
assert_eq!(result, 0);
assert_eq!(
context.parameter_calls,
[
(ZSTD_C_CONTENT_SIZE_FLAG, 0),
(ZSTD_C_CHECKSUM_FLAG, 1),
(ZSTD_C_DICT_ID_FLAG, 0),
]
);
}
#[test]
fn set_fparams_stops_after_the_first_parameter_error() {
let fparams = ZSTD_frameParameters {
contentSizeFlag: 1,
checksumFlag: 1,
noDictIDFlag: 0,
};
let mut context = SetFParamsTestContext {
set_result: ERROR(ZstdErrorCode::StageWrong),
fail_at: 1,
..SetFParamsTestContext::default()
};
let state = set_fparams_test_state(&mut context);
let result = unsafe { ZSTD_rust_setFParams(&state, fparams) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.parameter_calls, [(ZSTD_C_CONTENT_SIZE_FLAG, 1)]);
}
#[test]
fn set_fparams_propagates_a_later_parameter_error_without_following_updates() {
let fparams = ZSTD_frameParameters {
contentSizeFlag: 1,
checksumFlag: 0,
noDictIDFlag: 0,
};
let mut context = SetFParamsTestContext {
set_result: ERROR(ZstdErrorCode::ParameterOutOfBound),
fail_at: 2,
..SetFParamsTestContext::default()
};
let state = set_fparams_test_state(&mut context);
let result = unsafe { ZSTD_rust_setFParams(&state, fparams) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(
context.parameter_calls,
[(ZSTD_C_CONTENT_SIZE_FLAG, 1), (ZSTD_C_CHECKSUM_FLAG, 0),]
);
}
#[derive(Default)]
struct SetParamsTestContext {
events: Vec<&'static str>,
received_cparams: Option<ZSTD_compressionParameters>,
received_fparams: Option<ZSTD_frameParameters>,
check_result: usize,
f_params_result: usize,
c_params_result: usize,
}
unsafe fn set_params_test_context(context: *mut c_void) -> &'static mut SetParamsTestContext {
unsafe { &mut *context.cast::<SetParamsTestContext>() }
}
unsafe extern "C" fn set_params_test_check(
context: *mut c_void,
cparams: ZSTD_compressionParameters,
) -> usize {
let context = unsafe { set_params_test_context(context) };
context.events.push("check");
context.received_cparams = Some(cparams);
context.check_result
}
unsafe extern "C" fn set_params_test_set_f_params(
context: *mut c_void,
fparams: ZSTD_frameParameters,
) -> usize {
let context = unsafe { set_params_test_context(context) };
context.events.push("f-params");
context.received_fparams = Some(fparams);
context.f_params_result
}
unsafe extern "C" fn set_params_test_set_c_params(
context: *mut c_void,
cparams: ZSTD_compressionParameters,
) -> usize {
let context = unsafe { set_params_test_context(context) };
context.events.push("c-params");
context.received_cparams = Some(cparams);
context.c_params_result
}
fn set_params_test_state(context: &mut SetParamsTestContext) -> ZSTD_rust_setParamsState {
ZSTD_rust_setParamsState {
callback_context: (context as *mut SetParamsTestContext).cast(),
check_c_params: set_params_test_check,
set_f_params: set_params_test_set_f_params,
set_c_params: set_params_test_set_c_params,
}
}
fn set_params_test_params() -> ZSTD_parameters {
ZSTD_parameters {
cParams: ZSTD_compressionParameters {
windowLog: 20,
chainLog: 19,
hashLog: 18,
searchLog: 5,
minMatch: 4,
targetLength: 32,
strategy: 3,
},
fParams: ZSTD_frameParameters {
contentSizeFlag: 1,
checksumFlag: 0,
noDictIDFlag: 1,
},
}
}
#[test]
fn set_params_preserves_check_frame_then_compression_order() {
let params = set_params_test_params();
let mut context = SetParamsTestContext::default();
let state = set_params_test_state(&mut context);
let result = unsafe { ZSTD_rust_setParams(&state, params) };
assert_eq!(result, 0);
assert_eq!(context.events, ["check", "f-params", "c-params"]);
assert_eq!(context.received_cparams, Some(params.cParams));
assert_eq!(context.received_fparams, Some(params.fParams));
}
#[test]
fn set_params_stops_before_frame_and_compression_after_check_error() {
let params = set_params_test_params();
let mut context = SetParamsTestContext {
check_result: ERROR(ZstdErrorCode::ParameterOutOfBound),
..SetParamsTestContext::default()
};
let state = set_params_test_state(&mut context);
let result = unsafe { ZSTD_rust_setParams(&state, params) };
assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound));
assert_eq!(context.events, ["check"]);
assert!(context.received_fparams.is_none());
}
#[test]
fn set_params_stops_before_compression_after_frame_error() {
let params = set_params_test_params();
let mut context = SetParamsTestContext {
f_params_result: ERROR(ZstdErrorCode::StageWrong),
..SetParamsTestContext::default()
};
let state = set_params_test_state(&mut context);
let result = unsafe { ZSTD_rust_setParams(&state, params) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["check", "f-params"]);
assert!(context.received_cparams.is_some());
}
#[test]
fn set_params_propagates_compression_error_after_prior_stages() {
let params = set_params_test_params();
let mut context = SetParamsTestContext {
c_params_result: ERROR(ZstdErrorCode::StageWrong),
..SetParamsTestContext::default()
};
let state = set_params_test_state(&mut context);
let result = unsafe { ZSTD_rust_setParams(&state, params) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["check", "f-params", "c-params"]);
}
#[test]
fn pledged_src_size_writes_the_init_stage_value_plus_one() {
let mut pledged_src_size_plus_one = 0;
let result = unsafe {
ZSTD_rust_setPledgedSrcSize(
ZSTD_CSTREAM_STAGE_INIT,
123,
&mut pledged_src_size_plus_one,
)
};
assert_eq!(result, 0);
assert_eq!(pledged_src_size_plus_one, 124);
}
#[test]
fn pledged_src_size_wraps_at_u64_max() {
let mut pledged_src_size_plus_one = 123;
let result = unsafe {
ZSTD_rust_setPledgedSrcSize(
ZSTD_CSTREAM_STAGE_INIT,
u64::MAX,
&mut pledged_src_size_plus_one,
)
};
assert_eq!(result, 0);
assert_eq!(pledged_src_size_plus_one, 0);
}
#[test]
fn pledged_src_size_rejects_non_init_stage_with_stage_wrong() {
let mut pledged_src_size_plus_one = 123;
let result = unsafe { ZSTD_rust_setPledgedSrcSize(1, 456, &mut pledged_src_size_plus_one) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(
crate::errors::ERR_getErrorCode(result),
ZstdErrorCode::StageWrong as i32
);
assert_eq!(pledged_src_size_plus_one, 123);
}
#[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 sizeof_cdict_handles_zero_components() {
assert_eq!(sizeof_cdict(0, 0), 0);
assert_eq!(ZSTD_rust_sizeofCDict(0, 0), 0);
}
#[test]
fn sizeof_cdict_adds_components_in_order() {
assert_eq!(sizeof_cdict(17, 25), 42);
assert_eq!(ZSTD_rust_sizeofCDict(17, 25), 42);
}
#[test]
fn sizeof_cdict_wraps_like_c_size_t_addition() {
assert_eq!(sizeof_cdict(usize::MAX, 1), 0);
assert_eq!(ZSTD_rust_sizeofCDict(usize::MAX, 1), 0);
}
#[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 estimate_workspace_size_handles_zero_components() {
assert_eq!(estimate_workspace_size(0, 0, 0, 0, 0, 0, 0, 0, 0), 0);
assert_eq!(
ZSTD_rust_estimateWorkspaceSize(0, 0, 0, 0, 0, 0, 0, 0, 0),
0
);
}
#[test]
fn estimate_workspace_size_adds_components_in_order() {
assert_eq!(estimate_workspace_size(1, 2, 3, 4, 5, 6, 7, 8, 9), 45);
assert_eq!(
ZSTD_rust_estimateWorkspaceSize(1, 2, 3, 4, 5, 6, 7, 8, 9),
45
);
}
#[test]
fn estimate_workspace_size_wraps_at_multiple_operand_positions() {
assert_eq!(
estimate_workspace_size(usize::MAX, 1, 0, 0, 0, 0, 0, 0, 0),
0
);
assert_eq!(
estimate_workspace_size(0, usize::MAX, 1, 0, 0, 0, 0, 0, 0),
0
);
assert_eq!(
estimate_workspace_size(0, 0, 0, 0, 0, usize::MAX, 1, 0, 0),
0
);
assert_eq!(
ZSTD_rust_estimateWorkspaceSize(0, 0, 0, 0, 0, 0, 0, usize::MAX, 1),
0
);
}
fn cctx_workspace_test_sizing() -> ZSTD_rustCCtxWorkspaceSizing {
ZSTD_rustCCtxWorkspaceSizing {
cctxSize: 32,
compressedBlockStateSize: 24,
seqDefSize: 8,
rawSeqSize: 12,
externalSequenceSize: 16,
tmpWorkspaceSize: 64,
wildcopyOverlength: 32,
matchTSize: 4,
optimalTSize: 8,
asanRedzoneSize: 0,
}
}
fn cctx_workspace_test_cparams() -> ZSTD_compressionParameters {
ZSTD_compressionParameters {
windowLog: 10,
chainLog: 6,
hashLog: 6,
searchLog: 1,
minMatch: 4,
targetLength: 16,
strategy: 3,
}
}
fn cctx_workspace_test_estimate(
sizing: &ZSTD_rustCCtxWorkspaceSizing,
ldm_enable: c_int,
ldm_hash_log: u32,
ldm_bucket_size_log: u32,
ldm_min_match_length: u32,
is_static: c_int,
buff_in_size: usize,
buff_out_size: usize,
use_sequence_producer: c_int,
) -> usize {
unsafe {
ZSTD_rust_estimateCCtxWorkspaceSize(
cctx_workspace_test_cparams(),
ldm_enable,
ldm_hash_log,
ldm_bucket_size_log,
ldm_min_match_length,
is_static,
ZSTD_RUST_PS_DISABLE,
buff_in_size,
buff_out_size,
1000,
use_sequence_producer,
0,
sizing,
)
}
}
fn cctx_reset_test_state(
sizing: &ZSTD_rustCCtxWorkspaceSizing,
plan: &mut ZSTD_rustCCtxResetPlan,
pledged_src_size: u64,
max_block_size: usize,
ldm_enable: c_int,
ldm_min_match_length: u32,
in_buffer_buffered: c_int,
out_buffer_buffered: c_int,
use_sequence_producer: c_int,
initialized: c_int,
index_too_close: c_int,
dict_too_big: c_int,
) -> ZSTD_rustCCtxResetState {
ZSTD_rustCCtxResetState {
cParams: cctx_workspace_test_cparams(),
ldmEnable: ldm_enable,
ldmHashLog: 4,
ldmBucketSizeLog: 2,
ldmMinMatchLength: ldm_min_match_length,
isStatic: 0,
useRowMatchFinder: ZSTD_RUST_PS_DISABLE,
inBufferBuffered: in_buffer_buffered,
outBufferBuffered: out_buffer_buffered,
useSequenceProducer: use_sequence_producer,
initialized,
indexTooClose: index_too_close,
dictTooBig: dict_too_big,
pledgedSrcSize: pledged_src_size,
maxBlockSize: max_block_size,
sizing,
plan,
}
}
#[test]
fn cctx_reset_plan_computes_window_buffers_and_sequence_capacities() {
let sizing = cctx_workspace_test_sizing();
let mut plan = ZSTD_rustCCtxResetPlan::default();
let state = cctx_reset_test_state(
&sizing,
&mut plan,
1000,
512,
ZSTD_RUST_PS_DISABLE,
0,
1,
1,
1,
1,
0,
0,
);
assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0);
assert_eq!(plan.windowSize, 1000);
assert_eq!(plan.blockSize, 512);
assert_eq!(plan.maxNbSeq, 512 / 3);
assert_eq!(plan.buffInSize, 1000 + 512);
assert_eq!(
plan.buffOutSize,
crate::zstd_compress_api::ZSTD_compressBound(512) + 1
);
assert_eq!(
plan.maxNbExternalSeq,
crate::zstd_compress_api::ZSTD_sequenceBound(512)
);
assert_eq!(plan.maxNbLdmSeq, 0);
assert_eq!(plan.needsIndexReset, 0);
assert!(!ERR_isError(plan.neededSpace));
assert_ne!(plan.neededSpace, 0);
}
#[test]
fn cctx_reset_plan_tracks_ldm_and_index_reset_policy() {
let sizing = cctx_workspace_test_sizing();
let mut plan = ZSTD_rustCCtxResetPlan::default();
let mut state = cctx_reset_test_state(
&sizing,
&mut plan,
u64::MAX,
2048,
ZSTD_RUST_PS_ENABLE,
64,
0,
0,
0,
0,
0,
0,
);
assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0);
assert_eq!(plan.windowSize, 1 << 10);
assert_eq!(plan.blockSize, 1 << 10);
assert_eq!(plan.maxNbLdmSeq, (1 << 10) / 64);
assert_eq!(plan.buffInSize, 0);
assert_eq!(plan.buffOutSize, 0);
assert_eq!(plan.needsIndexReset, 1);
state.initialized = 1;
state.indexTooClose = 1;
assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0);
assert_eq!(plan.needsIndexReset, 1);
state.indexTooClose = 0;
state.dictTooBig = 1;
assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0);
assert_eq!(plan.needsIndexReset, 1);
}
#[test]
fn cctx_reset_plan_rejects_unadjusted_enabled_ldm() {
let sizing = cctx_workspace_test_sizing();
let mut plan = ZSTD_rustCCtxResetPlan::default();
let state = cctx_reset_test_state(
&sizing,
&mut plan,
1000,
512,
ZSTD_RUST_PS_ENABLE,
0,
0,
0,
0,
1,
0,
0,
);
assert_eq!(
unsafe { ZSTD_rust_planCCtxReset(&state) },
ERROR(ZstdErrorCode::Generic)
);
}
#[test]
fn cctx_reset_policy_preserves_plan_workspace_tail_order() {
let events = std::cell::RefCell::new(Vec::new());
let result = run_cctx_reset_policy(
|| {
events.borrow_mut().push("plan");
0
},
|| events.borrow_mut().push("prepare-workspace"),
|| {
events.borrow_mut().push("workspace");
0
},
|| events.borrow_mut().push("prepare-tail"),
|| {
events.borrow_mut().push("tail");
0
},
);
assert_eq!(result, 0);
assert_eq!(
events.into_inner(),
[
"plan",
"prepare-workspace",
"workspace",
"prepare-tail",
"tail"
]
);
}
#[test]
fn cctx_reset_policy_propagates_workspace_error_before_tail() {
let events = std::cell::RefCell::new(Vec::new());
let workspace_error = ERROR(ZstdErrorCode::MemoryAllocation);
let result = run_cctx_reset_policy(
|| {
events.borrow_mut().push("plan");
0
},
|| events.borrow_mut().push("prepare-workspace"),
|| {
events.borrow_mut().push("workspace");
workspace_error
},
|| events.borrow_mut().push("prepare-tail"),
|| {
events.borrow_mut().push("tail");
0
},
);
assert_eq!(result, workspace_error);
assert_eq!(
events.into_inner(),
["plan", "prepare-workspace", "workspace"]
);
}
#[derive(Debug, PartialEq)]
enum ResetCCtxStorageTestEvent {
Reserve(c_int, usize),
Pointer(c_int),
Size(c_int, usize),
Int(c_int, c_int),
Zero(usize),
WindowInit,
ResetExternalSequences,
}
#[derive(Default)]
struct ResetCCtxStorageTestContext {
events: Vec<ResetCCtxStorageTestEvent>,
reserve_count: usize,
reserve_failure: bool,
}
unsafe fn reset_cctx_storage_test_context(
context: *mut c_void,
) -> &'static mut ResetCCtxStorageTestContext {
unsafe { &mut *context.cast::<ResetCCtxStorageTestContext>() }
}
unsafe extern "C" fn reset_cctx_storage_test_set_pointer(
context: *mut c_void,
kind: c_int,
_pointer: *mut c_void,
) {
let context = unsafe { reset_cctx_storage_test_context(context) };
context
.events
.push(ResetCCtxStorageTestEvent::Pointer(kind));
}
unsafe extern "C" fn reset_cctx_storage_test_set_size(
context: *mut c_void,
kind: c_int,
value: usize,
) {
let context = unsafe { reset_cctx_storage_test_context(context) };
context
.events
.push(ResetCCtxStorageTestEvent::Size(kind, value));
}
unsafe extern "C" fn reset_cctx_storage_test_set_int(
context: *mut c_void,
kind: c_int,
value: c_int,
) {
let context = unsafe { reset_cctx_storage_test_context(context) };
context
.events
.push(ResetCCtxStorageTestEvent::Int(kind, value));
}
unsafe extern "C" fn reset_cctx_storage_test_reserve(
context: *mut c_void,
kind: c_int,
size: usize,
) -> *mut c_void {
let context = unsafe { reset_cctx_storage_test_context(context) };
context
.events
.push(ResetCCtxStorageTestEvent::Reserve(kind, size));
let pointer = (0x1000usize + context.reserve_count * 0x1000) as *mut c_void;
context.reserve_count += 1;
pointer
}
unsafe extern "C" fn reset_cctx_storage_test_reserve_failed(context: *mut c_void) -> c_int {
let context = unsafe { reset_cctx_storage_test_context(context) };
c_int::from(context.reserve_failure)
}
unsafe extern "C" fn reset_cctx_storage_test_zero(
context: *mut c_void,
_pointer: *mut c_void,
size: usize,
) {
let context = unsafe { reset_cctx_storage_test_context(context) };
context.events.push(ResetCCtxStorageTestEvent::Zero(size));
}
unsafe extern "C" fn reset_cctx_storage_test_window_init(context: *mut c_void) {
let context = unsafe { reset_cctx_storage_test_context(context) };
context.events.push(ResetCCtxStorageTestEvent::WindowInit);
}
unsafe extern "C" fn reset_cctx_storage_test_reset_external_sequences(context: *mut c_void) {
let context = unsafe { reset_cctx_storage_test_context(context) };
context
.events
.push(ResetCCtxStorageTestEvent::ResetExternalSequences);
}
fn reset_cctx_storage_test_state(
context: &mut ResetCCtxStorageTestContext,
ldm_enable: c_int,
has_ext_seq_prod: c_int,
block_size: usize,
max_nb_seq: usize,
max_nb_ldm_seq: usize,
max_nb_external_seq: usize,
buff_in_size: usize,
buff_out_size: usize,
) -> ZSTD_rust_resetCCtxStorageState {
ZSTD_rust_resetCCtxStorageState {
callbackContext: (context as *mut ResetCCtxStorageTestContext).cast(),
ldmEnable: ldm_enable,
hasExtSeqProd: has_ext_seq_prod,
hashLog: 4,
bucketSizeLog: 2,
blockSize: block_size,
maxNbSeq: max_nb_seq,
maxNbLdmSeq: max_nb_ldm_seq,
maxNbExternalSeq: max_nb_external_seq,
buffInSize: buff_in_size,
buffOutSize: buff_out_size,
seqDefSize: size_of::<SeqDef>(),
ldmEntrySize: 8,
rawSeqSize: 12,
externalSequenceSize: size_of::<ZSTD_Sequence>(),
byteSize: 1,
wildcopyOverlength: 8,
bufferedPolicy: ZSTD_BM_BUFFERED,
setPointer: Some(reset_cctx_storage_test_set_pointer),
setSize: Some(reset_cctx_storage_test_set_size),
setInt: Some(reset_cctx_storage_test_set_int),
reserve: Some(reset_cctx_storage_test_reserve),
reserveFailed: Some(reset_cctx_storage_test_reserve_failed),
zero: Some(reset_cctx_storage_test_zero),
windowInit: Some(reset_cctx_storage_test_window_init),
resetExternalSequences: Some(reset_cctx_storage_test_reset_external_sequences),
}
}
#[test]
fn cctx_storage_preserves_ordinary_reservation_and_publication_order() {
let mut context = ResetCCtxStorageTestContext::default();
let state = reset_cctx_storage_test_state(
&mut context,
ZSTD_RUST_PS_DISABLE,
0,
128,
4,
0,
0,
0,
0,
);
assert_eq!(unsafe { ZSTD_rust_resetCCtxStorage(&state) }, 0);
assert_eq!(
context.events,
[
ResetCCtxStorageTestEvent::Reserve(
RESET_CCTX_RESERVE_ALIGNED64,
4 * size_of::<SeqDef>()
),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_SEQ_START),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 136),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LITERALS),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_LIT, 128),
ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_BUFFERED_POLICY, ZSTD_BM_BUFFERED),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 0),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_INPUT_BUFFER),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_INPUT_BUFFER, 0),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 0),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OUTPUT_BUFFER),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_OUTPUT_BUFFER, 0),
ResetCCtxStorageTestEvent::ResetExternalSequences,
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_SEQ, 4),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LL_CODE),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_ML_CODE),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OF_CODE),
ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_INITIALIZED, 1),
]
);
}
#[test]
fn cctx_storage_preserves_ldm_and_external_sequence_order() {
let mut context = ResetCCtxStorageTestContext::default();
let state = reset_cctx_storage_test_state(
&mut context,
ZSTD_RUST_PS_ENABLE,
1,
128,
4,
2,
3,
17,
19,
);
let external_size = 3 * size_of::<ZSTD_Sequence>();
assert_eq!(unsafe { ZSTD_rust_resetCCtxStorage(&state) }, 0);
assert_eq!(
context.events,
[
ResetCCtxStorageTestEvent::Reserve(
RESET_CCTX_RESERVE_ALIGNED64,
4 * size_of::<SeqDef>()
),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_SEQ_START),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_ALIGNED64, 16 * 8),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LDM_HASH),
ResetCCtxStorageTestEvent::Zero(16 * 8),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_ALIGNED64, 2 * 12),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LDM_SEQUENCES),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_LDM_SEQ, 2),
ResetCCtxStorageTestEvent::WindowInit,
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_ALIGNED64, external_size),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_EXTERNAL_SEQUENCES),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_EXTERNAL_SEQ_CAPACITY, 3),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 136),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LITERALS),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_LIT, 128),
ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_BUFFERED_POLICY, ZSTD_BM_BUFFERED),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 17),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_INPUT_BUFFER),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_INPUT_BUFFER, 17),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 19),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OUTPUT_BUFFER),
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_OUTPUT_BUFFER, 19),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LDM_BUCKETS),
ResetCCtxStorageTestEvent::Zero(4),
ResetCCtxStorageTestEvent::ResetExternalSequences,
ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_SEQ, 4),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LL_CODE),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_ML_CODE),
ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4),
ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OF_CODE),
ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_INITIALIZED, 1),
]
);
}
#[test]
fn cctx_storage_propagates_reservation_failure_before_publication() {
let mut context = ResetCCtxStorageTestContext {
reserve_failure: true,
..ResetCCtxStorageTestContext::default()
};
let state = reset_cctx_storage_test_state(
&mut context,
ZSTD_RUST_PS_DISABLE,
0,
128,
4,
0,
0,
0,
0,
);
assert_eq!(
unsafe { ZSTD_rust_resetCCtxStorage(&state) },
ERROR(ZstdErrorCode::MemoryAllocation)
);
assert_eq!(
context.events,
[ResetCCtxStorageTestEvent::Reserve(
RESET_CCTX_RESERVE_ALIGNED64,
4 * size_of::<SeqDef>()
)]
);
}
#[derive(Debug, PartialEq)]
enum ResetCCtxWorkspaceTestEvent {
BumpOversizedDuration,
FreeWorkspace,
CreateWorkspace(usize),
ReserveObject(usize),
Pointer(c_int),
Size(c_int, usize),
ClearWorkspace,
}
#[derive(Default)]
struct ResetCCtxWorkspaceTestContext {
events: Vec<ResetCCtxWorkspaceTestEvent>,
reserve_count: usize,
create_result: usize,
}
unsafe fn reset_cctx_workspace_test_context(
context: *mut c_void,
) -> &'static mut ResetCCtxWorkspaceTestContext {
unsafe { &mut *context.cast::<ResetCCtxWorkspaceTestContext>() }
}
unsafe extern "C" fn reset_cctx_workspace_test_bump(context: *mut c_void) {
let context = unsafe { reset_cctx_workspace_test_context(context) };
context
.events
.push(ResetCCtxWorkspaceTestEvent::BumpOversizedDuration);
}
unsafe extern "C" fn reset_cctx_workspace_test_free(context: *mut c_void) {
let context = unsafe { reset_cctx_workspace_test_context(context) };
context
.events
.push(ResetCCtxWorkspaceTestEvent::FreeWorkspace);
}
unsafe extern "C" fn reset_cctx_workspace_test_create(
context: *mut c_void,
needed_space: usize,
) -> usize {
let context = unsafe { reset_cctx_workspace_test_context(context) };
context
.events
.push(ResetCCtxWorkspaceTestEvent::CreateWorkspace(needed_space));
context.create_result
}
unsafe extern "C" fn reset_cctx_workspace_test_reserve_object(
context: *mut c_void,
size: usize,
) -> *mut c_void {
let context = unsafe { reset_cctx_workspace_test_context(context) };
context
.events
.push(ResetCCtxWorkspaceTestEvent::ReserveObject(size));
let pointer = (0x1000usize + context.reserve_count * 0x1000) as *mut c_void;
context.reserve_count += 1;
pointer
}
unsafe extern "C" fn reset_cctx_workspace_test_set_pointer(
context: *mut c_void,
kind: c_int,
_pointer: *mut c_void,
) {
let context = unsafe { reset_cctx_workspace_test_context(context) };
context
.events
.push(ResetCCtxWorkspaceTestEvent::Pointer(kind));
}
unsafe extern "C" fn reset_cctx_workspace_test_set_size(
context: *mut c_void,
kind: c_int,
value: usize,
) {
let context = unsafe { reset_cctx_workspace_test_context(context) };
context
.events
.push(ResetCCtxWorkspaceTestEvent::Size(kind, value));
}
unsafe extern "C" fn reset_cctx_workspace_test_clear(context: *mut c_void) {
let context = unsafe { reset_cctx_workspace_test_context(context) };
context
.events
.push(ResetCCtxWorkspaceTestEvent::ClearWorkspace);
}
fn reset_cctx_workspace_test_state(
context: &mut ResetCCtxWorkspaceTestContext,
is_static: c_int,
workspace_too_small: c_int,
workspace_wasteful: c_int,
needed_space: usize,
compressed_block_state_size: usize,
tmp_workspace_size: usize,
needs_index_reset: &mut c_int,
) -> ZSTD_rust_resetCCtxWorkspaceState {
ZSTD_rust_resetCCtxWorkspaceState {
callbackContext: (context as *mut ResetCCtxWorkspaceTestContext).cast(),
isStatic: is_static,
workspaceTooSmall: workspace_too_small,
workspaceWasteful: workspace_wasteful,
neededSpace: needed_space,
compressedBlockStateSize: compressed_block_state_size,
tmpWorkspaceSize: tmp_workspace_size,
needsIndexReset: needs_index_reset,
bumpOversizedDuration: Some(reset_cctx_workspace_test_bump),
freeWorkspace: Some(reset_cctx_workspace_test_free),
createWorkspace: Some(reset_cctx_workspace_test_create),
reserveObject: Some(reset_cctx_workspace_test_reserve_object),
setPointer: Some(reset_cctx_workspace_test_set_pointer),
setSize: Some(reset_cctx_workspace_test_set_size),
clearWorkspace: Some(reset_cctx_workspace_test_clear),
}
}
#[test]
fn cctx_workspace_preserves_existing_workspace_and_clear_order() {
let mut context = ResetCCtxWorkspaceTestContext::default();
let mut needs_index_reset = 0;
let state = reset_cctx_workspace_test_state(
&mut context,
0,
0,
0,
4096,
128,
64,
&mut needs_index_reset,
);
assert_eq!(unsafe { ZSTD_rust_resetCCtxWorkspace(&state) }, 0);
assert_eq!(needs_index_reset, 0);
assert_eq!(
context.events,
[
ResetCCtxWorkspaceTestEvent::BumpOversizedDuration,
ResetCCtxWorkspaceTestEvent::ClearWorkspace,
]
);
}
#[test]
fn cctx_workspace_preserves_resize_reservation_and_publication_order() {
let mut context = ResetCCtxWorkspaceTestContext::default();
let mut needs_index_reset = 0;
let state = reset_cctx_workspace_test_state(
&mut context,
0,
1,
0,
4096,
128,
64,
&mut needs_index_reset,
);
assert_eq!(unsafe { ZSTD_rust_resetCCtxWorkspace(&state) }, 0);
assert_eq!(needs_index_reset, RESET_CCTX_INDEX_RESET);
assert_eq!(
context.events,
[
ResetCCtxWorkspaceTestEvent::BumpOversizedDuration,
ResetCCtxWorkspaceTestEvent::FreeWorkspace,
ResetCCtxWorkspaceTestEvent::CreateWorkspace(4096),
ResetCCtxWorkspaceTestEvent::ReserveObject(128),
ResetCCtxWorkspaceTestEvent::Pointer(RESET_CCTX_POINTER_PREV_CBLOCK),
ResetCCtxWorkspaceTestEvent::ReserveObject(128),
ResetCCtxWorkspaceTestEvent::Pointer(RESET_CCTX_POINTER_NEXT_CBLOCK),
ResetCCtxWorkspaceTestEvent::ReserveObject(64),
ResetCCtxWorkspaceTestEvent::Pointer(RESET_CCTX_POINTER_TMP_WORKSPACE),
ResetCCtxWorkspaceTestEvent::Size(RESET_CCTX_SIZE_TMP_WORKSPACE, 64),
ResetCCtxWorkspaceTestEvent::ClearWorkspace,
]
);
}
#[test]
fn cctx_workspace_rejects_static_resize_before_mutating_workspace() {
let mut context = ResetCCtxWorkspaceTestContext::default();
let mut needs_index_reset = 0;
let state = reset_cctx_workspace_test_state(
&mut context,
1,
1,
0,
4096,
128,
64,
&mut needs_index_reset,
);
assert_eq!(
unsafe { ZSTD_rust_resetCCtxWorkspace(&state) },
ERROR(ZstdErrorCode::MemoryAllocation)
);
assert_eq!(needs_index_reset, 0);
assert!(context.events.is_empty());
}
#[derive(Default)]
struct ResetCCtxTailTestContext {
events: Vec<&'static str>,
match_result: usize,
storage_result: usize,
}
unsafe fn reset_cctx_tail_test_context(
context: *mut c_void,
) -> &'static mut ResetCCtxTailTestContext {
unsafe { &mut *context.cast::<ResetCCtxTailTestContext>() }
}
unsafe extern "C" fn reset_cctx_tail_test_initialize(context: *mut c_void) {
let context = unsafe { reset_cctx_tail_test_context(context) };
context.events.push("initialize");
}
unsafe extern "C" fn reset_cctx_tail_test_reset_match_state(context: *mut c_void) -> usize {
let context = unsafe { reset_cctx_tail_test_context(context) };
context.events.push("reset-match-state");
context.match_result
}
unsafe extern "C" fn reset_cctx_tail_test_reset_storage(context: *mut c_void) -> usize {
let context = unsafe { reset_cctx_tail_test_context(context) };
context.events.push("reset-storage");
context.storage_result
}
fn reset_cctx_tail_test_state(
context: &mut ResetCCtxTailTestContext,
compressed_block_state: *mut ZSTD_compressedBlockState_t,
) -> ZSTD_rust_resetCCtxTailState {
ZSTD_rust_resetCCtxTailState {
callbackContext: (context as *mut ResetCCtxTailTestContext).cast(),
initialize: Some(reset_cctx_tail_test_initialize),
compressedBlockState: compressed_block_state,
resetMatchState: Some(reset_cctx_tail_test_reset_match_state),
resetStorage: Some(reset_cctx_tail_test_reset_storage),
}
}
#[test]
fn cctx_reset_tail_preserves_post_workspace_callback_order() {
let mut context = ResetCCtxTailTestContext::default();
let mut block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let state = reset_cctx_tail_test_state(&mut context, &mut block_state);
assert_eq!(unsafe { ZSTD_rust_resetCCtxTail(&state) }, 0);
assert_eq!(
context.events,
["initialize", "reset-match-state", "reset-storage"]
);
assert_eq!(block_state.rep, [1, 4, 8]);
assert_eq!(block_state.entropy.huf.repeatMode, 0);
}
#[test]
fn cctx_reset_tail_stops_before_storage_after_match_state_error() {
let mut context = ResetCCtxTailTestContext {
match_result: ERROR(ZstdErrorCode::StageWrong),
..ResetCCtxTailTestContext::default()
};
let mut block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let state = reset_cctx_tail_test_state(&mut context, &mut block_state);
assert_eq!(
unsafe { ZSTD_rust_resetCCtxTail(&state) },
ERROR(ZstdErrorCode::StageWrong)
);
assert_eq!(context.events, ["initialize", "reset-match-state"]);
}
#[test]
fn cctx_reset_tail_propagates_storage_error_after_match_state() {
let mut context = ResetCCtxTailTestContext {
storage_result: ERROR(ZstdErrorCode::MemoryAllocation),
..ResetCCtxTailTestContext::default()
};
let mut block_state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let state = reset_cctx_tail_test_state(&mut context, &mut block_state);
assert_eq!(
unsafe { ZSTD_rust_resetCCtxTail(&state) },
ERROR(ZstdErrorCode::MemoryAllocation)
);
assert_eq!(
context.events,
["initialize", "reset-match-state", "reset-storage"]
);
}
#[test]
fn estimate_cctx_workspace_size_keeps_static_and_buffer_components_separate() {
let sizing = cctx_workspace_test_sizing();
let ordinary = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 0, 0, 0, 0);
let static_context = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 1, 0, 0, 0);
let buffered = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 0, 17, 23, 0);
let external = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 0, 0, 0, 1);
assert_eq!(static_context - ordinary, sizing.cctxSize);
assert_eq!(buffered - ordinary, 17 + 23);
assert_eq!(external - ordinary, 6265);
}
#[test]
fn estimate_cctx_workspace_size_adds_ldm_table_and_sequence_space_only_when_enabled() {
let sizing = cctx_workspace_test_sizing();
let ordinary = cctx_workspace_test_estimate(&sizing, 0, 4, 2, 64, 0, 0, 0, 0);
let ldm = cctx_workspace_test_estimate(&sizing, ZSTD_RUST_PS_ENABLE, 4, 2, 64, 0, 0, 0, 0);
assert_eq!(ldm - ordinary, 324);
}
#[test]
fn max_estimate_cctx_size_handles_zero_values() {
assert_eq!(max_estimate_cctx_size(0, 0, 0, 0), 0);
assert_eq!(ZSTD_rust_maxEstimateCCtxSize(0, 0, 0, 0), 0);
}
#[test]
fn max_estimate_cctx_size_selects_the_largest_ordinary_value() {
assert_eq!(max_estimate_cctx_size(17, 42, 9, 31), 42);
assert_eq!(ZSTD_rust_maxEstimateCCtxSize(17, 42, 9, 31), 42);
}
#[test]
fn max_estimate_cctx_size_preserves_equal_values() {
assert_eq!(max_estimate_cctx_size(42, 42, 42, 42), 42);
assert_eq!(ZSTD_rust_maxEstimateCCtxSize(42, 42, 42, 42), 42);
}
#[test]
fn max_estimate_cctx_size_accepts_size_max() {
assert_eq!(max_estimate_cctx_size(1, usize::MAX, 3, 2), usize::MAX);
assert_eq!(
ZSTD_rust_maxEstimateCCtxSize(1, usize::MAX, 3, 2),
usize::MAX
);
}
#[test]
fn max_estimate_cctx_size_compares_error_like_raw_values() {
let smaller_error = ERROR(ZstdErrorCode::DstSizeTooSmall);
let larger_error = ERROR(ZstdErrorCode::ParameterUnsupported);
assert_eq!(
max_estimate_cctx_size(128, smaller_error, 256, larger_error),
larger_error
);
assert_eq!(
ZSTD_rust_maxEstimateCCtxSize(128, smaller_error, 256, larger_error),
larger_error
);
}
#[test]
fn estimate_cctx_size_internal_preserves_tier_order_and_max_policy() {
let mut context = EstimateCCtxSizeTestContext {
compression_levels: Vec::new(),
source_size_hints: Vec::new(),
parameter_tiers: Vec::new(),
};
let state = ZSTD_rust_estimateCCtxSizeState {
callback_context: (&mut context as *mut EstimateCCtxSizeTestContext).cast(),
compression_level: 7,
get_c_params: estimate_cctx_size_test_get_c_params,
estimate: estimate_cctx_size_test_estimate,
};
assert_eq!(unsafe { ZSTD_rust_estimateCCtxSizeInternal(&state) }, 91);
assert_eq!(context.compression_levels, vec![7; 4]);
assert_eq!(
context.source_size_hints,
vec![16 * 1024, 128 * 1024, 256 * 1024, ZSTD_CONTENTSIZE_UNKNOWN]
);
assert_eq!(context.parameter_tiers, vec![1, 2, 3, 4]);
}
#[test]
fn estimate_cctx_size_internal_rejects_a_null_state() {
assert_eq!(
unsafe { ZSTD_rust_estimateCCtxSizeInternal(ptr::null()) },
ERROR(ZstdErrorCode::Generic)
);
}
#[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 =
ZSTD_compress;
let context_entry: unsafe extern "C" fn(
*mut c_void,
*mut c_void,
usize,
*const c_void,
usize,
c_int,
) -> usize = ZSTD_compressCCtx;
assert_eq!(size_of::<usize>(), size_of::<*const c_void>());
let _ = entry;
let _ = context_entry;
}
#[test]
fn explicit_context_simple_api_matches_one_shot_path() {
let input = b"explicit context compression remains a simple API";
let capacity = crate::zstd_compress_api::ZSTD_compressBound(input.len());
let mut output = vec![0u8; capacity];
let written = unsafe {
ZSTD_compressCCtx(
std::ptr::dangling_mut::<c_void>(),
output.as_mut_ptr().cast(),
output.len(),
input.as_ptr().cast(),
input.len(),
3,
)
};
assert!(!ERR_isError(written));
output.truncate(written);
let one_shot = compress_input(input, 3);
assert_eq!(output, one_shot);
if let Some(restored) = system_round_trip(&output) {
assert_eq!(restored, input);
}
}
#[test]
fn one_shot_round_trip_across_block_boundaries() {
let mut input = Vec::with_capacity(128 * 1024 + 37);
for index in 0..(128 * 1024 + 37) {
input.push(((index * 17) ^ (index / 31)) as u8);
}
let compressed = compress_input(&input, 3);
if let Some(restored) = system_round_trip(&compressed) {
assert_eq!(restored, input);
}
}
#[test]
fn empty_and_short_inputs_have_valid_frames() {
for input in [b"".as_slice(), b"a", b"abcdefg", b"abcdefgh"] {
let compressed = compress_input(input, 1);
if let Some(restored) = system_round_trip(&compressed) {
assert_eq!(restored, input);
}
}
}
#[test]
fn one_shot_promotes_nonfirst_rle_blocks() {
let mut input = vec![b'B'; 256 * 1024 - 2];
input.extend(std::iter::repeat_n(b'A', 100 * 1024));
let compressed = compress_input(&input, 1);
assert!(compressed.len() <= 46);
if let Some(restored) = system_round_trip(&compressed) {
assert_eq!(restored, input);
}
}
#[test]
fn public_error_paths_match_size_t_error_contract() {
let mut output = [0u8; 64];
let source = [1u8; 8];
assert_eq!(
unsafe {
ZSTD_compress(
output.as_mut_ptr().cast(),
0,
source.as_ptr().cast(),
source.len(),
3,
)
},
ERROR(ZstdErrorCode::DstSizeTooSmall)
);
assert_eq!(
unsafe { ZSTD_compress(ptr::null_mut(), 1, source.as_ptr().cast(), source.len(), 3) },
ERROR(ZstdErrorCode::DstBufferNull)
);
assert_eq!(
unsafe { ZSTD_compress(output.as_mut_ptr().cast(), output.len(), ptr::null(), 1, 3) },
ERROR(ZstdErrorCode::SrcSizeWrong)
);
}
}