Files
zstd-rs/rust/src/zstd_compress.rs
T
ddidderr 41255c8dcc feat(compress): move CCtx copy policy into Rust
Move public ZSTD_copyCCtx pledge normalization and frame-parameter policy into
Rust while retaining the private workspace and match-table copy implementation
behind a C callback.

Test Plan:
- cargo test --manifest-path rust/Cargo.toml --lib
- cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- make -B -C programs -j1 zstd
- make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s
- focused copy_cctx unit tests
2026-07-19 15:30:07 +02:00

12036 lines
404 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; the public context lifecycle
//! remains C-owned because it shares the configuration-dependent private
//! `ZSTD_CCtx_s` layout. `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};
#[cfg(not(test))]
use crate::zstd_compress_api::ZSTD_compressBound;
use crate::zstd_compress_frame::{
write_raw_block, ZSTD_rust_noCompressBlock, ZSTD_rust_rleCompressBlock,
ZSTD_rust_writeBlockHeader, ZSTD_rust_writeFrameHeader, ZSTD_writeLastEmptyBlock,
};
use crate::zstd_compress_literals::min_gain;
use crate::zstd_compress_params::{
ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters,
ZSTD_rust_params_adjustCParams, ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_selectCParams,
ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE,
};
use crate::zstd_compress_params_api::{
ZSTD_CCtxParams_setParameter, ZSTD_CCtx_params, 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_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_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_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;
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 FrameChunkPrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
type FrameChunkCompressFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize;
type FrameChunkChecksumFn = unsafe extern "C" fn(*mut c_void, *const c_void, 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 BuildSeqStoreSelectFn = unsafe extern "C" fn(
*mut c_void,
*mut SeqStore_t,
*mut u32,
*const c_void,
usize,
*mut c_int,
) -> usize;
/// 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,
select_sequences: BuildSeqStoreSelectFn,
}
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, select_sequences)
== 6 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_buildSeqStoreState>()
== 7 * size_of::<usize>() + size_of::<c_uint>() + 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;
}
}
/// 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 c_void,
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_block: FrameChunkPrepareFn,
compress_target: FrameChunkCompressFn,
compress_split: FrameChunkCompressFn,
compress_internal: FrameChunkCompressFn,
update_checksum: FrameChunkChecksumFn,
}
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_block)
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_frameChunkState>()
== 12 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
};
/// Rust implementation of `ZSTD_compress_frameChunk`.
///
/// C still prepares match-state windows and invokes the selected block body
/// through callbacks. Rust owns the block-size heuristic call, output
/// framing, savings accounting, checksum sequencing, and frame-state update.
#[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() {
return ERROR(ZstdErrorCode::Generic);
}
if state.checksum_flag != 0 && src_size != 0 {
if state.checksum_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { (state.update_checksum)(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 { (state.prepare_block)(state.callback_context, ip.cast(), block_size) };
let c_size = if state.use_target_c_block_size != 0 {
unsafe {
(state.compress_target)(
state.callback_context,
op.cast(),
remaining_capacity,
ip.cast(),
block_size,
last_block,
)
}
} else if state.block_splitter_enabled != 0 {
unsafe {
(state.compress_split)(
state.callback_context,
op.cast(),
remaining_capacity,
ip.cast(),
block_size,
last_block,
)
}
} else {
let compressed_size = unsafe {
(state.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 CompressContinueHeaderFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize) -> usize;
type CompressContinueWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
type CompressContinueBlockFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> 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
/// opaque callback context remains in C, where callbacks update the private
/// match-state windows and invoke the C-owned context-sensitive operations.
#[repr(C)]
pub struct ZSTD_rust_compressContinueState {
callback_context: *mut c_void,
write_frame_header: CompressContinueHeaderFn,
update_window: CompressContinueWindowFn,
correct_overflow: CompressContinueWindowFn,
compress_frame_chunk: CompressContinueBlockFn,
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,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressContinueState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressContinueState, write_frame_header) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressContinueState, update_window) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 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)
== 9 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, block_size_max)
== 9 * size_of::<usize>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, check_block_size)
== 9 * size_of::<usize>() + size_of::<u64>() + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_compressContinueState>()
== if size_of::<usize>() == 8 { 96 } else { 52 }
);
};
const ZSTD_COMPRESSION_STAGE_CREATED: c_int = 0;
const ZSTD_COMPRESSION_STAGE_INIT: c_int = 1;
const ZSTD_COMPRESSION_STAGE_ONGOING: c_int = 2;
#[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 { (state.write_frame_header)(state.callback_context, dst, dst_capacity) };
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;
}
unsafe { (state.update_window)(state.callback_context, src, src_size) };
if frame == 0 {
unsafe { (state.correct_overflow)(state.callback_context, src, src_size) };
}
let compressed_size = if frame != 0 {
unsafe {
(state.compress_frame_chunk)(
state.callback_context,
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 CompressEndContinueFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
usize,
*const c_void,
usize,
c_uint,
c_uint,
) -> usize;
type CompressEndEpilogueFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize) -> usize;
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 opaque callback context retains the private
/// CCtx-dependent continue, epilogue, checksum, and trace operations in C.
#[repr(C)]
pub struct ZSTD_rust_compressEndState {
callback_context: *mut c_void,
compress_continue: CompressEndContinueFn,
write_epilogue: CompressEndEpilogueFn,
trace: CompressEndTraceFn,
consumed_src_size: *const u64,
pledged_src_size_plus_one: u64,
content_size_flag: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressEndState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressEndState, compress_continue) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressEndState, write_epilogue) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressEndState, trace) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressEndState, consumed_src_size) == 4 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressEndState, pledged_src_size_plus_one) == 5 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressEndState, content_size_flag)
== 5 * size_of::<usize>() + size_of::<u64>()
);
assert!(
size_of::<ZSTD_rust_compressEndState>() == if size_of::<usize>() == 8 { 56 } else { 32 }
);
};
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.consumed_src_size.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let c_size = unsafe {
(state.compress_continue)(
state.callback_context,
dst,
dst_capacity,
src,
src_size,
1,
1,
)
};
if ERR_isError(c_size) {
return c_size;
}
let end_result = unsafe {
(state.write_epilogue)(
state.callback_context,
dst.cast::<u8>().add(c_size).cast(),
dst_capacity.wrapping_sub(c_size),
)
};
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 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 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 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 CompressStreamBlockFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize) -> usize;
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. The opaque callback context stays in C; callbacks
/// retain access to `ZSTD_CCtx`, frame construction, and 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: CompressStreamBlockFn,
compress_end: CompressStreamBlockFn,
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)
== 12 * size_of::<usize>() + 2 * size_of::<c_int>() + 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamState, compress_end)
== 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 CompressStreamInitLocalDictFn = unsafe extern "C" fn(*mut c_void) -> usize;
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);
/// 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,
init_local_dict: CompressStreamInitLocalDictFn,
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 { 256 } else { 128 }
);
};
#[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 { (state.init_local_dict)(state.callback_context) };
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()
{
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 {
(state.compress_end)(
state.callback_context,
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 {
(state.compress_end)(
state.callback_context,
output_dst,
output_size,
source,
input_size,
)
} else {
(state.compress_continue)(
state.callback_context,
output_dst,
output_size,
source,
input_size,
)
}
};
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;
type SequenceApiWriteFrameHeaderFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, usize) -> usize;
type SequenceApiUpdateChecksumFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
type SequenceApiDigestChecksumFn = unsafe extern "C" fn(*mut c_void) -> c_uint;
type SequenceApiWriteChecksumFn = unsafe extern "C" fn(*mut c_void, *mut c_void, c_uint);
/// Explicit projection for the public sequence-compression API orchestration.
///
/// Rust owns validation ordering, frame-header/checksum sequencing, and
/// output accounting. C retains the private CCtx, sequence-store, block,
/// and checksum layouts through the two block-state projections and callbacks.
#[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,
write_frame_header: SequenceApiWriteFrameHeaderFn,
update_checksum: SequenceApiUpdateChecksumFn,
digest_checksum: SequenceApiDigestChecksumFn,
write_checksum: SequenceApiWriteChecksumFn,
checksum_flag: c_int,
block_delimiters: c_int,
validate_sequences: c_int,
}
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, write_frame_header) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, update_checksum) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, digest_checksum) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, write_checksum) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, checksum_flag) == size_of::<[usize; 8]>());
assert!(
offset_of!(ZSTD_rust_sequenceApiState, block_delimiters)
== size_of::<[usize; 8]>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_sequenceApiState, validate_sequences)
== size_of::<[usize; 8]>() + 2 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_sequenceApiState>() == if size_of::<usize>() == 8 { 80 } else { 44 }
);
};
#[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 and the no-compress fallback remain in C.
/// 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 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
|| state.select_sequences 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 {
(state.select_sequences)(
state.callback_context,
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 `ZSTD_deriveBlockSplits()` and the private `ZSTD_CCtx`; Rust
/// receives only the sequence-store views and block-emission state needed to
/// reproduce the original loop.
#[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 {
src: *const c_void,
size: usize,
pos: usize,
}
#[repr(C)]
pub struct ZSTD_outBuffer {
dst: *mut c_void,
size: usize,
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]
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)
}
#[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
}
#[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,
)
}
#[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)
}
#[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_rust_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_rust_dictTooBig(loaded_dict_size: usize) -> c_int {
dict_too_big(loaded_dict_size) as c_int
}
#[inline]
fn sizeof_local_dict(dict_buffer_present: c_int, dict_size: usize, cdict_size: usize) -> usize {
let buffer_size = if dict_buffer_present != 0 {
dict_size
} else {
0
};
buffer_size.wrapping_add(cdict_size)
}
/// Add the C-owned local-dictionary sizes with C `size_t` wrapping semantics.
#[no_mangle]
pub extern "C" fn ZSTD_rust_sizeofLocalDict(
dict_buffer_present: c_int,
dict_size: usize,
cdict_size: usize,
) -> usize {
sizeof_local_dict(dict_buffer_present, dict_size, cdict_size)
}
#[inline]
fn 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,
)
}
#[inline]
fn max_estimate_cctx_size(
estimate0: usize,
estimate1: usize,
estimate2: usize,
estimate3: usize,
) -> usize {
estimate0.max(estimate1).max(estimate2).max(estimate3)
}
/// 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);
}
}
/// 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;
}
}
#[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.write_frame_header as usize == 0
|| state.update_checksum as usize == 0
|| state.digest_checksum as usize == 0
|| state.write_checksum as usize == 0
{
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 {
(state.write_frame_header)(
state.callback_context,
(*op).cast(),
*dst_capacity,
pledged_src_size,
)
};
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 { (state.digest_checksum)(state.callback_context) };
if dst_capacity < 4 {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
unsafe {
(state.write_checksum)(state.callback_context, 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
/// callbacks for the private CCtx/header/checksum operations. 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 { (state.update_checksum)(state.callback_context, 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
}
/// 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;
#[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,
epilogue_result: usize,
epilogue_offset: usize,
epilogue_capacity: usize,
trace_extra: usize,
continue_frame: c_uint,
continue_last_frame_chunk: c_uint,
dst_base: usize,
}
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_continue(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
_src: *const c_void,
_src_size: usize,
frame: c_uint,
last_frame_chunk: c_uint,
) -> usize {
let context = unsafe { compress_end_test_context(context) };
context.events.push("continue");
context.continue_frame = frame;
context.continue_last_frame_chunk = last_frame_chunk;
context.continue_result
}
unsafe extern "C" fn compress_end_test_epilogue(
context: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
) -> usize {
let context = unsafe { compress_end_test_context(context) };
context.events.push("epilogue");
context.epilogue_offset = (dst as usize).wrapping_sub(context.dst_base);
context.epilogue_capacity = dst_capacity;
context.epilogue_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: &u64,
pledged_src_size_plus_one: u64,
content_size_flag: c_int,
) -> ZSTD_rust_compressEndState {
ZSTD_rust_compressEndState {
callback_context: (context as *mut CompressEndTestContext).cast(),
compress_continue: compress_end_test_continue,
write_epilogue: compress_end_test_epilogue,
trace: compress_end_test_trace,
consumed_src_size: consumed_src_size as *const u64,
pledged_src_size_plus_one,
content_size_flag,
}
}
#[test]
fn compress_end_preserves_callback_order_and_output_accounting() {
let mut dst = [0u8; 16];
let mut context = CompressEndTestContext {
continue_result: 3,
epilogue_result: 5,
dst_base: dst.as_mut_ptr() as usize,
..CompressEndTestContext::default()
};
let consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &consumed_src_size, 8, 1);
let result = unsafe {
ZSTD_rust_compressEnd(&state, dst.as_mut_ptr().cast(), dst.len(), ptr::null(), 0)
};
assert_eq!(result, 8);
assert_eq!(context.events, ["continue", "epilogue", "trace"]);
assert_eq!(
(context.continue_frame, context.continue_last_frame_chunk),
(1, 1)
);
assert_eq!(
(context.epilogue_offset, context.epilogue_capacity),
(3, 13)
);
assert_eq!(context.trace_extra, 5);
}
#[test]
fn compress_end_stops_before_epilogue_when_continue_fails() {
let mut dst = [0u8; 16];
let mut context = CompressEndTestContext {
continue_result: ERROR(ZstdErrorCode::MemoryAllocation),
dst_base: dst.as_mut_ptr() as usize,
..CompressEndTestContext::default()
};
let consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &consumed_src_size, 8, 0);
let result = unsafe {
ZSTD_rust_compressEnd(&state, dst.as_mut_ptr().cast(), dst.len(), ptr::null(), 0)
};
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; 16];
let mut context = CompressEndTestContext {
continue_result: 3,
epilogue_result: ERROR(ZstdErrorCode::MemoryAllocation),
dst_base: dst.as_mut_ptr() as usize,
..CompressEndTestContext::default()
};
let consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &consumed_src_size, 99, 1);
let result = unsafe {
ZSTD_rust_compressEnd(&state, dst.as_mut_ptr().cast(), dst.len(), ptr::null(), 0)
};
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["continue", "epilogue"]);
}
#[test]
fn compress_end_rejects_pledged_size_before_trace() {
let mut dst = [0u8; 16];
let mut context = CompressEndTestContext {
continue_result: 3,
epilogue_result: 5,
dst_base: dst.as_mut_ptr() as usize,
..CompressEndTestContext::default()
};
let consumed_src_size = 7;
let state = compress_end_test_state(&mut context, &consumed_src_size, 99, 1);
let result = unsafe {
ZSTD_rust_compressEnd(&state, dst.as_mut_ptr().cast(), dst.len(), ptr::null(), 0)
};
assert_eq!(result, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!(context.events, ["continue", "epilogue"]);
}
#[derive(Default)]
struct CompressStreamInitTestContext {
events: Vec<&'static str>,
local_result: usize,
create_result: usize,
mt_result: usize,
begin_result: usize,
pledged: u64,
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_local_dict(context: *mut c_void) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("local-dict");
context.local_result
}
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,
init_local_dict: compress_stream_init_test_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_stops_before_later_callbacks_on_error() {
let mut context = CompressStreamInitTestContext {
local_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"]);
}
#[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],
target_calls: usize,
split_calls: usize,
internal_calls: usize,
last_blocks: [c_uint; 4],
checksum_calls: usize,
checksum_size: usize,
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 extern "C" fn frame_chunk_test_prepare(
context: *mut c_void,
_src: *const c_void,
block_size: usize,
) {
let context = unsafe { frame_chunk_test_context(context) };
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_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
}
unsafe extern "C" fn frame_chunk_test_checksum(
context: *mut c_void,
_src: *const c_void,
src_size: usize,
) {
let context = unsafe { frame_chunk_test_context(context) };
context.checksum_calls += 1;
context.checksum_size = context.checksum_size.wrapping_add(src_size);
}
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 = context as *mut FrameChunkTestContext as *mut c_void;
ZSTD_rust_frameChunkState {
callback_context: context,
tmp_workspace: ptr::null_mut(),
checksum_state: context,
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_block: frame_chunk_test_prepare,
compress_target: frame_chunk_test_target,
compress_split: frame_chunk_test_split,
compress_internal: frame_chunk_test_internal,
update_checksum: frame_chunk_test_checksum,
}
}
#[test]
fn frame_chunk_internal_path_emits_blocks_and_updates_state_once() {
let mut context = FrameChunkTestContext {
internal_result: 2,
..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.internal_calls, 2);
assert_eq!(context.last_blocks[..2], [0, 1]);
assert_eq!(context.checksum_calls, 1);
assert_eq!(context.checksum_size, source.len());
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_calls, 1);
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 {
header_calls: usize,
update_window_calls: usize,
overflow_calls: usize,
frame_calls: usize,
block_calls: usize,
last_frame_chunk: c_uint,
header_result: usize,
frame_result: usize,
block_result: usize,
}
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_header(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
) -> usize {
let context = unsafe { compress_continue_test_context(context) };
context.header_calls += 1;
context.header_result
}
unsafe extern "C" fn compress_continue_test_window(
context: *mut c_void,
_src: *const c_void,
_src_size: usize,
) {
let context = unsafe { compress_continue_test_context(context) };
context.update_window_calls += 1;
}
unsafe extern "C" fn compress_continue_test_overflow(
context: *mut c_void,
_src: *const c_void,
_src_size: usize,
) {
let context = unsafe { compress_continue_test_context(context) };
context.overflow_calls += 1;
}
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 {
ZSTD_rust_compressContinueState {
callback_context: (context as *mut CompressContinueTestContext).cast(),
write_frame_header: compress_continue_test_header,
update_window: compress_continue_test_window,
correct_overflow: compress_continue_test_overflow,
compress_frame_chunk: compress_continue_test_frame,
compress_block: compress_continue_test_block,
stage,
consumed_src_size,
produced_c_size,
pledged_src_size_plus_one,
block_size_max,
check_block_size,
}
}
#[test]
fn compress_continue_starts_frame_and_updates_progression() {
let mut context = CompressContinueTestContext {
header_result: 4,
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, 10);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!(consumed, 12);
assert_eq!(produced, 21);
assert_eq!(context.header_calls, 1);
assert_eq!(context.update_window_calls, 1);
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);
}
#[test]
fn compress_continue_empty_frame_only_writes_header() {
let mut context = CompressContinueTestContext {
header_result: 3,
..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; 8];
let result = unsafe {
ZSTD_rust_compressContinue(
&state,
output.as_mut_ptr().cast(),
output.len(),
ptr::null(),
0,
1,
0,
)
};
assert_eq!(result, 3);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!((consumed, produced), (7, 11));
assert_eq!(context.header_calls, 1);
assert_eq!(context.update_window_calls, 0);
assert_eq!(context.frame_calls, 0);
assert_eq!(output, [0xa5; 8]);
}
#[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 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.update_window_calls, 0);
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.update_window_calls, 0);
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.update_window_calls, 1);
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.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,
}
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,
) -> 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,
) -> 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
}
#[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 {
ZSTD_rust_compressStreamState {
callback_context: (context as *mut CompressStreamTestContext).cast(),
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: compress_stream_test_block,
compress_end: compress_stream_test_end,
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
);
}
#[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 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
);
}
#[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_rust_indexTooCloseToMax(threshold), 0);
assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold + 1), 1);
}
#[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 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_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX), 0);
assert_eq!(ZSTD_rust_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);
}
#[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
);
}
#[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 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);
}
#[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 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 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
);
}
#[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 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)
);
}
}