#![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(clippy::missing_safety_doc)] #![allow(clippy::too_many_arguments)] //! First high-level compression slice. //! //! The layout-independent one-shot entry point in this module drives the //! already migrated compression leaves. `ZSTD_compressCCtx` uses the same //! path after a narrow C-owned context reset; public context allocation now //! runs through a Rust-owned boundary while C retains configuration-dependent //! private `ZSTD_CCtx_s` initialization. `ZSTD_compress2` and the complete-input simple //! `ZSTD_compressStream2(..., ZSTD_e_end)` path dispatch through Rust while //! retaining the C implementation for advanced and partial-stream cases. use crate::common::MINMATCH; use crate::errors::{ERR_isError, ZstdErrorCode, ERROR}; #[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_rustMatchStateSizing, ZSTD_rust_params_adjustCParams, ZSTD_rust_params_allocateChainTable, ZSTD_rust_params_checkCParams, ZSTD_rust_params_defaultCLevel, ZSTD_rust_params_estimateMatchStateSize, ZSTD_rust_params_getParamsInternal, ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_resolveMaxBlockSize, ZSTD_rust_params_rowMatchFinderUsed, ZSTD_rust_params_selectCParams, ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE, ZSTD_RUST_PS_ENABLE, }; use crate::zstd_compress_params_api::{ ZSTD_CCtxParams_setParameter, ZSTD_CCtx_params, ZSTD_customMem, ZSTD_rust_isUpdateAuthorized, }; use crate::zstd_compress_sequences::SeqDef; use crate::zstd_compress_stats::{ update_rep, SeqCollector, SeqStore_t, ZSTD_Sequence, ZSTD_SequencePosition, ZSTD_compressedBlockState_t, ZSTD_entropyCTables_t, ZSTD_rust_confirmRepcodesAndEntropyTables, ZSTD_rust_convertSequencesNoRepcodes, ZSTD_rust_copyBlockSequences, ZSTD_rust_countSeqStoreLiteralsBytes, ZSTD_rust_countSeqStoreMatchBytes, ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize, ZSTD_rust_entropyCompressSeqStore, ZSTD_rust_entropyCompressSeqStore_internal, ZSTD_rust_fastSequenceLengthSum, ZSTD_rust_finalizeOffBase, ZSTD_rust_get1BlockSummary, ZSTD_rust_isRLE, ZSTD_rust_maybeRLE, ZSTD_rust_postProcessSequenceProducerResult, ZSTD_rust_resetCompressedBlockState, ZSTD_rust_resetSeqStore, ZSTD_rust_seqStore_resolveOffCodes, ZSTD_rust_storeLastLiterals, ZSTD_rust_transferSequencesNoDelim, ZSTD_rust_transferSequencesWBlockDelim, ZSTD_rust_validateSeqStore, ZSTD_LLT_LITERAL_LENGTH, ZSTD_LLT_MATCH_LENGTH, }; use crate::zstd_compress_superblock::ZSTD_rust_compressSuperBlock; use std::ffi::c_void; use std::mem::{offset_of, size_of, MaybeUninit}; use std::os::raw::{c_int, c_longlong, c_uint}; use std::ptr; #[cfg(not(test))] unsafe extern "C" { fn ZSTD_createCCtx() -> *mut c_void; fn ZSTD_freeCCtx(cctx: *mut c_void) -> usize; fn ZSTD_rust_resetCCtxForSimpleCompression(cctx: *mut c_void) -> usize; fn ZSTD_rust_prepareCCtxForSimpleCompression( cctx: *mut c_void, src_size: usize, compression_level: c_int, ) -> usize; fn ZSTD_rust_resetCCtxForSimpleCompressionSession(cctx: *mut c_void) -> usize; fn ZSTD_rust_markSimpleCompression2Complete(cctx: *mut c_void); fn ZSTD_rust_simpleCompress2Level(cctx: *const c_void, src_size: usize) -> c_int; fn ZSTD_compress_usingDict( cctx: *mut c_void, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, compression_level: c_int, ) -> usize; fn ZSTD_rust_simpleCompressStream2Level(cctx: *const c_void, src_size: usize) -> c_int; fn ZSTD_compress2_c( cctx: *mut c_void, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, ) -> usize; fn ZSTD_compressStream2_c( cctx: *mut c_void, output: *mut ZSTD_outBuffer, input: *mut ZSTD_inBuffer, end_op: c_int, ) -> usize; } const ZSTD_FAST: c_int = 1; const ZSTD_DFAST: c_int = 2; const ZSTD_BTLAZY2: c_int = 6; const ZSTD_REP_NUM: usize = 3; const ZSTD_BM_BUFFERED: c_int = 0; const ZSTD_BM_STABLE: c_int = 1; const ZSTD_SF_NO_BLOCK_DELIMITERS: c_int = 0; const ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS: c_int = 1; const ZSTD_BLOCKSIZE_MAX: usize = 1 << 17; const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX; const ZSTD_TARGET_CBLOCK_BSS_COMPRESS: c_int = 0; const ZSTD_BLOCK_HEADER_SIZE: usize = 3; const MIN_CBLOCK_SIZE: usize = 2; const MIN_COMPRESSIBLE_BLOCK_SIZE: usize = MIN_CBLOCK_SIZE + ZSTD_BLOCK_HEADER_SIZE + 1 + 1; const ZSTD_ROWSIZE: usize = 16; const ZSTD_CSTREAM_STAGE_INIT: c_int = 0; 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::() == 8 { 3500usize << 20 } else { 2000usize << 20 }; const ZSTD_CHUNKSIZE_MAX: usize = u32::MAX as usize - ZSTD_CURRENT_MAX; const ZSTD_E_END: c_int = 2; const ZSTD_E_CONTINUE: c_int = 0; const ZSTD_E_FLUSH: c_int = 1; const ZSTD_C_WINDOW_LOG: c_int = 101; const ZSTD_C_HASH_LOG: c_int = 102; const ZSTD_C_CHAIN_LOG: c_int = 103; const ZSTD_C_SEARCH_LOG: c_int = 104; const ZSTD_C_MIN_MATCH: c_int = 105; const ZSTD_C_TARGET_LENGTH: c_int = 106; const ZSTD_C_STRATEGY: c_int = 107; const ZSTD_C_FORMAT: c_int = 10; const ZSTD_C_COMPRESSION_LEVEL: c_int = 100; const ZSTD_C_TARGET_C_BLOCK_SIZE: c_int = 130; const ZSTD_C_ENABLE_LDM: c_int = 160; const ZSTD_C_LDM_HASH_LOG: c_int = 161; const ZSTD_C_LDM_MIN_MATCH: c_int = 162; const ZSTD_C_LDM_BUCKET_SIZE_LOG: c_int = 163; const ZSTD_C_LDM_HASH_RATE_LOG: c_int = 164; const ZSTD_C_CONTENT_SIZE_FLAG: c_int = 200; const ZSTD_C_CHECKSUM_FLAG: c_int = 201; const ZSTD_C_DICT_ID_FLAG: c_int = 202; const ZSTD_C_NB_WORKERS: c_int = 400; const ZSTD_C_JOB_SIZE: c_int = 401; const ZSTD_C_OVERLAP_LOG: c_int = 402; const ZSTD_C_RSYNCABLE: c_int = 500; const ZSTD_C_FORCE_MAX_WINDOW: c_int = 1000; const ZSTD_C_FORCE_ATTACH_DICT: c_int = 1001; const ZSTD_C_LITERAL_COMPRESSION_MODE: c_int = 1002; const ZSTD_C_SRC_SIZE_HINT: c_int = 1004; const ZSTD_C_ENABLE_DEDICATED_DICT_SEARCH: c_int = 1005; const ZSTD_C_STABLE_IN_BUFFER: c_int = 1006; const ZSTD_C_STABLE_OUT_BUFFER: c_int = 1007; const ZSTD_C_BLOCK_DELIMITERS: c_int = 1008; const ZSTD_C_VALIDATE_SEQUENCES: c_int = 1009; const ZSTD_C_SPLIT_AFTER_SEQUENCES: c_int = 1010; const ZSTD_C_USE_ROW_MATCH_FINDER: c_int = 1011; const ZSTD_C_DETERMINISTIC_REF_PREFIX: c_int = 1012; const ZSTD_C_PREFETCH_CDICT_TABLES: c_int = 1013; const ZSTD_C_ENABLE_SEQ_PRODUCER_FALLBACK: c_int = 1014; const ZSTD_C_MAX_BLOCK_SIZE: c_int = 1015; const ZSTD_C_REPCODE_RESOLUTION: c_int = 1016; const ZSTD_C_BLOCK_SPLITTER_LEVEL: c_int = 1017; const ZSTD_RESET_SESSION_ONLY: c_int = 1; const ZSTD_RESET_PARAMETERS: c_int = 2; const ZSTD_RESET_SESSION_AND_PARAMETERS: c_int = 3; const ZSTD_CSTREAM_STAGE_LOAD: c_int = 1; const ZSTD_CSTREAM_STAGE_FLUSH: c_int = 2; const ZSTD_BSS_COMPRESS: c_int = 0; const ZSTD_BSS_NO_COMPRESS: c_int = 1; const FSE_REPEAT_CHECK: c_int = 1; const FSE_REPEAT_VALID: c_int = 2; type FrameChunkPrepareOverflowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize); type FrameChunkPrepareWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, c_uint); type FrameChunkCompressFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize; type 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 BuildSeqStoreCompressFn = unsafe extern "C" fn(*mut c_void, *mut SeqStore_t, *mut u32, *const c_void, usize) -> usize; type BuildSeqStoreTryExternalProducerFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, *mut c_int, *mut c_int) -> usize; type BuildSeqStoreClearLdmFn = unsafe extern "C" fn(*mut c_void); /// Explicit projection for the sequence-store builder. /// /// Rust owns the threshold/reset/repcode/literal-store orchestration. The /// callbacks retain the private matchfinder, LDM, and external-producer /// fallback operations in C without passing `ZSTD_CCtx` across the ABI. #[repr(C)] pub struct ZSTD_rust_buildSeqStoreState { seq_store: *mut SeqStore_t, prev_c_block: *mut *mut ZSTD_compressedBlockState_t, next_c_block: *mut *mut ZSTD_compressedBlockState_t, callback_context: *mut c_void, min_match: c_uint, validate_seq_store: c_int, skip_small_block: BuildSeqStoreSkipFn, prepare_match_state: BuildSeqStorePrepareFn, has_external_sequences: c_int, ldm_enabled: c_int, has_external_sequence_producer: c_int, enable_match_finder_fallback: c_int, compress_external_sequences: BuildSeqStoreCompressFn, compress_ldm: BuildSeqStoreCompressFn, try_external_sequence_producer: BuildSeqStoreTryExternalProducerFn, compress_internal: BuildSeqStoreCompressFn, clear_ldm_seq_store: BuildSeqStoreClearLdmFn, } const _: () = { assert!(offset_of!(ZSTD_rust_buildSeqStoreState, seq_store) == 0); assert!(offset_of!(ZSTD_rust_buildSeqStoreState, prev_c_block) == size_of::()); assert!(offset_of!(ZSTD_rust_buildSeqStoreState, next_c_block) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_buildSeqStoreState, callback_context) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_buildSeqStoreState, min_match) == 4 * size_of::()); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, validate_seq_store) == 4 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, skip_small_block) == 4 * size_of::() + size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, prepare_match_state) == 5 * size_of::() + size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, has_external_sequences) == 6 * size_of::() + size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, ldm_enabled) == 6 * size_of::() + size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, has_external_sequence_producer) == 6 * size_of::() + size_of::() + 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, enable_match_finder_fallback) == 6 * size_of::() + size_of::() + 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, compress_external_sequences) == 6 * size_of::() + size_of::() + 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, compress_ldm) == 7 * size_of::() + size_of::() + 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, try_external_sequence_producer) == size_of::<[usize; 8]>() + size_of::() + 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, compress_internal) == 9 * size_of::() + size_of::() + 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_buildSeqStoreState, clear_ldm_seq_store) == 10 * size_of::() + size_of::() + 5 * size_of::() ); assert!( size_of::() == 11 * size_of::() + size_of::() + 5 * size_of::() ); }; 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, 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, 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::() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, producer) == 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, ext_seq_buf) == 3 * size_of::() ); assert!( offset_of!( ZSTD_rust_externalSequenceProducerState, ext_seq_buf_capacity ) == 4 * size_of::() ); assert!(offset_of!(ZSTD_rust_externalSequenceProducerState, src) == 5 * size_of::()); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, src_size) == 6 * size_of::() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, compression_level) == 7 * size_of::() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, window_size) == size_of::<[usize; 8]>() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, transfer) == 9 * size_of::() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, external_seq_count) == 10 * size_of::() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, seq_store_complete) == 11 * size_of::() ); assert!( offset_of!(ZSTD_rust_externalSequenceProducerState, allow_fallback) == 12 * size_of::() ); assert!(size_of::() == 13 * size_of::()); }; /// 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::()); assert!( offset_of!(ZSTD_rust_externalSequenceStoreState, pos_in_sequence) == 2 * size_of::() ); assert!(offset_of!(ZSTD_rust_externalSequenceStoreState, size) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_externalSequenceStoreState, capacity) == 4 * size_of::()); assert!(size_of::() == 5 * size_of::()); }; #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_referenceExternalSequences( state: *const ZSTD_rust_externalSequenceStoreState, seq: *mut c_void, nb_seq: usize, ) { let Some(state) = state.as_ref() else { return; }; if state.seq.is_null() || state.pos.is_null() || state.pos_in_sequence.is_null() || state.size.is_null() || state.capacity.is_null() { return; } unsafe { *state.seq = seq; *state.size = nb_seq; *state.capacity = nb_seq; *state.pos = 0; *state.pos_in_sequence = 0; } } /// C-owned operations used by Rust's frame-chunk block-preparation policy. /// /// The callback context and private window/workspace layout remain in C; Rust /// controls the order in which these operations run. #[repr(C)] pub struct ZSTD_rust_frameChunkPrepareState { callback_context: *mut c_void, max_dist: c_uint, correct_overflow: FrameChunkPrepareOverflowFn, check_dict_validity: FrameChunkPrepareWindowFn, enforce_max_dist: FrameChunkPrepareWindowFn, clamp_state: *const ZSTD_rust_frameChunkClampState, } #[repr(C)] pub struct ZSTD_rust_frameChunkClampState { next_to_update: *mut c_uint, low_limit: *const c_uint, } const _: () = { assert!(offset_of!(ZSTD_rust_frameChunkClampState, next_to_update) == 0); assert!(offset_of!(ZSTD_rust_frameChunkClampState, low_limit) == size_of::()); assert!(size_of::() == 2 * size_of::()); }; const _: () = { assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, max_dist) == size_of::()); assert!( offset_of!(ZSTD_rust_frameChunkPrepareState, correct_overflow) == 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkPrepareState, check_dict_validity) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkPrepareState, enforce_max_dist) == 4 * size_of::() ); assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, clamp_state) == 5 * size_of::()); assert!(size_of::() == 6 * size_of::()); }; /// 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_state: *const ZSTD_rust_frameChunkPrepareState, 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::()); assert!(offset_of!(ZSTD_rust_frameChunkState, checksum_state) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_frameChunkState, is_first_block) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_frameChunkState, stage) == 4 * size_of::()); assert!(offset_of!(ZSTD_rust_frameChunkState, tmp_wksp_size) == 5 * size_of::()); assert!(offset_of!(ZSTD_rust_frameChunkState, block_size_max) == 6 * size_of::()); assert!(offset_of!(ZSTD_rust_frameChunkState, savings) == 7 * size_of::()); assert!( offset_of!(ZSTD_rust_frameChunkState, pre_block_splitter_level) == 7 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkState, strategy) == 7 * size_of::() + size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkState, use_target_c_block_size) == 7 * size_of::() + size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkState, block_splitter_enabled) == 7 * size_of::() + size_of::() + 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkState, checksum_flag) == 7 * size_of::() + size_of::() + 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkState, ending_stage) == 7 * size_of::() + size_of::() + 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_frameChunkState, prepare_state) == 7 * size_of::() + size_of::() + 6 * size_of::() ); assert!( size_of::() == 12 * size_of::() + size_of::() + 6 * size_of::() ); }; /// Rust implementation of `ZSTD_compress_frameChunk`. /// /// C still owns the private match-state/window operations and invokes the /// selected block body through callbacks. Rust owns preparation ordering, the /// block-size heuristic call, output framing, savings accounting, checksum /// sequencing, and frame-state update. #[inline] unsafe fn prepare_frame_chunk_block( state: &ZSTD_rust_frameChunkPrepareState, src: *const c_void, block_size: usize, ) { unsafe { (state.correct_overflow)(state.callback_context, src, block_size) }; unsafe { (state.check_dict_validity)(state.callback_context, src, block_size, state.max_dist) }; unsafe { (state.enforce_max_dist)(state.callback_context, src, block_size, state.max_dist) }; if state.clamp_state.is_null() { return; } let clamp_state = unsafe { &*state.clamp_state }; if clamp_state.next_to_update.is_null() || clamp_state.low_limit.is_null() { return; } unsafe { /* Ensure hash/chain table insertion resumes no sooner than lowlimit. */ if *clamp_state.next_to_update < *clamp_state.low_limit { *clamp_state.next_to_update = *clamp_state.low_limit; } } } #[allow(clippy::too_many_arguments)] unsafe fn compress_frame_chunk_body_with( state: &ZSTD_rust_frameChunkState, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, last_frame_chunk: c_uint, ) -> usize { if state.is_first_block.is_null() || state.stage.is_null() || state.prepare_state.is_null() { return ERROR(ZstdErrorCode::Generic); } if state.checksum_flag != 0 && src_size != 0 { if state.checksum_state.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { (state.update_checksum)(state.checksum_state, src, src_size) }; } let mut remaining = src_size; let mut ip = src.cast::(); let mut op = dst.cast::(); let mut remaining_capacity = dst_capacity; let mut savings = state.savings; let mut compressed_size_total = 0usize; while remaining != 0 { let block_size = unsafe { crate::zstd_compress_frame::ZSTD_rust_optimalBlockSize( ip.cast(), remaining, state.block_size_max, state.pre_block_splitter_level, state.strategy, savings, state.tmp_workspace, state.tmp_wksp_size, ) }; if block_size == 0 || block_size > remaining { return ERROR(ZstdErrorCode::Generic); } let last_block = last_frame_chunk & u32::from(block_size == remaining); /* Keep the original early capacity guard: even a raw block needs the * minimum block header plus the minimum compressible payload budget. */ if remaining_capacity < ZSTD_BLOCK_HEADER_SIZE + MIN_CBLOCK_SIZE + 1 { return ERROR(ZstdErrorCode::DstSizeTooSmall); } unsafe { prepare_frame_chunk_block(&*state.prepare_state, ip.cast(), block_size) }; let c_size = if state.use_target_c_block_size != 0 { 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::()); assert!(offset_of!(ZSTD_rust_compressContinueState, update_window) == 2 * size_of::()); assert!( offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 4 * size_of::() ); assert!(offset_of!(ZSTD_rust_compressContinueState, compress_block) == 5 * size_of::()); assert!(offset_of!(ZSTD_rust_compressContinueState, stage) == 6 * size_of::()); assert!( offset_of!(ZSTD_rust_compressContinueState, consumed_src_size) == 7 * size_of::() ); 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::() ); assert!( offset_of!(ZSTD_rust_compressContinueState, block_size_max) == 9 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressContinueState, check_block_size) == 9 * size_of::() + size_of::() + size_of::() ); assert!( size_of::() == if size_of::() == 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::().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::()); assert!(offset_of!(ZSTD_rust_compressEndState, write_epilogue) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_compressEndState, trace) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_compressEndState, consumed_src_size) == 4 * size_of::()); assert!( offset_of!(ZSTD_rust_compressEndState, pledged_src_size_plus_one) == 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressEndState, content_size_flag) == 5 * size_of::() + size_of::() ); assert!( size_of::() == if size_of::() == 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::().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::()); assert!(offset_of!(ZSTD_rust_compress2State, set_buffer_modes) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_compress2State, compress_stream_end) == 3 * size_of::()); assert!( offset_of!(ZSTD_rust_compress2State, original_in_buffer_mode) == 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_compress2State, original_out_buffer_mode) == 4 * size_of::() + size_of::() ); assert!( size_of::() == 4 * size_of::() + 2 * size_of::() ); }; 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::() ); assert!( offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, stream_stage) == 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, cdict) == 3 * size_of::() ); assert!(size_of::() == 4 * size_of::()); }; /// 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::()); assert!(offset_of!(ZSTD_rust_setParameterState, c_params_changed) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_setParameterState, static_size) == 3 * size_of::()); assert!( offset_of!( ZSTD_rust_setParameterState, rust_simple_compress2_max_block_size_set ) == 4 * size_of::() ); assert!(size_of::() == 5 * size_of::()); }; #[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::()); assert!(offset_of!(ZSTD_rust_refThreadPoolState, stream_stage) == 2 * size_of::()); assert!(size_of::() == 3 * size_of::()); }; /// 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 CreateCCtxValidateCustomMemFn = unsafe extern "C" fn(*mut c_void) -> c_int; type CreateCCtxAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void; type CreateCCtxInitFn = unsafe extern "C" fn(*mut c_void, *mut c_void); /// Explicit projection for the public `ZSTD_createCCtx_advanced` wrapper. /// /// Rust owns custom-memory validation, allocation failure, and /// allocate-before-init ordering. C retains private `ZSTD_CCtx` initialization /// behind the final callback. #[repr(C)] pub struct ZSTD_rust_createCCtxState { callback_context: *mut c_void, cctx_size: usize, validate_custom_mem: CreateCCtxValidateCustomMemFn, allocate: CreateCCtxAllocateFn, init: CreateCCtxInitFn, } const _: () = { assert!(offset_of!(ZSTD_rust_createCCtxState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_createCCtxState, cctx_size) == size_of::()); assert!(offset_of!(ZSTD_rust_createCCtxState, validate_custom_mem) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_createCCtxState, allocate) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_createCCtxState, init) == 4 * size_of::()); assert!(size_of::() == size_of::<[usize; 5]>()); }; /// Allocate and initialize a C-owned compression context. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_createCCtx( state: *const ZSTD_rust_createCCtxState, ) -> *mut c_void { if state.is_null() { return ptr::null_mut(); } let state = unsafe { &*state }; if state.callback_context.is_null() || unsafe { (state.validate_custom_mem)(state.callback_context) == 0 } { return ptr::null_mut(); } let cctx = unsafe { (state.allocate)(state.callback_context, state.cctx_size) }; if cctx.is_null() { return ptr::null_mut(); } unsafe { (state.init)(state.callback_context, cctx) }; cctx } type InitStaticCCtxFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void; const ZSTD_STATIC_WORKSPACE_ALIGNMENT: usize = 8; /// Explicit projection for the public `ZSTD_initStaticCCtx` wrapper. /// /// Rust owns workspace pointer/alignment validation, minimum-size validation, /// and callback ordering. C retains the private static-workspace layout and /// context initialization behind one callback. #[repr(C)] pub struct ZSTD_rust_initStaticCCtxState { callback_context: *mut c_void, workspace: *mut c_void, workspace_size: usize, cctx_size: usize, init: InitStaticCCtxFn, } const _: () = { assert!(offset_of!(ZSTD_rust_initStaticCCtxState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace) == size_of::()); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace_size) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, cctx_size) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, init) == 4 * size_of::()); assert!(size_of::() == size_of::<[usize; 5]>()); }; /// Validate static CCtx workspace ownership before entering the C leaf. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_initStaticCCtx( state: *const ZSTD_rust_initStaticCCtxState, ) -> *mut c_void { if state.is_null() { return ptr::null_mut(); } let state = unsafe { &*state }; if state.callback_context.is_null() || state.workspace.is_null() || (state.workspace as usize) & (ZSTD_STATIC_WORKSPACE_ALIGNMENT - 1) != 0 || state.workspace_size <= state.cctx_size { return ptr::null_mut(); } unsafe { (state.init)(state.callback_context) } } 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::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_freeCCtxState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_freeCCtxState, static_size) == size_of::()); assert!(offset_of!(ZSTD_rust_freeCCtxState, cctx_in_workspace) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_freeCCtxState, free_content) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_freeCCtxState, free_object) == 4 * size_of::()); assert!(size_of::() == 5 * size_of::()); }; /// 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::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_resetCCtxState, callback_context) == 0); assert!( offset_of!(ZSTD_rust_resetCCtxState, rust_simple_compress2_completed) == size_of::() ); assert!(offset_of!(ZSTD_rust_resetCCtxState, stream_stage) == 2 * size_of::()); assert!( offset_of!(ZSTD_rust_resetCCtxState, pledged_src_size_plus_one) == 3 * size_of::() ); assert!( offset_of!( ZSTD_rust_resetCCtxState, rust_simple_compress2_max_block_size_set ) == 4 * size_of::() ); assert!(offset_of!(ZSTD_rust_resetCCtxState, clear_all_dicts) == 5 * size_of::()); assert!(offset_of!(ZSTD_rust_resetCCtxState, reset_params) == 6 * size_of::()); assert!(size_of::() == 7 * size_of::()); }; /// 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, } const _: () = { assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_copyCCtxState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_copyCCtxState, src_cctx) == size_of::()); assert!(offset_of!(ZSTD_rust_copyCCtxState, f_params) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_copyCCtxState, pledged_src_size) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_copyCCtxState, zbuff) == 4 * size_of::()); assert!(offset_of!(ZSTD_rust_copyCCtxState, copy_internal) == 5 * size_of::()); assert!(size_of::() == size_of::<[usize; 6]>()); }; /// Normalize the public copy request before invoking the private C operation. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_copyCCtx(state: *const ZSTD_rust_copyCCtxState) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; let Some(copy_internal) = state.copy_internal else { return ERROR(ZstdErrorCode::Generic); }; if state.callback_context.is_null() || state.src_cctx.is_null() || state.f_params.is_null() || state.pledged_src_size.is_null() || state.zbuff.is_null() { return ERROR(ZstdErrorCode::Generic); } let pledged_src_size = unsafe { *state.pledged_src_size }; let pledged_src_size = if pledged_src_size == 0 { ZSTD_CONTENTSIZE_UNKNOWN } else { pledged_src_size }; let mut f_params = unsafe { *state.f_params }; f_params.contentSizeFlag = c_int::from(pledged_src_size != ZSTD_CONTENTSIZE_UNKNOWN); unsafe { copy_internal( state.callback_context, state.src_cctx, &f_params, pledged_src_size, *state.zbuff, ) } } type CopyCCtxResetFn = unsafe extern "C" fn( *mut c_void, *const c_void, *const ZSTD_frameParameters, u64, c_int, ) -> usize; type CopyCCtxMarkTablesFn = unsafe extern "C" fn(*mut c_void); type CopyCCtxCopyTablesFn = unsafe extern "C" fn(*mut c_void, *const c_void); #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct ZSTD_rust_copyWindowState { next_src: *const c_void, base: *const c_void, dict_base: *const c_void, dict_limit: c_uint, low_limit: c_uint, nb_overflow_corrections: c_uint, } const _: () = { assert!(offset_of!(ZSTD_rust_copyWindowState, next_src) == 0); assert!(offset_of!(ZSTD_rust_copyWindowState, base) == size_of::()); assert!(offset_of!(ZSTD_rust_copyWindowState, dict_base) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_copyWindowState, dict_limit) == 3 * size_of::()); assert!( offset_of!(ZSTD_rust_copyWindowState, low_limit) == 3 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_copyWindowState, nb_overflow_corrections) == 3 * size_of::() + 2 * size_of::() ); assert!( size_of::() == (3 * size_of::() + 3 * size_of::()).div_ceil(size_of::()) * size_of::() ); }; const _: () = { assert!(size_of::() == 3 * size_of::()); }; /// Projection for the private `ZSTD_copyCCtx_internal` operation. /// /// Rust owns the stage/error branch and the order of the reset, workspace, /// table, window, dictionary, and block-state operations. C retains access to /// the private `ZSTD_CCtx` layout behind callbacks. #[repr(C)] pub struct ZSTD_rust_copyCCtxInternalState { callback_context: *mut c_void, src_cctx: *const c_void, f_params: *const ZSTD_frameParameters, pledged_src_size: u64, source_stage: *const c_int, destination_custom_mem: *mut c_void, source_custom_mem: *const c_void, reset: Option, mark_tables_dirty: Option, copy_tables: Option, mark_tables_clean: Option, destination_window: *mut c_void, source_window: *const c_void, destination_next_to_update: *mut c_uint, source_next_to_update: *const c_uint, destination_loaded_dict_end: *mut c_uint, source_loaded_dict_end: *const c_uint, destination_dict_id: *mut c_uint, source_dict_id: *const c_uint, destination_dict_content_size: *mut usize, source_dict_content_size: *const usize, destination_block_state: *mut *mut ZSTD_compressedBlockState_t, source_block_state: *const ZSTD_compressedBlockState_t, zbuff: c_int, } const _: () = { assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_copyCCtxInternalState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_copyCCtxInternalState, src_cctx) == size_of::()); assert!(offset_of!(ZSTD_rust_copyCCtxInternalState, f_params) == 2 * size_of::()); assert!( offset_of!(ZSTD_rust_copyCCtxInternalState, pledged_src_size) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_copyCCtxInternalState, source_stage) == 3 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_copyCCtxInternalState, zbuff) == 3 * size_of::() + size_of::() + 19 * size_of::() ); assert!( size_of::() == (offset_of!(ZSTD_rust_copyCCtxInternalState, zbuff) + size_of::()) .div_ceil(size_of::()) * size_of::() ); }; /// Run the private context-copy operation through C-owned layout callbacks /// and copy the compressed-block state directly in Rust. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_copyCCtxInternal( state: *const ZSTD_rust_copyCCtxInternalState, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; let (Some(reset), Some(mark_tables_dirty), Some(copy_tables), Some(mark_tables_clean)) = ( state.reset, state.mark_tables_dirty, state.copy_tables, state.mark_tables_clean, ) else { return ERROR(ZstdErrorCode::Generic); }; if state.callback_context.is_null() || state.src_cctx.is_null() || state.f_params.is_null() || state.source_stage.is_null() || state.destination_custom_mem.is_null() || state.source_custom_mem.is_null() || state.destination_window.is_null() || state.source_window.is_null() || state.destination_next_to_update.is_null() || state.source_next_to_update.is_null() || state.destination_loaded_dict_end.is_null() || state.source_loaded_dict_end.is_null() || state.destination_dict_id.is_null() || state.source_dict_id.is_null() || state.destination_dict_content_size.is_null() || state.source_dict_content_size.is_null() || state.destination_block_state.is_null() || state.source_block_state.is_null() { return ERROR(ZstdErrorCode::Generic); } if unsafe { *state.source_stage } != ZSTD_COMPRESSION_STAGE_INIT { return ERROR(ZstdErrorCode::StageWrong); } unsafe { ptr::copy( state.source_custom_mem.cast::(), state.destination_custom_mem.cast::(), 1, ); let reset_error = reset( state.callback_context, state.src_cctx, state.f_params, state.pledged_src_size, state.zbuff, ); if ERR_isError(reset_error) { return reset_error; } mark_tables_dirty(state.callback_context); copy_tables(state.callback_context, state.src_cctx); mark_tables_clean(state.callback_context); ptr::copy( state.source_window.cast::(), state.destination_window.cast::(), 1, ); *state.destination_next_to_update = *state.source_next_to_update; *state.destination_loaded_dict_end = *state.source_loaded_dict_end; *state.destination_dict_id = *state.source_dict_id; *state.destination_dict_content_size = *state.source_dict_content_size; let destination_block_state = *state.destination_block_state; if destination_block_state.is_null() { return ERROR(ZstdErrorCode::Generic); } ptr::copy_nonoverlapping(state.source_block_state, destination_block_state, 1); } 0 } type CompressAdvancedInitParamsFn = unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters); type CompressAdvancedInternalFn = unsafe extern "C" fn( *mut c_void, *mut c_void, usize, *const c_void, usize, *const c_void, usize, ) -> usize; /// Explicit projection for the public `ZSTD_compress_advanced` wrapper. /// /// Rust owns parameter validation and the init-then-compress order. C retains /// the private `simpleApiParams` initialization and advanced compression call. #[repr(C)] pub struct ZSTD_rust_compressAdvancedState { callback_context: *mut c_void, init_params: Option, compress_internal: Option, } const _: () = { assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_compressAdvancedState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_compressAdvancedState, init_params) == size_of::()); assert!( offset_of!(ZSTD_rust_compressAdvancedState, compress_internal) == 2 * size_of::() ); assert!(size_of::() == size_of::<[usize; 3]>()); }; /// Validate parameters and invoke the C-owned advanced compression operation. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_compressAdvanced( state: *const ZSTD_rust_compressAdvancedState, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, params: *const ZSTD_parameters, ) -> usize { if state.is_null() || params.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; let Some(init_params) = state.init_params else { return ERROR(ZstdErrorCode::Generic); }; let Some(compress_internal) = state.compress_internal else { return ERROR(ZstdErrorCode::Generic); }; if state.callback_context.is_null() { return ERROR(ZstdErrorCode::Generic); } let params = unsafe { &*params }; let check_result = ZSTD_rust_params_checkCParams(params.cParams); if ERR_isError(check_result) { return check_result; } unsafe { init_params(state.callback_context, params) }; unsafe { compress_internal( state.callback_context, dst, dst_capacity, src, src_size, dict, dict_size, ) } } type CompressBeginAdvancedInitParamsFn = unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters); type CompressBeginAdvancedBeginFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, *const c_void, u64) -> usize; /// Explicit projection for the public `ZSTD_compressBegin_advanced` wrapper. /// /// Rust owns public parameter validation and init-then-begin ordering. C /// retains private `ZSTD_CCtx_params` initialization and the final begin /// operation behind callbacks. #[repr(C)] pub struct ZSTD_rust_compressBeginAdvancedState { cctx: *mut c_void, cctx_params: *mut c_void, init_params: CompressBeginAdvancedInitParamsFn, begin: CompressBeginAdvancedBeginFn, } const _: () = { assert!(offset_of!(ZSTD_rust_compressBeginAdvancedState, cctx) == 0); assert!(offset_of!(ZSTD_rust_compressBeginAdvancedState, cctx_params) == size_of::()); assert!( offset_of!(ZSTD_rust_compressBeginAdvancedState, init_params) == 2 * size_of::() ); assert!(offset_of!(ZSTD_rust_compressBeginAdvancedState, begin) == 3 * size_of::()); assert!(size_of::() == size_of::<[usize; 4]>()); }; /// Validate parameters and begin a frame through the C-owned operation. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_compressBeginAdvanced( state: *const ZSTD_rust_compressBeginAdvancedState, dict: *const c_void, dict_size: usize, params: *const ZSTD_parameters, pledged_src_size: u64, ) -> usize { if state.is_null() || params.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; if state.cctx.is_null() || state.cctx_params.is_null() { return ERROR(ZstdErrorCode::Generic); } let params = unsafe { &*params }; let check_result = ZSTD_rust_params_checkCParams(params.cParams); if ERR_isError(check_result) { return check_result; } unsafe { (state.init_params)(state.cctx_params, params); (state.begin)( state.cctx, dict, dict_size, state.cctx_params, pledged_src_size, ) } } type CompressUsingDictInitParamsFn = unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters, c_int); /// Explicit projection for the public `ZSTD_compress_usingDict` wrapper. /// /// Rust owns compression-parameter selection, dictionary-presence handling, /// and the default-level normalization. C retains the private /// `ZSTD_CCtx_params` initialization and advanced compression operation. #[repr(C)] pub struct ZSTD_rust_compressUsingDictState { callback_context: *mut c_void, exclusion_mask: *const c_uint, init_params: Option, compress_internal: Option, } const _: () = { assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_compressUsingDictState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_compressUsingDictState, exclusion_mask) == size_of::()); assert!(offset_of!(ZSTD_rust_compressUsingDictState, init_params) == 2 * size_of::()); assert!( offset_of!(ZSTD_rust_compressUsingDictState, compress_internal) == 3 * size_of::() ); assert!(size_of::() == size_of::<[usize; 4]>()); }; /// Select parameters and invoke the C-owned dictionary compression operation. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_compressUsingDict( state: *const ZSTD_rust_compressUsingDictState, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, compression_level: c_int, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; let Some(init_params) = state.init_params else { return ERROR(ZstdErrorCode::Generic); }; let Some(compress_internal) = state.compress_internal else { return ERROR(ZstdErrorCode::Generic); }; if state.callback_context.is_null() || state.exclusion_mask.is_null() { return ERROR(ZstdErrorCode::Generic); } let dict_size_for_params = if dict.is_null() { 0 } else { dict_size }; let params = ZSTD_rust_params_getParamsInternal( compression_level, src_size as u64, dict_size_for_params, ZSTD_RUST_CPM_NO_ATTACH_DICT, unsafe { *state.exclusion_mask }, ); let init_level = if compression_level == 0 { ZSTD_rust_params_defaultCLevel() } else { compression_level }; unsafe { init_params(state.callback_context, ¶ms, init_level) }; unsafe { compress_internal( state.callback_context, dst, dst_capacity, src, src_size, dict, dict_size, ) } } type ResetCStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize; type ResetCStreamSetPledgedSrcSizeFn = unsafe extern "C" fn(*mut c_void, u64) -> usize; /// Explicit projection for the public `ZSTD_resetCStream` wrapper. /// /// Rust owns the zero-to-unknown conversion, callback ordering, and error /// propagation. The opaque callback context stays in C, where the callbacks /// retain access to the private `ZSTD_CCtx` layout. #[repr(C)] pub struct ZSTD_rust_resetCStreamState { callback_context: *mut c_void, reset_session: ResetCStreamResetFn, set_pledged_src_size: ResetCStreamSetPledgedSrcSizeFn, } const _: () = { assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_resetCStreamState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_resetCStreamState, reset_session) == size_of::()); assert!( offset_of!(ZSTD_rust_resetCStreamState, set_pledged_src_size) == 2 * size_of::() ); assert!(size_of::() == 3 * size_of::()); }; /// 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::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!( offset_of!( ZSTD_rust_initCStreamUsingCDictAdvancedState, callback_context ) == 0 ); assert!( offset_of!(ZSTD_rust_initCStreamUsingCDictAdvancedState, reset_session) == size_of::() ); assert!( offset_of!( ZSTD_rust_initCStreamUsingCDictAdvancedState, set_pledged_src_size ) == 2 * size_of::() ); assert!( offset_of!( ZSTD_rust_initCStreamUsingCDictAdvancedState, set_frame_params ) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_initCStreamUsingCDictAdvancedState, ref_cdict) == 4 * size_of::() ); assert!(size_of::() == 5 * size_of::()); }; #[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::()); assert!(offset_of!(ZSTD_rust_initCStreamUsingCDictState, ref_cdict) == 2 * size_of::()); assert!(size_of::() == 3 * size_of::()); }; #[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::()); assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, ref_cdict) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, set_level) == 3 * size_of::()); assert!( offset_of!(ZSTD_rust_initCStreamSrcSizeState, set_pledged_src_size) == 4 * size_of::() ); assert!(size_of::() == 5 * size_of::()); }; #[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::()); assert!(offset_of!(ZSTD_rust_initCStreamState, ref_cdict) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_initCStreamState, set_level) == 3 * size_of::()); assert!(size_of::() == 4 * size_of::()); }; #[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::()); assert!(offset_of!(ZSTD_rust_initCStreamUsingDictState, set_level) == 2 * size_of::()); assert!( offset_of!(ZSTD_rust_initCStreamUsingDictState, load_dictionary) == 3 * size_of::() ); assert!(size_of::() == 4 * size_of::()); }; #[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::()); assert!( offset_of!(ZSTD_rust_initCStreamAdvancedState, set_pledged_src_size) == 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_initCStreamAdvancedState, check_c_params) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_initCStreamAdvancedState, set_zstd_params) == 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_initCStreamAdvancedState, load_dictionary) == 5 * size_of::() ); assert!(size_of::() == 6 * size_of::()); }; #[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::()); assert!(offset_of!(ZSTD_rust_setCParamsState, set_parameter) == 2 * size_of::()); assert!(size_of::() == 3 * size_of::()); }; #[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::()); assert!(size_of::() == 2 * size_of::()); }; #[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::()); assert!(offset_of!(ZSTD_rust_setParamsState, set_f_params) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_setParamsState, set_c_params) == 3 * size_of::()); assert!(size_of::() == 4 * size_of::()); }; #[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::()); assert!( offset_of!(ZSTD_rust_compressStreamState, out_buffer_mode) == size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, stream_stage) == size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, block_size_max) == 2 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, stable_in_not_consumed) == 3 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, in_buff) == 4 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, in_buff_size) == 5 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, in_to_compress) == 5 * size_of::() + 2 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, in_buff_pos) == 6 * size_of::() + 2 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, in_buff_target) == 7 * size_of::() + 2 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, out_buff) == 9 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, out_buff_size) == 9 * size_of::() + 2 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, out_buff_content_size) == 9 * size_of::() + 2 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, out_buff_flushed_size) == 10 * size_of::() + 2 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, frame_ended) == 11 * size_of::() + 2 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, compress_continue) == 12 * size_of::() + 2 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, compress_end) == 13 * size_of::() + 2 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamState, reset_session) == 14 * size_of::() + 2 * size_of::() + 2 * size_of::() ); assert!( size_of::() == 15 * size_of::() + 2 * size_of::() + 2 * size_of::() ); }; const ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER: c_int = 0; const ZSTD_RUST_INIT_RESOLVE_LDM: c_int = 1; const ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER: c_int = 2; const ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES: c_int = 3; const ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE: c_int = 4; const ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH: c_int = 5; type CompressStreamInitGetLocalDictFn = unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitLocalDictState); type CompressStreamInitCreateLocalDictFn = unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitLocalDictState) -> usize; type CompressStreamInitPublishLocalDictFn = unsafe extern "C" fn(*mut c_void, *const ZSTD_rust_compressStreamInitLocalDictState); type CompressStreamInitRefreshCDictFn = unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitDictionaryState); type CompressStreamInitClearPrefixFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitAssertDictionariesFn = unsafe extern "C" fn(*mut c_void, *const c_void); type CompressStreamInitSetLevelFn = unsafe extern "C" fn(*mut c_void, c_int); type CompressStreamInitDebugFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitGetPledgedFn = unsafe extern "C" fn(*mut c_void) -> u64; type CompressStreamInitSetPledgedFn = unsafe extern "C" fn(*mut c_void, usize); type CompressStreamInitGetCParamModeFn = unsafe extern "C" fn(*mut c_void, *const c_void, u64) -> c_int; type CompressStreamInitBuildCParamsFn = unsafe extern "C" fn(*mut c_void, u64, usize, c_int); type CompressStreamInitResolveParamsFn = unsafe extern "C" fn(*mut c_void, c_int); type CompressStreamInitGetNbWorkersFn = unsafe extern "C" fn(*mut c_void) -> c_uint; type CompressStreamInitSetNbWorkersFn = unsafe extern "C" fn(*mut c_void, c_uint); type CompressStreamInitHasExtSeqProdFn = unsafe extern "C" fn(*mut c_void) -> c_int; type CompressStreamInitTraceFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitGetMTContextFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void; type CompressStreamInitCreateMTContextFn = unsafe extern "C" fn(*mut c_void, c_uint) -> usize; type CompressStreamInitMTFn = unsafe extern "C" fn( *mut c_void, *mut c_void, *const ZSTD_rust_compressStreamInitDictionaryState, *mut c_void, u64, ) -> usize; type CompressStreamInitCommitMTFn = unsafe extern "C" fn( *mut c_void, *const ZSTD_rust_compressStreamInitDictionaryState, *mut c_void, ); type CompressStreamInitCheckCParamsFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitBeginFn = unsafe extern "C" fn( *mut c_void, *const ZSTD_rust_compressStreamInitDictionaryState, *mut c_void, u64, ) -> usize; type CompressStreamInitAssertOrdinaryFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitGetBufferModeFn = unsafe extern "C" fn(*mut c_void) -> c_int; type CompressStreamInitGetBlockSizeFn = unsafe extern "C" fn(*mut c_void) -> usize; type CompressStreamInitCommitOrdinaryFn = unsafe extern "C" fn(*mut c_void, usize); /// Private local-dictionary projection used by transparent stream initialization. /// /// Rust owns the no-dictionary, already-initialized, create, and publish order; /// C retains the private dictionary storage and allocator-sensitive callbacks. #[repr(C)] pub struct ZSTD_rust_compressStreamInitLocalDictState { dict: *const c_void, dict_buffer: *mut c_void, dict_size: usize, dict_content_type: c_int, local_cdict: *mut c_void, cdict: *const c_void, prefix_dict: *const c_void, created_cdict: *mut c_void, } const _: () = { assert!(offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict) == 0); assert!( offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict_buffer) == size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict_size) == 2 * size_of::() ); assert!( offset_of!( ZSTD_rust_compressStreamInitLocalDictState, dict_content_type ) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitLocalDictState, local_cdict) == 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitLocalDictState, cdict) == 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitLocalDictState, prefix_dict) == 6 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitLocalDictState, created_cdict) == 7 * size_of::() ); assert!(size_of::() == size_of::<[usize; 8]>()); }; /// Scalar dictionary snapshot used by transparent stream initialization. /// /// The prefix is populated by C before local-dictionary initialization, which /// preserves the original single-use snapshot order. C refreshes only the /// CDict fields after local-dictionary initialization because that operation /// may create the local CDict. #[repr(C)] pub struct ZSTD_rust_compressStreamInitDictionaryState { prefix_dict: *const c_void, prefix_dict_size: usize, prefix_dict_content_type: c_int, cdict: *const c_void, cdict_is_local: c_int, cdict_compression_level: c_int, cdict_dict_content_size: usize, } const _: () = { assert!(offset_of!(ZSTD_rust_compressStreamInitDictionaryState, prefix_dict) == 0); assert!( offset_of!( ZSTD_rust_compressStreamInitDictionaryState, prefix_dict_size ) == size_of::() ); assert!( offset_of!( ZSTD_rust_compressStreamInitDictionaryState, prefix_dict_content_type ) == 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitDictionaryState, cdict) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitDictionaryState, cdict_is_local) == 4 * size_of::() ); assert!( offset_of!( ZSTD_rust_compressStreamInitDictionaryState, cdict_compression_level ) == 4 * size_of::() + size_of::() ); assert!( offset_of!( ZSTD_rust_compressStreamInitDictionaryState, cdict_dict_content_size ) == 4 * size_of::() + 2 * size_of::() ); assert!( size_of::() == if size_of::() == 8 { 48 } else { 28 } ); }; /// Projection for `ZSTD_CCtx_init_compressStream2()`. /// /// Rust owns the ordering and branch policy. The C callback slots retain /// private parameter/context layouts, local dictionary storage, allocators, /// trace setup, MT construction/init, and codec/reset operations. #[repr(C)] pub struct ZSTD_rust_compressStreamInitState { callback_context: *mut c_void, params: *mut c_void, dictionaries: *mut ZSTD_rust_compressStreamInitDictionaryState, end_op: c_int, in_size: usize, multithreaded: c_int, mt_job_size_min: usize, get_local_dict: CompressStreamInitGetLocalDictFn, create_local_dict: CompressStreamInitCreateLocalDictFn, publish_local_dict: CompressStreamInitPublishLocalDictFn, refresh_cdict: CompressStreamInitRefreshCDictFn, clear_prefix: CompressStreamInitClearPrefixFn, assert_dictionaries: CompressStreamInitAssertDictionariesFn, set_compression_level: CompressStreamInitSetLevelFn, debug_init: CompressStreamInitDebugFn, get_pledged_src_size_plus_one: CompressStreamInitGetPledgedFn, set_pledged_src_size: CompressStreamInitSetPledgedFn, get_cparam_mode: CompressStreamInitGetCParamModeFn, build_cparams: CompressStreamInitBuildCParamsFn, resolve_params: CompressStreamInitResolveParamsFn, get_nb_workers: CompressStreamInitGetNbWorkersFn, set_nb_workers: CompressStreamInitSetNbWorkersFn, has_ext_seq_prod: CompressStreamInitHasExtSeqProdFn, trace_begin: CompressStreamInitTraceFn, get_mt_context: CompressStreamInitGetMTContextFn, create_mt_context: CompressStreamInitCreateMTContextFn, init_mt: CompressStreamInitMTFn, commit_mt: CompressStreamInitCommitMTFn, check_cparams: CompressStreamInitCheckCParamsFn, compress_begin: CompressStreamInitBeginFn, assert_ordinary: CompressStreamInitAssertOrdinaryFn, get_buffer_mode: CompressStreamInitGetBufferModeFn, get_block_size: CompressStreamInitGetBlockSizeFn, commit_ordinary: CompressStreamInitCommitOrdinaryFn, } const _: () = { assert!(offset_of!(ZSTD_rust_compressStreamInitState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_compressStreamInitState, params) == size_of::()); assert!(offset_of!(ZSTD_rust_compressStreamInitState, dictionaries) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_compressStreamInitState, end_op) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_compressStreamInitState, in_size) == 4 * size_of::()); assert!( offset_of!(ZSTD_rust_compressStreamInitState, multithreaded) == 4 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressStreamInitState, mt_job_size_min) == 5 * size_of::() + size_of::() ); assert!( size_of::() == if size_of::() == 8 { 272 } else { 144 } ); }; #[inline] unsafe fn compress_stream_init_local_dict_with(state: &ZSTD_rust_compressStreamInitState) -> usize { let mut local_dict = ZSTD_rust_compressStreamInitLocalDictState { dict: ptr::null(), dict_buffer: ptr::null_mut(), dict_size: 0, dict_content_type: 0, local_cdict: ptr::null_mut(), cdict: ptr::null(), prefix_dict: ptr::null(), created_cdict: ptr::null_mut(), }; unsafe { (state.get_local_dict)(state.callback_context, &mut local_dict); } if local_dict.dict.is_null() { debug_assert!(local_dict.dict_buffer.is_null()); debug_assert!(local_dict.local_cdict.is_null()); debug_assert_eq!(local_dict.dict_size, 0); return 0; } if !local_dict.local_cdict.is_null() { debug_assert_eq!(local_dict.cdict, local_dict.local_cdict.cast_const(),); return 0; } debug_assert!(local_dict.dict_size > 0); debug_assert!(local_dict.cdict.is_null()); debug_assert!(local_dict.prefix_dict.is_null()); let result = unsafe { (state.create_local_dict)(state.callback_context, &mut local_dict) }; if ERR_isError(result) { return result; } debug_assert!(!local_dict.created_cdict.is_null()); unsafe { (state.publish_local_dict)(state.callback_context, &local_dict); } 0 } #[inline] unsafe fn compress_stream_init_body_with(state: &ZSTD_rust_compressStreamInitState) -> usize { if state.params.is_null() || state.dictionaries.is_null() { return ERROR(ZstdErrorCode::Generic); } let result = unsafe { compress_stream_init_local_dict_with(state) }; if ERR_isError(result) { return result; } unsafe { (state.refresh_cdict)(state.callback_context, state.dictionaries) }; let dictionaries = unsafe { &*state.dictionaries }; unsafe { (state.clear_prefix)(state.callback_context) }; unsafe { (state.assert_dictionaries)(state.callback_context, dictionaries.prefix_dict) }; if !dictionaries.cdict.is_null() && dictionaries.cdict_is_local == 0 { unsafe { (state.set_compression_level)(state.params, dictionaries.cdict_compression_level) }; } unsafe { (state.debug_init)(state.callback_context) }; if state.end_op == ZSTD_E_END { unsafe { (state.set_pledged_src_size)(state.callback_context, state.in_size) }; } let pledged_src_size_plus_one = unsafe { (state.get_pledged_src_size_plus_one)(state.callback_context) }; let pledged_src_size = pledged_src_size_plus_one.wrapping_sub(1); let dict_size = if !dictionaries.prefix_dict.is_null() { dictionaries.prefix_dict_size } else if !dictionaries.cdict.is_null() { dictionaries.cdict_dict_content_size } else { 0 }; let mode = unsafe { (state.get_cparam_mode)(state.params, dictionaries.cdict, pledged_src_size) }; unsafe { (state.build_cparams)(state.params, pledged_src_size, dict_size, mode); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_LDM); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH); } if state.multithreaded != 0 { let has_ext_seq_prod = unsafe { (state.has_ext_seq_prod)(state.params) }; let mut nb_workers = unsafe { (state.get_nb_workers)(state.params) }; if has_ext_seq_prod != 0 && nb_workers >= 1 { return ERROR(ZstdErrorCode::ParameterCombinationUnsupported); } if pledged_src_size <= state.mt_job_size_min as u64 { nb_workers = 0; unsafe { (state.set_nb_workers)(state.params, 0) }; } if nb_workers > 0 { unsafe { (state.trace_begin)(state.callback_context) }; if unsafe { (state.get_mt_context)(state.callback_context) }.is_null() { let result = unsafe { (state.create_mt_context)(state.callback_context, nb_workers) }; if ERR_isError(result) { return result; } } let result = unsafe { (state.init_mt)( state.callback_context, (state.get_mt_context)(state.callback_context), dictionaries, state.params, pledged_src_size, ) }; if ERR_isError(result) { return result; } unsafe { (state.commit_mt)(state.callback_context, dictionaries, state.params) }; return 0; } } unsafe { (state.check_cparams)(state.params) }; let result = unsafe { (state.compress_begin)( state.callback_context, dictionaries, state.params, pledged_src_size, ) }; if ERR_isError(result) { return result; } unsafe { (state.assert_ordinary)(state.callback_context) }; let buffer_mode = unsafe { (state.get_buffer_mode)(state.callback_context) }; let block_size = unsafe { (state.get_block_size)(state.callback_context) }; let in_buff_target = if buffer_mode == ZSTD_BM_BUFFERED { block_size.wrapping_add(usize::from(block_size as u64 == pledged_src_size)) } else { 0 }; unsafe { (state.commit_ordinary)(state.callback_context, in_buff_target) }; 0 } /// Rust-owned high-level policy for transparent stream initialization. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_compressStreamInit( state: *const ZSTD_rust_compressStreamInitState, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { compress_stream_init_body_with(&*state) } } #[inline] unsafe fn stream_limit_copy( dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, ) -> usize { let length = dst_capacity.min(src_size); if length != 0 { unsafe { ptr::copy_nonoverlapping(src.cast::(), dst.cast::(), 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::().add(output.pos).cast() } }; let src = if input.src.is_null() { ptr::null() } else { unsafe { input.src.cast::().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::().add(input.pos).cast() } }; let dst = if state.in_buff.is_null() { ptr::null_mut() } else { unsafe { state.in_buff.cast::().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::().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::().add(*state.in_to_compress).cast() } } } else if input.src.is_null() { ptr::null() } else { unsafe { input.src.cast::().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::().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::() .add(*state.out_buff_flushed_size) .cast() } }; let dst = if output.dst.is_null() { ptr::null_mut() } else { unsafe { output.dst.cast::().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::()); assert!(offset_of!(ZSTD_rust_sequenceCompressionState, next_c_block) == 2 * size_of::()); assert!( offset_of!(ZSTD_rust_sequenceCompressionState, tmp_workspace) == 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceCompressionState, tmp_wksp_size) == 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceCompressionState, block_size_max) == 5 * size_of::() ); assert!(offset_of!(ZSTD_rust_sequenceCompressionState, bmi2) == 6 * size_of::()); assert!( offset_of!(ZSTD_rust_sequenceCompressionState, min_match) == 6 * size_of::() + 6 * size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceCompressionState, use_sequence_producer) == 6 * size_of::() + 6 * size_of::() + 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceCompressionState, is_first_block) == 6 * size_of::() + 7 * size_of::() + 3 * size_of::() ); assert!( size_of::() == if size_of::() == 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::()); assert!( offset_of!(ZSTD_rust_convertBlockSequencesState, next_c_block) == 2 * size_of::() ); assert!(size_of::() == 3 * size_of::()); }; const _: () = { assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, seq_store) == 0); assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, prev_c_block) == size_of::()); assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, next_c_block) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, tmp_workspace) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, tmp_wksp_size) == 4 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, block_size_max) == 5 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceLiteralsState, bmi2) == 6 * size_of::()); assert!( offset_of!(ZSTD_rust_sequenceLiteralsState, strategy) == 6 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceLiteralsState, disable_literal_compression) == 6 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceLiteralsState, repcode_resolution) == 6 * size_of::() + 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceLiteralsState, is_first_block) == 6 * size_of::() + 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceLiteralsState, callback_context) == 6 * size_of::() + 4 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_sequenceLiteralsState, convert_block_sequences) == 6 * size_of::() + 4 * size_of::() + 2 * size_of::() ); assert!( size_of::() == 6 * size_of::() + 4 * size_of::() + 3 * size_of::() ); }; 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::()); assert!( offset_of!(ZSTD_rust_sequenceApiState, sequence_literals_state) == 2 * size_of::() ); assert!(offset_of!(ZSTD_rust_sequenceApiState, init) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceApiState, write_frame_header) == 4 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceApiState, update_checksum) == 5 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceApiState, digest_checksum) == 6 * size_of::()); assert!(offset_of!(ZSTD_rust_sequenceApiState, write_checksum) == 7 * size_of::()); 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::() ); assert!( offset_of!(ZSTD_rust_sequenceApiState, validate_sequences) == size_of::<[usize; 8]>() + 2 * size_of::() ); assert!( size_of::() == if size_of::() == 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 { 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::()); assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, c_rep) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, prev_c_block) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_seqStoreSingleBlockState, next_c_block) == 4 * size_of::()); assert!( offset_of!(ZSTD_rust_seqStoreSingleBlockState, tmp_workspace) == 5 * size_of::() ); assert!( offset_of!(ZSTD_rust_seqStoreSingleBlockState, tmp_wksp_size) == 6 * size_of::() ); assert!( offset_of!(ZSTD_rust_seqStoreSingleBlockState, seq_collector) == 7 * size_of::() ); 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::() ); assert!( offset_of!(ZSTD_rust_seqStoreSingleBlockState, bmi2) == usize::BITS as usize + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_seqStoreSingleBlockState, is_first_block) == usize::BITS as usize + 3 * size_of::() ); assert!( size_of::() == usize::BITS as usize + 4 * size_of::() ); }; /// 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::()); assert!(offset_of!(ZSTD_rust_splitBlockState, next_seq_store) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_splitBlockState, curr_seq_store) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_splitBlockState, prev_c_block) == 4 * size_of::()); assert!(offset_of!(ZSTD_rust_splitBlockState, next_c_block) == 5 * size_of::()); assert!(offset_of!(ZSTD_rust_splitBlockState, tmp_workspace) == 6 * size_of::()); assert!(offset_of!(ZSTD_rust_splitBlockState, tmp_wksp_size) == 7 * size_of::()); assert!(offset_of!(ZSTD_rust_splitBlockState, seq_collector) == usize::BITS as usize); assert!(offset_of!(ZSTD_rust_splitBlockState, block_size_max) == 9 * size_of::()); assert!(offset_of!(ZSTD_rust_splitBlockState, strategy) == 10 * size_of::()); assert!( offset_of!(ZSTD_rust_splitBlockState, disable_literal_compression) == 10 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_splitBlockState, bmi2) == 10 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_splitBlockState, is_first_block) == 10 * size_of::() + 3 * size_of::() ); assert!( size_of::() == 10 * size_of::() + 4 * size_of::() ); }; /// 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::()); assert!(offset_of!(ZSTD_rust_blockInternalState, next_c_block) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_blockInternalState, tmp_workspace) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_blockInternalState, tmp_wksp_size) == 4 * size_of::()); assert!(offset_of!(ZSTD_rust_blockInternalState, seq_collector) == 5 * size_of::()); assert!(offset_of!(ZSTD_rust_blockInternalState, strategy) == 6 * size_of::()); assert!( offset_of!(ZSTD_rust_blockInternalState, disable_literal_compression) == 6 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_blockInternalState, bmi2) == 6 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_blockInternalState, is_first_block) == 6 * size_of::() + 3 * size_of::() ); assert!( size_of::() == 6 * size_of::() + 4 * size_of::() ); }; /// 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::()); assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, next_c_block) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, tmp_workspace) == 3 * size_of::()); assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, tmp_wksp_size) == 4 * size_of::()); assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, strategy) == 5 * size_of::()); assert!( offset_of!(ZSTD_rust_targetCBlockSizeState, disable_literal_compression) == 5 * size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_targetCBlockSizeState, bmi2) == 5 * size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_targetCBlockSizeState, window_log) == 5 * size_of::() + 3 * size_of::() ); assert!( offset_of!(ZSTD_rust_targetCBlockSizeState, target_c_block_size) == 5 * size_of::() + 4 * size_of::() ); assert!( offset_of!(ZSTD_rust_targetCBlockSizeState, is_first_block) == 5 * size_of::() + 4 * size_of::() + size_of::() ); assert!( size_of::() == if size_of::() == 8 { 72 } else { 44 } ); }; #[repr(i32)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum TargetCBlockAction { Raw = 0, Rle = 1, Compressed = 2, Error = 3, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum SequenceBlockAction { Raw, Rle, Compressed, } type SingleBlockEntropyFn = unsafe extern "C" fn( *const SeqStore_t, *const ZSTD_entropyCTables_t, *mut ZSTD_entropyCTables_t, c_int, c_int, *mut c_void, usize, usize, *mut c_void, usize, c_int, ) -> usize; type SingleBlockResolveOffCodesFn = unsafe extern "C" fn(*mut u32, *mut u32, *const SeqStore_t, c_uint); type SingleBlockCopySequencesFn = unsafe extern "C" fn(*mut SeqCollector, *const SeqStore_t, *const u32) -> usize; type SingleBlockConfirmFn = unsafe extern "C" fn( *mut *mut ZSTD_compressedBlockState_t, *mut *mut ZSTD_compressedBlockState_t, ); type SingleBlockRawFn = unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_uint) -> usize; type SingleBlockRleFn = unsafe extern "C" fn(*mut c_void, usize, u8, usize, c_uint) -> usize; type SingleBlockWriteHeaderFn = unsafe extern "C" fn(*mut c_void, usize, usize, c_uint); type SingleBlockIsRleFn = unsafe extern "C" fn(*const u8, usize) -> c_int; #[derive(Clone, Copy)] struct SingleBlockSeams { entropy_compress: SingleBlockEntropyFn, resolve_off_codes: SingleBlockResolveOffCodesFn, copy_sequences: SingleBlockCopySequencesFn, confirm: SingleBlockConfirmFn, raw: SingleBlockRawFn, rle: SingleBlockRleFn, write_header: SingleBlockWriteHeaderFn, is_rle: SingleBlockIsRleFn, } impl SingleBlockSeams { fn production() -> Self { Self { entropy_compress: ZSTD_rust_entropyCompressSeqStore, resolve_off_codes: ZSTD_rust_seqStore_resolveOffCodes, copy_sequences: ZSTD_rust_copyBlockSequences, confirm: ZSTD_rust_confirmRepcodesAndEntropyTables, raw: ZSTD_rust_noCompressBlock, rle: ZSTD_rust_rleCompressBlock, write_header: ZSTD_rust_writeBlockHeader, is_rle: ZSTD_rust_isRLE, } } } /// Rust-owned sequence-store branch policy. C callbacks perform the /// operations which need the private matchfinder or CCtx /// parameter/function-pointer state. #[allow(clippy::too_many_arguments)] unsafe fn build_seq_store_select_sequences_body_with( state: &ZSTD_rust_buildSeqStoreState, seq_store: *mut SeqStore_t, next_rep: *mut u32, src: *const c_void, src_size: usize, seq_store_complete: &mut c_int, ) -> usize { if seq_store.is_null() || next_rep.is_null() || src.is_null() || state.compress_external_sequences as usize == 0 || state.compress_ldm as usize == 0 || state.try_external_sequence_producer as usize == 0 || state.compress_internal as usize == 0 || state.clear_ldm_seq_store as usize == 0 { return ERROR(ZstdErrorCode::Generic); } *seq_store_complete = 0; if state.has_external_sequences != 0 { if state.has_external_sequence_producer != 0 { return ERROR(ZstdErrorCode::ParameterCombinationUnsupported); } return unsafe { (state.compress_external_sequences)( state.callback_context, seq_store, next_rep, src, src_size, ) }; } if state.ldm_enabled != 0 { if state.has_external_sequence_producer != 0 { return ERROR(ZstdErrorCode::ParameterCombinationUnsupported); } return unsafe { (state.compress_ldm)(state.callback_context, seq_store, next_rep, src, src_size) }; } if state.has_external_sequence_producer != 0 { let mut allow_fallback = 0; let producer_result = unsafe { (state.try_external_sequence_producer)( state.callback_context, src, src_size, seq_store_complete, &mut allow_fallback, ) }; if *seq_store_complete != 0 { unsafe { (state.clear_ldm_seq_store)(state.callback_context) }; return producer_result; } if allow_fallback == 0 || state.enable_match_finder_fallback == 0 { return producer_result; } unsafe { (state.clear_ldm_seq_store)(state.callback_context) }; return unsafe { (state.compress_internal)(state.callback_context, seq_store, next_rep, src, src_size) }; } unsafe { (state.clear_ldm_seq_store)(state.callback_context) }; unsafe { (state.compress_internal)(state.callback_context, seq_store, next_rep, src, src_size) } } /// Rust-owned sequence-store boundary. C callbacks perform the operations /// which need the private matchfinder or CCtx parameter/function-pointer state. #[allow(clippy::too_many_arguments)] unsafe fn build_seq_store_body_with( state: &ZSTD_rust_buildSeqStoreState, src: *const c_void, src_size: usize, ) -> usize { if state.seq_store.is_null() || state.skip_small_block as usize == 0 || state.prepare_match_state as usize == 0 { return ERROR(ZstdErrorCode::Generic); } if src_size > ZSTD_BLOCKSIZE_MAX { return ERROR(ZstdErrorCode::SrcSizeWrong); } if src_size < MIN_COMPRESSIBLE_BLOCK_SIZE { unsafe { (state.skip_small_block)(state.callback_context, src_size) }; return ZSTD_BSS_NO_COMPRESS as usize; } if src.is_null() || state.prev_c_block.is_null() || state.next_c_block.is_null() { return ERROR(ZstdErrorCode::Generic); } let prev_c_block = unsafe { *state.prev_c_block }; let next_c_block = unsafe { *state.next_c_block }; if prev_c_block.is_null() || next_c_block.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { ZSTD_rust_resetSeqStore(state.seq_store) }; unsafe { (state.prepare_match_state)(state.callback_context, src, src_size); } unsafe { ptr::copy_nonoverlapping( (*prev_c_block).rep.as_ptr(), (*next_c_block).rep.as_mut_ptr(), ZSTD_REP_NUM, ); } let mut seq_store_complete = 0; let last_literals_size = unsafe { build_seq_store_select_sequences_body_with( state, state.seq_store, (*next_c_block).rep.as_mut_ptr(), src, src_size, &mut seq_store_complete, ) }; if ERR_isError(last_literals_size) { return last_literals_size; } if seq_store_complete != 0 { return ZSTD_BSS_COMPRESS as usize; } if last_literals_size > src_size { return ERROR(ZstdErrorCode::Generic); } unsafe { ZSTD_rust_storeLastLiterals( state.seq_store, src.cast::().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::() = *src.cast::(); } } 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::(), 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::(); let ip = src.cast::(); 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::(), 0, size_of::(), ); } 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::(); let mut op = dst.cast::(); 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 } type OverflowNeedCorrectionFn = unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void) -> c_int; type OverflowCorrectFn = unsafe extern "C" fn(*mut c_void, *const c_void) -> c_uint; type OverflowCallbackFn = unsafe extern "C" fn(*mut c_void); type OverflowReduceIndexFn = unsafe extern "C" fn(*mut c_void, c_uint); /// Projection for the overflow-correction ordering around C-owned match state. /// /// Rust owns the correction branch and callback order. C retains the window, /// workspace, match tables, and dictionary pointers behind these callbacks. #[repr(C)] pub struct ZSTD_rust_overflowCorrectState { callback_context: *mut c_void, next_to_update: *mut c_uint, need_correction: Option, correct_overflow: Option, mark_tables_dirty: Option, reduce_index: Option, mark_tables_clean: Option, loaded_dict_end: *mut c_uint, dict_match_state: *mut *const c_void, } const _: () = { assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_overflowCorrectState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_overflowCorrectState, next_to_update) == size_of::()); assert!(offset_of!(ZSTD_rust_overflowCorrectState, need_correction) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_overflowCorrectState, correct_overflow) == 3 * size_of::()); assert!( offset_of!(ZSTD_rust_overflowCorrectState, mark_tables_dirty) == 4 * size_of::() ); assert!(offset_of!(ZSTD_rust_overflowCorrectState, reduce_index) == 5 * size_of::()); assert!( offset_of!(ZSTD_rust_overflowCorrectState, mark_tables_clean) == 6 * size_of::() ); assert!(offset_of!(ZSTD_rust_overflowCorrectState, loaded_dict_end) == 7 * size_of::()); assert!( offset_of!(ZSTD_rust_overflowCorrectState, dict_match_state) == size_of::<[usize; 8]>() ); assert!(size_of::() == size_of::<[usize; 9]>()); }; /// Apply one overflow correction while keeping all private codec state in C. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_overflowCorrectIfNeeded( state: *const ZSTD_rust_overflowCorrectState, src: *const c_void, src_end: *const c_void, ) { if state.is_null() { return; } let state = unsafe { &*state }; if state.callback_context.is_null() || state.next_to_update.is_null() || state.loaded_dict_end.is_null() || state.dict_match_state.is_null() { return; } let ( Some(need_correction), Some(correct_overflow), Some(mark_tables_dirty), Some(reduce_index), Some(mark_tables_clean), ) = ( state.need_correction, state.correct_overflow, state.mark_tables_dirty, state.reduce_index, state.mark_tables_clean, ) else { return; }; unsafe { if need_correction(state.callback_context, src, src_end) == 0 { return; } let correction = correct_overflow(state.callback_context, src); mark_tables_dirty(state.callback_context); reduce_index(state.callback_context, correction); mark_tables_clean(state.callback_context); *state.next_to_update = (*state.next_to_update).saturating_sub(correction); *state.loaded_dict_end = 0; *state.dict_match_state = ptr::null(); } } #[inline] fn limit_next_to_update(curr: u32, next_to_update: u32) -> u32 { let const_gap = 384u32; let const_step = 192u32; if curr > next_to_update.wrapping_add(const_gap) { curr.wrapping_sub(const_step.min(curr.wrapping_sub(next_to_update).wrapping_sub(const_gap))) } else { next_to_update } } /// Limit a stale matchfinder update point after a very long match. /// /// C computes the source index against its private window base; Rust owns the /// wrapping U32 threshold and bounded catch-up arithmetic. #[no_mangle] pub extern "C" fn ZSTD_rust_limitNextToUpdate(curr: u32, next_to_update: u32) -> u32 { limit_next_to_update(curr, next_to_update) } #[inline] fn next_input_size_hint( in_buffer_mode: c_int, block_size_max: usize, stable_in_not_consumed: usize, in_buff_target: usize, in_buff_pos: usize, ) -> usize { if in_buffer_mode == ZSTD_BM_STABLE { return block_size_max.wrapping_sub(stable_in_not_consumed); } let hint_in_size = in_buff_target.wrapping_sub(in_buff_pos); if hint_in_size == 0 { block_size_max } else { hint_in_size } } /// Return the next input size required by the C streaming state machine. #[no_mangle] pub extern "C" fn ZSTD_rust_nextInputSizeHint( in_buffer_mode: c_int, block_size_max: usize, stable_in_not_consumed: usize, in_buff_target: usize, in_buff_pos: usize, ) -> usize { next_input_size_hint( in_buffer_mode, block_size_max, stable_in_not_consumed, in_buff_target, in_buff_pos, ) } #[inline] fn mt_next_input_size_hint(target_section_size: usize, in_buff_filled: usize) -> usize { let hint_in_size = target_section_size.wrapping_sub(in_buff_filled); if hint_in_size == 0 { target_section_size } else { hint_in_size } } /// Return the next input size required by the C multithreaded streaming state. #[no_mangle] pub extern "C" fn ZSTDMT_rust_nextInputSizeHint( target_section_size: usize, in_buff_filled: usize, ) -> usize { mt_next_input_size_hint(target_section_size, in_buff_filled) } #[inline] fn mt_sizeof_cctx( mtctx_size: usize, factory_size: usize, buffer_pool_size: usize, jobs_size: usize, cctx_pool_size: usize, seq_pool_size: usize, cdict_size: usize, round_buff_size: usize, ) -> usize { mtctx_size .wrapping_add(factory_size) .wrapping_add(buffer_pool_size) .wrapping_add(jobs_size) .wrapping_add(cctx_pool_size) .wrapping_add(seq_pool_size) .wrapping_add(cdict_size) .wrapping_add(round_buff_size) } /// Aggregate C-owned multithreaded context size components with C `size_t` /// wrapping semantics. #[no_mangle] pub extern "C" fn ZSTDMT_rust_sizeofCCtx( mtctx_size: usize, factory_size: usize, buffer_pool_size: usize, jobs_size: usize, cctx_pool_size: usize, seq_pool_size: usize, cdict_size: usize, round_buff_size: usize, ) -> usize { mt_sizeof_cctx( mtctx_size, factory_size, buffer_pool_size, jobs_size, cctx_pool_size, seq_pool_size, cdict_size, round_buff_size, ) } #[inline] fn bitmix(mut val: u64, len: u64) -> u64 { val ^= val.rotate_right(49) ^ val.rotate_right(24); val = val.wrapping_mul(0x9FB21C651E98DF25); val ^= (val >> 35).wrapping_add(len); val = val.wrapping_mul(0x9FB21C651E98DF25); val ^ (val >> 28) } #[inline] fn advance_hash_salt(hash_salt: u64, hash_salt_entropy: u64) -> u64 { bitmix(hash_salt, 8) ^ bitmix(hash_salt_entropy, 4) } /// Advance the row-matchfinder salt without exposing C's private match state. #[no_mangle] pub extern "C" fn ZSTD_rust_advanceHashSalt(hash_salt: u64, hash_salt_entropy: u64) -> u64 { advance_hash_salt(hash_salt, hash_salt_entropy) } /// Advance a C-owned row-matchfinder salt in place while C retains the /// private match-state layout. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_advanceHashSaltInPlace( hash_salt: *mut u64, hash_salt_entropy: u64, ) { debug_assert!(!hash_salt.is_null()); unsafe { *hash_salt = advance_hash_salt(*hash_salt, hash_salt_entropy); } } #[inline] fn index_too_close_to_max(next_src_base_offset: usize) -> bool { next_src_base_offset > ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN } /// Return whether a scalar C window offset is within the overflow margin. #[no_mangle] pub extern "C" fn ZSTD_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 } type ResetMatchStateCallback = unsafe extern "C" fn(*mut c_void); type ResetMatchStateSetPointer = unsafe extern "C" fn(*mut c_void, c_int, *mut c_void); type ResetMatchStateReserve = unsafe extern "C" fn(*mut c_void, c_int, usize) -> *mut c_void; type ResetMatchStateReserveFailed = unsafe extern "C" fn(*mut c_void) -> c_int; type ResetMatchStateZero = unsafe extern "C" fn(*mut c_void, *mut c_void, usize); const RESET_MATCH_RESERVE_TABLE: c_int = 0; const RESET_MATCH_RESERVE_ALIGNED_INIT_ONCE: c_int = 1; const RESET_MATCH_RESERVE_ALIGNED64: c_int = 2; const RESET_MATCH_POINTER_HASH_TABLE: c_int = 0; const RESET_MATCH_POINTER_CHAIN_TABLE: c_int = 1; const RESET_MATCH_POINTER_HASH_TABLE3: c_int = 2; const RESET_MATCH_POINTER_TAG_TABLE: c_int = 3; const RESET_MATCH_POINTER_LIT_FREQ: c_int = 4; const RESET_MATCH_POINTER_LIT_LENGTH_FREQ: c_int = 5; const RESET_MATCH_POINTER_MATCH_LENGTH_FREQ: c_int = 6; const RESET_MATCH_POINTER_OFF_CODE_FREQ: c_int = 7; const RESET_MATCH_POINTER_MATCH_TABLE: c_int = 8; const RESET_MATCH_POINTER_PRICE_TABLE: c_int = 9; /// Private C layout pointers and callback seams for match-state reset. /// /// Rust owns the reset policy and allocation order. C retains the workspace, /// window, and private match-state pointer representations behind callbacks. #[repr(C)] pub struct ZSTD_rust_resetMatchStateState { callback_context: *mut c_void, c_params: ZSTD_compressionParameters, dedicated_dict_search: c_int, use_row_match_finder: c_int, comp_reset_policy: c_int, index_reset_policy: c_int, reset_target: c_int, hash_log3_max: c_uint, lit_freq_size: usize, lit_length_freq_size: usize, match_length_freq_size: usize, off_code_freq_size: usize, match_size: usize, optimal_size: usize, hash_log3: *mut c_uint, row_hash_log: *mut c_uint, hash_salt: *mut u64, lazy_skipping: *mut c_int, c_params_out: *mut ZSTD_compressionParameters, set_pointer: Option, window_init: Option, mark_tables_dirty: Option, invalidate_match_state: Option, clear_tables: Option, clean_tables: Option, advance_hash_salt: Option, reserve: Option, reserve_failed: Option, zero: Option, } const _: () = { assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_resetMatchStateState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_resetMatchStateState, c_params) == size_of::()); assert!( offset_of!(ZSTD_rust_resetMatchStateState, lit_freq_size) > offset_of!(ZSTD_rust_resetMatchStateState, hash_log3_max) ); assert!( offset_of!(ZSTD_rust_resetMatchStateState, hash_log3) == offset_of!(ZSTD_rust_resetMatchStateState, optimal_size) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetMatchStateState, set_pointer) == offset_of!(ZSTD_rust_resetMatchStateState, c_params_out) + size_of::() ); assert!( size_of::() == offset_of!(ZSTD_rust_resetMatchStateState, zero) + size_of::() ); }; #[inline] fn reset_match_state_table_size(log: u32) -> Option { 1usize.checked_shl(log) } /// Reset and allocate one private match-state projection. /// /// The callback context deliberately remains opaque here. This keeps C's /// workspace phases and match-state layout out of the Rust ABI while making /// the policy/order independently testable. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_resetMatchState( state: *const ZSTD_rust_resetMatchStateState, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; let Some(set_pointer) = state.set_pointer else { return ERROR(ZstdErrorCode::Generic); }; let Some(window_init) = state.window_init else { return ERROR(ZstdErrorCode::Generic); }; let Some(mark_tables_dirty) = state.mark_tables_dirty else { return ERROR(ZstdErrorCode::Generic); }; let Some(invalidate_match_state) = state.invalidate_match_state else { return ERROR(ZstdErrorCode::Generic); }; let Some(clear_tables) = state.clear_tables else { return ERROR(ZstdErrorCode::Generic); }; let Some(clean_tables) = state.clean_tables else { return ERROR(ZstdErrorCode::Generic); }; let Some(advance_hash_salt) = state.advance_hash_salt else { return ERROR(ZstdErrorCode::Generic); }; let Some(reserve) = state.reserve else { return ERROR(ZstdErrorCode::Generic); }; let Some(reserve_failed) = state.reserve_failed else { return ERROR(ZstdErrorCode::Generic); }; let Some(zero) = state.zero else { return ERROR(ZstdErrorCode::Generic); }; if state.callback_context.is_null() || state.hash_log3.is_null() || state.row_hash_log.is_null() || state.hash_salt.is_null() || state.lazy_skipping.is_null() || state.c_params_out.is_null() { return ERROR(ZstdErrorCode::Generic); } if state.use_row_match_finder == ZSTD_RUST_PS_AUTO || !matches!(state.comp_reset_policy, 0..=1) || !matches!(state.index_reset_policy, 0..=1) || !matches!(state.reset_target, 0..=1) { return ERROR(ZstdErrorCode::Generic); } let chain_size = if ZSTD_rust_params_allocateChainTable( state.c_params.strategy, state.use_row_match_finder, c_int::from(state.dedicated_dict_search != 0 && state.reset_target == 0), ) != 0 { let Some(size) = reset_match_state_table_size(state.c_params.chainLog) else { return ERROR(ZstdErrorCode::Generic); }; size } else { 0 }; let Some(hash_size) = reset_match_state_table_size(state.c_params.hashLog) else { return ERROR(ZstdErrorCode::Generic); }; let hash_log3 = if state.reset_target == 1 && state.c_params.minMatch == 3 { state.hash_log3_max.min(state.c_params.windowLog) } else { 0 }; let hash3_size = if hash_log3 == 0 { 0 } else { let Some(size) = reset_match_state_table_size(hash_log3) else { return ERROR(ZstdErrorCode::Generic); }; size }; if state.index_reset_policy == 1 { unsafe { window_init(state.callback_context); mark_tables_dirty(state.callback_context); } } unsafe { *state.hash_log3 = hash_log3; *state.lazy_skipping = 0; invalidate_match_state(state.callback_context); debug_assert_eq!(reserve_failed(state.callback_context), 0); clear_tables(state.callback_context); } unsafe { set_pointer( state.callback_context, RESET_MATCH_POINTER_HASH_TABLE, reserve( state.callback_context, RESET_MATCH_RESERVE_TABLE, hash_size.wrapping_mul(size_of::()), ), ); set_pointer( state.callback_context, RESET_MATCH_POINTER_CHAIN_TABLE, reserve( state.callback_context, RESET_MATCH_RESERVE_TABLE, chain_size.wrapping_mul(size_of::()), ), ); set_pointer( state.callback_context, RESET_MATCH_POINTER_HASH_TABLE3, reserve( state.callback_context, RESET_MATCH_RESERVE_TABLE, hash3_size.wrapping_mul(size_of::()), ), ); } if unsafe { reserve_failed(state.callback_context) } != 0 { return ERROR(ZstdErrorCode::MemoryAllocation); } if state.comp_reset_policy != 1 { unsafe { clean_tables(state.callback_context) }; } if ZSTD_rust_params_rowMatchFinderUsed(state.c_params.strategy, state.use_row_match_finder) != 0 { if state.reset_target == 1 { let tag_table = unsafe { reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED_INIT_ONCE, hash_size, ) }; unsafe { set_pointer( state.callback_context, RESET_MATCH_POINTER_TAG_TABLE, tag_table, ); advance_hash_salt(state.callback_context); } } else { let tag_table = unsafe { reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED64, hash_size, ) }; unsafe { set_pointer( state.callback_context, RESET_MATCH_POINTER_TAG_TABLE, tag_table, ); } if unsafe { reserve_failed(state.callback_context) } != 0 { return ERROR(ZstdErrorCode::MemoryAllocation); } unsafe { zero(state.callback_context, tag_table, hash_size); *state.hash_salt = 0; } } if unsafe { reserve_failed(state.callback_context) } != 0 { return ERROR(ZstdErrorCode::MemoryAllocation); } let row_log = state.c_params.searchLog.clamp(4, 6); debug_assert!(state.c_params.hashLog >= row_log); unsafe { *state.row_hash_log = state.c_params.hashLog.wrapping_sub(row_log); } } if state.reset_target == 1 && state.c_params.strategy >= 7 { unsafe { set_pointer( state.callback_context, RESET_MATCH_POINTER_LIT_FREQ, reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED64, state.lit_freq_size, ), ); set_pointer( state.callback_context, RESET_MATCH_POINTER_LIT_LENGTH_FREQ, reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED64, state.lit_length_freq_size, ), ); set_pointer( state.callback_context, RESET_MATCH_POINTER_MATCH_LENGTH_FREQ, reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED64, state.match_length_freq_size, ), ); set_pointer( state.callback_context, RESET_MATCH_POINTER_OFF_CODE_FREQ, reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED64, state.off_code_freq_size, ), ); set_pointer( state.callback_context, RESET_MATCH_POINTER_MATCH_TABLE, reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED64, state.match_size, ), ); set_pointer( state.callback_context, RESET_MATCH_POINTER_PRICE_TABLE, reserve( state.callback_context, RESET_MATCH_RESERVE_ALIGNED64, state.optimal_size, ), ); } } unsafe { *state.c_params_out = state.c_params; } if unsafe { reserve_failed(state.callback_context) } != 0 { return ERROR(ZstdErrorCode::MemoryAllocation); } 0 } #[inline] fn sizeof_local_dict(dict_buffer_present: c_int, dict_size: usize, cdict_size: usize) -> usize { let buffer_size = if dict_buffer_present != 0 { dict_size } else { 0 }; buffer_size.wrapping_add(cdict_size) } /// Add the C-owned local-dictionary sizes with C `size_t` wrapping semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_sizeofLocalDict( dict_buffer_present: c_int, dict_size: usize, cdict_size: usize, ) -> usize { sizeof_local_dict(dict_buffer_present, dict_size, cdict_size) } #[inline] fn sizeof_cdict(object_size: usize, workspace_size: usize) -> usize { object_size.wrapping_add(workspace_size) } /// Aggregate C-owned dictionary size components with C `size_t` wrapping /// semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_sizeofCDict(objectSize: usize, workspaceSize: usize) -> usize { sizeof_cdict(objectSize, workspaceSize) } #[inline] fn sizeof_cctx( object_size: usize, workspace_size: usize, local_dict_size: usize, mtctx_size: usize, ) -> usize { object_size .wrapping_add(workspace_size) .wrapping_add(local_dict_size) .wrapping_add(mtctx_size) } /// Aggregate C-owned context size components with C `size_t` wrapping /// semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_sizeofCCtx( object_size: usize, workspace_size: usize, local_dict_size: usize, mtctx_size: usize, ) -> usize { sizeof_cctx(object_size, workspace_size, local_dict_size, mtctx_size) } #[inline] fn estimate_workspace_size( cctx_space: usize, tmp_work_space: usize, block_state_space: usize, ldm_space: usize, ldm_seq_space: usize, match_state_size: usize, token_space: usize, buffer_space: usize, external_seq_space: usize, ) -> usize { cctx_space .wrapping_add(tmp_work_space) .wrapping_add(block_state_space) .wrapping_add(ldm_space) .wrapping_add(ldm_seq_space) .wrapping_add(match_state_size) .wrapping_add(token_space) .wrapping_add(buffer_space) .wrapping_add(external_seq_space) } /// Aggregate workspace-size components with C `size_t` wrapping semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_estimateWorkspaceSize( cctx_space: usize, tmp_work_space: usize, block_state_space: usize, ldm_space: usize, ldm_seq_space: usize, match_state_size: usize, token_space: usize, buffer_space: usize, external_seq_space: usize, ) -> usize { estimate_workspace_size( cctx_space, tmp_work_space, block_state_space, ldm_space, ldm_seq_space, match_state_size, token_space, buffer_space, external_seq_space, ) } /// Private C layout sizes needed by the CCtx workspace estimator. /// /// The scalar sizes keep `ZSTD_CCtx`, `ldmEntry_t`, and the other private C /// objects opaque while allowing Rust to own the workspace-sizing formula. #[repr(C)] #[derive(Clone, Copy, Debug, Default)] pub struct ZSTD_rustCCtxWorkspaceSizing { pub cctxSize: usize, pub compressedBlockStateSize: usize, pub seqDefSize: usize, pub rawSeqSize: usize, pub externalSequenceSize: usize, pub tmpWorkspaceSize: usize, pub wildcopyOverlength: usize, pub matchTSize: usize, pub optimalTSize: usize, pub asanRedzoneSize: usize, } const _: () = { assert!(size_of::() == size_of::<[usize; 10]>()); }; const ZSTD_HASHLOG3_MAX: u32 = 17; #[inline] fn cctx_cwksp_alloc_size(size: usize, asan_redzone_size: usize) -> usize { if size == 0 { 0 } else { size.wrapping_add(asan_redzone_size.wrapping_mul(2)) } } #[inline] fn cctx_cwksp_align(size: usize, alignment: usize) -> usize { size.wrapping_add(alignment - 1) & !(alignment - 1) } #[inline] fn cctx_cwksp_aligned64_alloc_size(size: usize, asan_redzone_size: usize) -> usize { cctx_cwksp_alloc_size(cctx_cwksp_align(size, 64), asan_redzone_size) } #[inline] fn cctx_ldm_table_size( ldm_enable: c_int, hash_log: u32, bucket_size_log: u32, asan_redzone_size: usize, ) -> usize { if ldm_enable != ZSTD_RUST_PS_ENABLE { return 0; } let hash_size = 1usize << hash_log; let bucket_log = bucket_size_log.min(hash_log); let bucket_size = 1usize << (hash_log - bucket_log); cctx_cwksp_alloc_size(bucket_size, asan_redzone_size).wrapping_add(cctx_cwksp_alloc_size( hash_size.wrapping_mul(2 * size_of::()), asan_redzone_size, )) } /// Estimate the CCtx workspace using the private C layout sizes supplied by /// the adapter. This owns the orchestration formerly kept in /// `ZSTD_estimateCCtxSize_usingCCtxParams_internal()`. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_estimateCCtxWorkspaceSize( cparams: ZSTD_compressionParameters, ldm_enable: c_int, ldm_hash_log: u32, ldm_bucket_size_log: u32, ldm_min_match_length: u32, is_static: c_int, use_row_match_finder: c_int, buff_in_size: usize, buff_out_size: usize, pledged_src_size: u64, use_sequence_producer: c_int, max_block_size: usize, sizing: *const ZSTD_rustCCtxWorkspaceSizing, ) -> usize { if sizing.is_null() { return 0; } let sizing = unsafe { *sizing }; let window_limit = 1u64.checked_shl(cparams.windowLog).unwrap_or(u64::MAX); let window_size = (window_limit.min(pledged_src_size).min(usize::MAX as u64) as usize).max(1); let block_size = ZSTD_rust_params_resolveMaxBlockSize(max_block_size).min(window_size); let max_nb_seq = ZSTD_rust_params_maxNbSeq(block_size, cparams.minMatch, use_sequence_producer); let token_space = cctx_cwksp_alloc_size( sizing.wildcopyOverlength.wrapping_add(block_size), sizing.asanRedzoneSize, ) .wrapping_add(cctx_cwksp_aligned64_alloc_size( max_nb_seq.wrapping_mul(sizing.seqDefSize), sizing.asanRedzoneSize, )) .wrapping_add(3usize.wrapping_mul(cctx_cwksp_alloc_size( max_nb_seq.wrapping_mul(size_of::()), sizing.asanRedzoneSize, ))); let tmp_workspace = cctx_cwksp_alloc_size(sizing.tmpWorkspaceSize, sizing.asanRedzoneSize); let block_state_space = 2usize.wrapping_mul(cctx_cwksp_alloc_size( sizing.compressedBlockStateSize, sizing.asanRedzoneSize, )); let match_state_sizing = ZSTD_rustMatchStateSizing { hashLog3Max: ZSTD_HASHLOG3_MAX, matchTSize: sizing.matchTSize, optimalTSize: sizing.optimalTSize, asanRedzoneSize: sizing.asanRedzoneSize, }; let match_state_size = unsafe { ZSTD_rust_params_estimateMatchStateSize( cparams, use_row_match_finder, 0, 1, &match_state_sizing, ) }; let ldm_space = cctx_ldm_table_size( ldm_enable, ldm_hash_log, ldm_bucket_size_log, sizing.asanRedzoneSize, ); let max_nb_ldm_seq = if ldm_enable == ZSTD_RUST_PS_ENABLE && ldm_min_match_length != 0 { block_size / ldm_min_match_length as usize } else { 0 }; let ldm_seq_space = if ldm_enable == ZSTD_RUST_PS_ENABLE { cctx_cwksp_aligned64_alloc_size( max_nb_ldm_seq.wrapping_mul(sizing.rawSeqSize), sizing.asanRedzoneSize, ) } else { 0 }; let buffer_space = cctx_cwksp_alloc_size(buff_in_size, sizing.asanRedzoneSize) .wrapping_add(cctx_cwksp_alloc_size(buff_out_size, sizing.asanRedzoneSize)); let cctx_space = if is_static != 0 { cctx_cwksp_alloc_size(sizing.cctxSize, sizing.asanRedzoneSize) } else { 0 }; let external_seq_space = if use_sequence_producer != 0 { cctx_cwksp_aligned64_alloc_size( crate::zstd_compress_api::ZSTD_sequenceBound(block_size) .wrapping_mul(sizing.externalSequenceSize), sizing.asanRedzoneSize, ) } else { 0 }; estimate_workspace_size( cctx_space, tmp_workspace, block_state_space, ldm_space, ldm_seq_space, match_state_size, token_space, buffer_space, external_seq_space, ) } /// Scalar outputs computed while resetting a private C compression context. /// /// C still owns workspace resizing and all private reservations. Rust owns /// this policy projection so the reset formula is testable without exposing /// `ZSTD_CCtx` or `ZSTD_cwksp` layouts across the ABI. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ZSTD_rustCCtxResetPlan { pub windowSize: usize, pub blockSize: usize, pub maxNbSeq: usize, pub buffInSize: usize, pub buffOutSize: usize, pub maxNbLdmSeq: usize, pub maxNbExternalSeq: usize, pub neededSpace: usize, pub needsIndexReset: c_int, } /// Scalar inputs needed to plan a private C compression-context reset. /// /// The C adapter supplies resolved parameters, private object sizes, and the /// already-reduced index/dictionary predicates. No private C pointer or /// layout is passed through this record. #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct ZSTD_rustCCtxResetState { pub cParams: ZSTD_compressionParameters, pub ldmEnable: c_int, pub ldmHashLog: c_uint, pub ldmBucketSizeLog: c_uint, pub ldmMinMatchLength: c_uint, pub isStatic: c_int, pub useRowMatchFinder: c_int, pub inBufferBuffered: c_int, pub outBufferBuffered: c_int, pub useSequenceProducer: c_int, pub initialized: c_int, pub indexTooClose: c_int, pub dictTooBig: c_int, pub pledgedSrcSize: u64, pub maxBlockSize: usize, pub sizing: *const ZSTD_rustCCtxWorkspaceSizing, pub plan: *mut ZSTD_rustCCtxResetPlan, } const _: () = { assert!(size_of::() == 9 * size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, windowSize) == 0); assert!(offset_of!(ZSTD_rustCCtxResetPlan, blockSize) == size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, maxNbSeq) == 2 * size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, buffInSize) == 3 * size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, buffOutSize) == 4 * size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, maxNbLdmSeq) == 5 * size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, maxNbExternalSeq) == 6 * size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, neededSpace) == 7 * size_of::()); assert!(offset_of!(ZSTD_rustCCtxResetPlan, needsIndexReset) == size_of::<[usize; 8]>()); assert!(offset_of!(ZSTD_rustCCtxResetState, cParams) == 0); assert!( offset_of!(ZSTD_rustCCtxResetState, ldmEnable) == size_of::() ); assert!( offset_of!(ZSTD_rustCCtxResetState, pledgedSrcSize) > offset_of!(ZSTD_rustCCtxResetState, dictTooBig) ); assert!( offset_of!(ZSTD_rustCCtxResetState, maxBlockSize) == offset_of!(ZSTD_rustCCtxResetState, pledgedSrcSize) + size_of::() ); assert!( offset_of!(ZSTD_rustCCtxResetState, sizing) == offset_of!(ZSTD_rustCCtxResetState, maxBlockSize) + size_of::() ); assert!( offset_of!(ZSTD_rustCCtxResetState, plan) == offset_of!(ZSTD_rustCCtxResetState, sizing) + size_of::() ); assert!( size_of::() == offset_of!(ZSTD_rustCCtxResetState, plan) + size_of::() ); }; /// Compute the scalar portion of `ZSTD_resetCCtx_internal()`. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_planCCtxReset(state: *const ZSTD_rustCCtxResetState) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; if state.sizing.is_null() || state.plan.is_null() { return ERROR(ZstdErrorCode::Generic); } let ldm_enabled = state.ldmEnable == ZSTD_RUST_PS_ENABLE; if ldm_enabled && state.ldmMinMatchLength == 0 { return ERROR(ZstdErrorCode::Generic); } let window_limit = 1u64 .checked_shl(state.cParams.windowLog) .unwrap_or(u64::MAX); let window_size = (window_limit .min(state.pledgedSrcSize) .min(usize::MAX as u64) as usize) .max(1); let block_size = state.maxBlockSize.min(window_size); let max_nb_seq = ZSTD_rust_params_maxNbSeq( block_size, state.cParams.minMatch, state.useSequenceProducer, ); let buff_out_size = if state.outBufferBuffered != 0 { crate::zstd_compress_api::ZSTD_compressBound(block_size).wrapping_add(1) } else { 0 }; let buff_in_size = if state.inBufferBuffered != 0 { window_size.wrapping_add(block_size) } else { 0 }; let max_nb_ldm_seq = if ldm_enabled { block_size / state.ldmMinMatchLength as usize } else { 0 }; let max_nb_external_seq = if state.useSequenceProducer != 0 { crate::zstd_compress_api::ZSTD_sequenceBound(block_size) } else { 0 }; let needed_space = unsafe { ZSTD_rust_estimateCCtxWorkspaceSize( state.cParams, state.ldmEnable, state.ldmHashLog, state.ldmBucketSizeLog, state.ldmMinMatchLength, state.isStatic, state.useRowMatchFinder, buff_in_size, buff_out_size, state.pledgedSrcSize, state.useSequenceProducer, state.maxBlockSize, state.sizing, ) }; if ERR_isError(needed_space) { return needed_space; } unsafe { *state.plan = ZSTD_rustCCtxResetPlan { windowSize: window_size, blockSize: block_size, maxNbSeq: max_nb_seq, buffInSize: buff_in_size, buffOutSize: buff_out_size, maxNbLdmSeq: max_nb_ldm_seq, maxNbExternalSeq: max_nb_external_seq, neededSpace: needed_space, needsIndexReset: c_int::from( state.indexTooClose != 0 || state.dictTooBig != 0 || state.initialized == 0, ), }; } 0 } type ResetCCtxStorageSetPointer = unsafe extern "C" fn(*mut c_void, c_int, *mut c_void); type ResetCCtxStorageSetSize = unsafe extern "C" fn(*mut c_void, c_int, usize); type ResetCCtxStorageSetInt = unsafe extern "C" fn(*mut c_void, c_int, c_int); type ResetCCtxStorageReserve = unsafe extern "C" fn(*mut c_void, c_int, usize) -> *mut c_void; type ResetCCtxStorageReserveFailed = unsafe extern "C" fn(*mut c_void) -> c_int; type ResetCCtxStorageZero = unsafe extern "C" fn(*mut c_void, *mut c_void, usize); type ResetCCtxStorageCallback = unsafe extern "C" fn(*mut c_void); const RESET_CCTX_RESERVE_ALIGNED64: c_int = 0; const RESET_CCTX_RESERVE_BUFFER: c_int = 1; const RESET_CCTX_POINTER_SEQ_START: c_int = 0; const RESET_CCTX_POINTER_LDM_HASH: c_int = 1; const RESET_CCTX_POINTER_LDM_SEQUENCES: c_int = 2; const RESET_CCTX_POINTER_EXTERNAL_SEQUENCES: c_int = 3; const RESET_CCTX_POINTER_LITERALS: c_int = 4; const RESET_CCTX_POINTER_INPUT_BUFFER: c_int = 5; const RESET_CCTX_POINTER_OUTPUT_BUFFER: c_int = 6; const RESET_CCTX_POINTER_LL_CODE: c_int = 7; const RESET_CCTX_POINTER_ML_CODE: c_int = 8; const RESET_CCTX_POINTER_OF_CODE: c_int = 9; const RESET_CCTX_POINTER_LDM_BUCKETS: c_int = 10; const RESET_CCTX_POINTER_PREV_CBLOCK: c_int = 11; const RESET_CCTX_POINTER_NEXT_CBLOCK: c_int = 12; const RESET_CCTX_POINTER_TMP_WORKSPACE: c_int = 13; const RESET_CCTX_SIZE_MAX_NB_SEQ: c_int = 0; const RESET_CCTX_SIZE_MAX_NB_LIT: c_int = 1; const RESET_CCTX_SIZE_MAX_NB_LDM_SEQ: c_int = 2; const RESET_CCTX_SIZE_EXTERNAL_SEQ_CAPACITY: c_int = 3; const RESET_CCTX_SIZE_INPUT_BUFFER: c_int = 4; const RESET_CCTX_SIZE_OUTPUT_BUFFER: c_int = 5; const RESET_CCTX_SIZE_TMP_WORKSPACE: c_int = 6; const RESET_CCTX_INT_BUFFERED_POLICY: c_int = 0; const RESET_CCTX_INT_INITIALIZED: c_int = 1; const RESET_CCTX_INDEX_RESET: c_int = 1; /// C-owned workspace/layout projection for the post-match-state reset tail. /// /// Rust owns the reservation order and size policy. C retains the private /// `ZSTD_CCtx`/`ldmState_t` layouts and implements the workspace callbacks. #[repr(C)] pub struct ZSTD_rust_resetCCtxStorageState { pub callbackContext: *mut c_void, pub ldmEnable: c_int, pub hasExtSeqProd: c_int, pub hashLog: c_uint, pub bucketSizeLog: c_uint, pub blockSize: usize, pub maxNbSeq: usize, pub maxNbLdmSeq: usize, pub maxNbExternalSeq: usize, pub buffInSize: usize, pub buffOutSize: usize, pub seqDefSize: usize, pub ldmEntrySize: usize, pub rawSeqSize: usize, pub externalSequenceSize: usize, pub byteSize: usize, pub wildcopyOverlength: usize, pub bufferedPolicy: c_int, pub setPointer: Option, pub setSize: Option, pub setInt: Option, pub reserve: Option, pub reserveFailed: Option, pub zero: Option, pub windowInit: Option, pub resetExternalSequences: Option, } const _: () = { assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_resetCCtxStorageState, callbackContext) == 0); assert!(offset_of!(ZSTD_rust_resetCCtxStorageState, ldmEnable) == size_of::()); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, hasExtSeqProd) == size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, hashLog) > offset_of!(ZSTD_rust_resetCCtxStorageState, hasExtSeqProd) ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, bucketSizeLog) == offset_of!(ZSTD_rust_resetCCtxStorageState, hashLog) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, blockSize) > offset_of!(ZSTD_rust_resetCCtxStorageState, bucketSizeLog) ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbSeq) == offset_of!(ZSTD_rust_resetCCtxStorageState, blockSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbLdmSeq) == offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbSeq) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbExternalSeq) == offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbLdmSeq) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, buffInSize) == offset_of!(ZSTD_rust_resetCCtxStorageState, maxNbExternalSeq) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, buffOutSize) == offset_of!(ZSTD_rust_resetCCtxStorageState, buffInSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, seqDefSize) == offset_of!(ZSTD_rust_resetCCtxStorageState, buffOutSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, ldmEntrySize) == offset_of!(ZSTD_rust_resetCCtxStorageState, seqDefSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, rawSeqSize) == offset_of!(ZSTD_rust_resetCCtxStorageState, ldmEntrySize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, externalSequenceSize) == offset_of!(ZSTD_rust_resetCCtxStorageState, rawSeqSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, byteSize) == offset_of!(ZSTD_rust_resetCCtxStorageState, externalSequenceSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, wildcopyOverlength) == offset_of!(ZSTD_rust_resetCCtxStorageState, byteSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, bufferedPolicy) > offset_of!(ZSTD_rust_resetCCtxStorageState, wildcopyOverlength) ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, setSize) == offset_of!(ZSTD_rust_resetCCtxStorageState, setPointer) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, setInt) == offset_of!(ZSTD_rust_resetCCtxStorageState, setSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxStorageState, reserve) == offset_of!(ZSTD_rust_resetCCtxStorageState, setInt) + size_of::() ); assert!( size_of::() == offset_of!(ZSTD_rust_resetCCtxStorageState, resetExternalSequences) + size_of::() ); }; type ResetCCtxWorkspaceCreate = unsafe extern "C" fn(*mut c_void, usize) -> usize; type ResetCCtxWorkspaceReserveObject = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void; type ResetCCtxTailCallback = unsafe extern "C" fn(*mut c_void) -> usize; /// C-owned workspace operations used by the CCtx reset policy. /// /// Rust owns the resize decision, error ordering, object-reservation order, /// and workspace-clear boundary. C retains the workspace representation, /// allocator, and private CCtx publication callbacks. #[repr(C)] pub struct ZSTD_rust_resetCCtxWorkspaceState { callbackContext: *mut c_void, isStatic: c_int, workspaceTooSmall: c_int, workspaceWasteful: c_int, neededSpace: usize, compressedBlockStateSize: usize, tmpWorkspaceSize: usize, needsIndexReset: *mut c_int, bumpOversizedDuration: Option, freeWorkspace: Option, createWorkspace: Option, reserveObject: Option, setPointer: Option, setSize: Option, clearWorkspace: Option, } const _: () = { assert!(offset_of!(ZSTD_rust_resetCCtxWorkspaceState, callbackContext) == 0); assert!(offset_of!(ZSTD_rust_resetCCtxWorkspaceState, isStatic) == size_of::()); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, workspaceTooSmall) == size_of::() + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, workspaceWasteful) == size_of::() + 2 * size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, neededSpace) > offset_of!(ZSTD_rust_resetCCtxWorkspaceState, workspaceWasteful) ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, compressedBlockStateSize) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, neededSpace) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, tmpWorkspaceSize) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, compressedBlockStateSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, needsIndexReset) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, tmpWorkspaceSize) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, freeWorkspace) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, bumpOversizedDuration) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, createWorkspace) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, freeWorkspace) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, reserveObject) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, createWorkspace) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, setPointer) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, reserveObject) + size_of::() ); assert!( offset_of!(ZSTD_rust_resetCCtxWorkspaceState, setSize) == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, setPointer) + size_of::() ); assert!( size_of::() == offset_of!(ZSTD_rust_resetCCtxWorkspaceState, clearWorkspace) + size_of::() ); }; /// C-owned operations used by the post-workspace CCtx reset tail. /// /// Rust owns the operation order, compressed-block reset, and error /// propagation. C retains the private context initialization, match-state /// reset, and storage publication operations. #[repr(C)] pub struct ZSTD_rust_resetCCtxTailState { callbackContext: *mut c_void, initialize: Option, compressedBlockState: *mut ZSTD_compressedBlockState_t, resetMatchState: Option, resetStorage: Option, } const _: () = { assert!(offset_of!(ZSTD_rust_resetCCtxTailState, callbackContext) == 0); assert!(offset_of!(ZSTD_rust_resetCCtxTailState, initialize) == size_of::()); assert!( offset_of!(ZSTD_rust_resetCCtxTailState, compressedBlockState) == 2 * size_of::() ); assert!(offset_of!(ZSTD_rust_resetCCtxTailState, resetMatchState) == 3 * size_of::()); assert!( size_of::() == offset_of!(ZSTD_rust_resetCCtxTailState, resetStorage) + size_of::() ); }; /// Reserve and publish the private CCtx storage that follows match-state reset. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_resetCCtxStorage( state: *const ZSTD_rust_resetCCtxStorageState, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; if state.callbackContext.is_null() { return ERROR(ZstdErrorCode::Generic); } let Some(set_pointer) = state.setPointer else { return ERROR(ZstdErrorCode::Generic); }; let Some(set_size) = state.setSize else { return ERROR(ZstdErrorCode::Generic); }; let Some(set_int) = state.setInt else { return ERROR(ZstdErrorCode::Generic); }; let Some(reserve_callback) = state.reserve else { return ERROR(ZstdErrorCode::Generic); }; let Some(reserve_failed) = state.reserveFailed else { return ERROR(ZstdErrorCode::Generic); }; let Some(zero) = state.zero else { return ERROR(ZstdErrorCode::Generic); }; let Some(window_init) = state.windowInit else { return ERROR(ZstdErrorCode::Generic); }; let Some(reset_external_sequences) = state.resetExternalSequences else { return ERROR(ZstdErrorCode::Generic); }; let reserve = |kind: c_int, size: usize| -> Result<*mut c_void, usize> { let pointer = unsafe { reserve_callback(state.callbackContext, kind, size) }; if unsafe { reserve_failed(state.callbackContext) } != 0 { Err(ERROR(ZstdErrorCode::MemoryAllocation)) } else { Ok(pointer) } }; let publish_pointer = |kind: c_int, pointer: *mut c_void| unsafe { set_pointer(state.callbackContext, kind, pointer) }; let publish_size = |kind: c_int, value: usize| unsafe { set_size(state.callbackContext, kind, value) }; let sequence_start = match reserve( RESET_CCTX_RESERVE_ALIGNED64, state.maxNbSeq.wrapping_mul(state.seqDefSize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_SEQ_START, sequence_start); if state.ldmEnable == ZSTD_RUST_PS_ENABLE { if state.bucketSizeLog > state.hashLog { return ERROR(ZstdErrorCode::Generic); } let Some(hash_size) = 1usize.checked_shl(state.hashLog) else { return ERROR(ZstdErrorCode::Generic); }; let hash_table = match reserve( RESET_CCTX_RESERVE_ALIGNED64, hash_size.wrapping_mul(state.ldmEntrySize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_LDM_HASH, hash_table); unsafe { zero( state.callbackContext, hash_table, hash_size.wrapping_mul(state.ldmEntrySize), ) }; let ldm_sequences = match reserve( RESET_CCTX_RESERVE_ALIGNED64, state.maxNbLdmSeq.wrapping_mul(state.rawSeqSize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_LDM_SEQUENCES, ldm_sequences); publish_size(RESET_CCTX_SIZE_MAX_NB_LDM_SEQ, state.maxNbLdmSeq); unsafe { window_init(state.callbackContext) }; } if state.hasExtSeqProd != 0 { let external_sequences = match reserve( RESET_CCTX_RESERVE_ALIGNED64, state .maxNbExternalSeq .wrapping_mul(state.externalSequenceSize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_EXTERNAL_SEQUENCES, external_sequences); publish_size( RESET_CCTX_SIZE_EXTERNAL_SEQ_CAPACITY, state.maxNbExternalSeq, ); } let literals = match reserve( RESET_CCTX_RESERVE_BUFFER, state.blockSize.wrapping_add(state.wildcopyOverlength), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_LITERALS, literals); publish_size(RESET_CCTX_SIZE_MAX_NB_LIT, state.blockSize); unsafe { set_int( state.callbackContext, RESET_CCTX_INT_BUFFERED_POLICY, state.bufferedPolicy, ) }; let input_buffer = match reserve(RESET_CCTX_RESERVE_BUFFER, state.buffInSize) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_INPUT_BUFFER, input_buffer); publish_size(RESET_CCTX_SIZE_INPUT_BUFFER, state.buffInSize); let output_buffer = match reserve(RESET_CCTX_RESERVE_BUFFER, state.buffOutSize) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_OUTPUT_BUFFER, output_buffer); publish_size(RESET_CCTX_SIZE_OUTPUT_BUFFER, state.buffOutSize); if state.ldmEnable == ZSTD_RUST_PS_ENABLE { let bucket_count = match 1usize.checked_shl(state.hashLog - state.bucketSizeLog) { Some(count) => count, None => return ERROR(ZstdErrorCode::Generic), }; let bucket_offsets = match reserve( RESET_CCTX_RESERVE_BUFFER, bucket_count.wrapping_mul(state.byteSize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_LDM_BUCKETS, bucket_offsets); unsafe { zero( state.callbackContext, bucket_offsets, bucket_count.wrapping_mul(state.byteSize), ) }; } unsafe { reset_external_sequences(state.callbackContext) }; publish_size(RESET_CCTX_SIZE_MAX_NB_SEQ, state.maxNbSeq); let ll_code = match reserve( RESET_CCTX_RESERVE_BUFFER, state.maxNbSeq.wrapping_mul(state.byteSize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_LL_CODE, ll_code); let ml_code = match reserve( RESET_CCTX_RESERVE_BUFFER, state.maxNbSeq.wrapping_mul(state.byteSize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_ML_CODE, ml_code); let of_code = match reserve( RESET_CCTX_RESERVE_BUFFER, state.maxNbSeq.wrapping_mul(state.byteSize), ) { Ok(pointer) => pointer, Err(error) => return error, }; publish_pointer(RESET_CCTX_POINTER_OF_CODE, of_code); unsafe { set_int(state.callbackContext, RESET_CCTX_INT_INITIALIZED, 1) }; 0 } /// Own the CCtx workspace resize, object reservation, and clear order. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_resetCCtxWorkspace( state: *const ZSTD_rust_resetCCtxWorkspaceState, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; if state.callbackContext.is_null() || state.needsIndexReset.is_null() { return ERROR(ZstdErrorCode::Generic); } let Some(bump_oversized_duration) = state.bumpOversizedDuration else { return ERROR(ZstdErrorCode::Generic); }; let Some(free_workspace) = state.freeWorkspace else { return ERROR(ZstdErrorCode::Generic); }; let Some(create_workspace) = state.createWorkspace else { return ERROR(ZstdErrorCode::Generic); }; let Some(reserve_object) = state.reserveObject else { return ERROR(ZstdErrorCode::Generic); }; let Some(set_pointer) = state.setPointer else { return ERROR(ZstdErrorCode::Generic); }; let Some(set_size) = state.setSize else { return ERROR(ZstdErrorCode::Generic); }; let Some(clear_workspace) = state.clearWorkspace else { return ERROR(ZstdErrorCode::Generic); }; unsafe { if state.isStatic == 0 { bump_oversized_duration(state.callbackContext); } let resize_workspace = state.workspaceTooSmall != 0 || state.workspaceWasteful != 0; if resize_workspace { if state.isStatic != 0 { return ERROR(ZstdErrorCode::MemoryAllocation); } *state.needsIndexReset = RESET_CCTX_INDEX_RESET; free_workspace(state.callbackContext); let result = create_workspace(state.callbackContext, state.neededSpace); if ERR_isError(result) { return result; } let previous = reserve_object(state.callbackContext, state.compressedBlockStateSize); if previous.is_null() { return ERROR(ZstdErrorCode::MemoryAllocation); } set_pointer( state.callbackContext, RESET_CCTX_POINTER_PREV_CBLOCK, previous, ); let next = reserve_object(state.callbackContext, state.compressedBlockStateSize); if next.is_null() { return ERROR(ZstdErrorCode::MemoryAllocation); } set_pointer(state.callbackContext, RESET_CCTX_POINTER_NEXT_CBLOCK, next); let tmp_workspace = reserve_object(state.callbackContext, state.tmpWorkspaceSize); if tmp_workspace.is_null() { return ERROR(ZstdErrorCode::MemoryAllocation); } set_pointer( state.callbackContext, RESET_CCTX_POINTER_TMP_WORKSPACE, tmp_workspace, ); set_size( state.callbackContext, RESET_CCTX_SIZE_TMP_WORKSPACE, state.tmpWorkspaceSize, ); } clear_workspace(state.callbackContext); } 0 } /// Own the callback order for the post-workspace CCtx reset tail. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_resetCCtxTail( state: *const ZSTD_rust_resetCCtxTailState, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; if state.callbackContext.is_null() { return ERROR(ZstdErrorCode::Generic); } let Some(initialize) = state.initialize else { return ERROR(ZstdErrorCode::Generic); }; let Some(reset_match_state) = state.resetMatchState else { return ERROR(ZstdErrorCode::Generic); }; let Some(reset_storage) = state.resetStorage else { return ERROR(ZstdErrorCode::Generic); }; if state.compressedBlockState.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { initialize(state.callbackContext); ZSTD_rust_resetCompressedBlockState(state.compressedBlockState); let result = reset_match_state(state.callbackContext); if ERR_isError(result) { return result; } let result = reset_storage(state.callbackContext); if ERR_isError(result) { return result; } } 0 } #[inline] fn max_estimate_cctx_size( estimate0: usize, estimate1: usize, estimate2: usize, estimate3: usize, ) -> usize { estimate0.max(estimate1).max(estimate2).max(estimate3) } /// Return the largest raw estimate, including any C size_t error values. #[no_mangle] pub extern "C" fn ZSTD_rust_maxEstimateCCtxSize( estimate0: usize, estimate1: usize, estimate2: usize, estimate3: usize, ) -> usize { max_estimate_cctx_size(estimate0, estimate1, estimate2, estimate3) } #[inline] fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) { debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0); debug_assert!(table.len() < (1usize << 31)); /* Protect special index values < ZSTD_WINDOW_START_INDEX. */ let reducer_threshold = reducer_value.wrapping_add(ZSTD_WINDOW_START_INDEX); let mut rows = table.chunks_exact_mut(ZSTD_ROWSIZE); for row in &mut rows { for cell in row { let value = *cell; *cell = if preserve_mark && value == ZSTD_DUBT_UNSORTED_MARK { /* Keep the btlazy2 unsorted marker across table reduction. */ ZSTD_DUBT_UNSORTED_MARK } else if value < reducer_threshold { 0 } else { value.wrapping_sub(reducer_value) }; } } debug_assert!(rows.into_remainder().is_empty()); } /// Rust implementation of the C match-table reduction leaf. /// /// The C caller supplies a clear zero/one `preserve_mark` value for the /// strategy-specific btlazy2 policy. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_reduceTable( table: *mut u32, size: u32, reducer_value: u32, preserve_mark: c_int, ) { debug_assert!(!table.is_null() || size == 0); debug_assert_eq!(size % ZSTD_ROWSIZE as u32, 0); debug_assert!(size < (1u32 << 31)); if size == 0 { return; } let table = unsafe { std::slice::from_raw_parts_mut(table, size as usize) }; reduce_table_internal(table, reducer_value, preserve_mark != 0); } /// Reduce the match tables selected by C's stateful overflow-correction path. /// /// C retains match-state access and table selection. Each non-zero size is /// paired with a validated mutable table pointer; zero-sized optional tables /// may use null pointers. Only the chain table receives the btlazy2 marker /// policy. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_reduceIndex( hash_table: *mut u32, hash_size: u32, chain_table: *mut u32, chain_size: u32, hash_table3: *mut u32, hash_size3: u32, reducer_value: u32, preserve_chain_mark: c_int, ) { debug_assert!(!hash_table.is_null() || hash_size == 0); debug_assert!(!chain_table.is_null() || chain_size == 0); debug_assert!(!hash_table3.is_null() || hash_size3 == 0); debug_assert!(preserve_chain_mark == 0 || preserve_chain_mark == 1); unsafe { ZSTD_rust_reduceTable(hash_table, hash_size, reducer_value, 0); ZSTD_rust_reduceTable(chain_table, chain_size, reducer_value, preserve_chain_mark); ZSTD_rust_reduceTable(hash_table3, hash_size3, reducer_value, 0); } } /// Select the match tables participating in overflow correction and delegate /// their cell updates to the Rust reducer. C supplies private table pointers; /// Rust owns the strategy, row-matchfinder, dedicated-dictionary, and marker /// policy that determines the participating table set. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_reduceIndexForMatchState( hash_table: *mut u32, hash_log: c_uint, chain_table: *mut u32, chain_log: c_uint, hash_table3: *mut u32, hash_log3: c_uint, strategy: c_int, use_row_match_finder: c_int, dedicated_dict_search: c_int, reducer_value: c_uint, ) { let hash_size = 1u32.wrapping_shl(hash_log); let (chain_table, chain_size) = if ZSTD_rust_params_allocateChainTable( strategy, use_row_match_finder, dedicated_dict_search, ) != 0 { (chain_table, 1u32.wrapping_shl(chain_log)) } else { (ptr::null_mut(), 0) }; let (hash_table3, hash_size3) = if hash_log3 != 0 { (hash_table3, 1u32.wrapping_shl(hash_log3)) } else { (ptr::null_mut(), 0) }; unsafe { ZSTD_rust_reduceIndex( hash_table, hash_size, chain_table, chain_size, hash_table3, hash_size3, reducer_value, c_int::from(strategy == ZSTD_BTLAZY2), ); } } /// Copies a CDict match table into a CCtx, removing short-cache tags when the /// C-owned compression parameters say the source table is tagged. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_copyCDictTableIntoCCtx( dst: *mut u32, src: *const u32, table_size: usize, tagged: c_int, ) { if tagged != 0 { for i in 0..table_size { let index = unsafe { *src.add(i) } >> ZSTD_SHORT_CACHE_TAG_BITS; unsafe { *dst.add(i) = index }; } } else if table_size != 0 { unsafe { ptr::copy_nonoverlapping(src, dst, table_size) }; } } /// Clear the previous block's repcodes before the next regular compression. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_invalidateRepCodes(rep: *mut u32) { debug_assert!(!rep.is_null()); let rep = unsafe { std::slice::from_raw_parts_mut(rep, ZSTD_REP_NUM) }; rep.fill(0); } /// Clear a C-owned compression window using the original `size_t`-to-`U32` /// conversion. The caller computes the pointer difference while the private /// `ZSTD_window_t` layout remains entirely on the C side. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_windowClear( end_t: usize, low_limit: *mut u32, dict_limit: *mut u32, ) { debug_assert!(!low_limit.is_null()); debug_assert!(!dict_limit.is_null()); let end = end_t as u32; unsafe { *low_limit = end; *dict_limit = end; } } /// Invalidate a C-owned match-state projection after the caller computes the /// private window pointer difference. Rust owns the paired window-limit and /// match-state field updates while C retains the match-state layout. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_invalidateMatchState( end_t: usize, low_limit: *mut u32, dict_limit: *mut u32, next_to_update: *mut u32, loaded_dict_end: *mut u32, lit_length_sum: *mut u32, dict_match_state: *mut *const c_void, ) { debug_assert!(!low_limit.is_null()); debug_assert!(!dict_limit.is_null()); debug_assert!(!next_to_update.is_null()); debug_assert!(!loaded_dict_end.is_null()); debug_assert!(!lit_length_sum.is_null()); debug_assert!(!dict_match_state.is_null()); unsafe { ZSTD_rust_windowClear(end_t, low_limit, dict_limit); *next_to_update = *dict_limit; *loaded_dict_end = 0; *lit_length_sum = 0; *dict_match_state = ptr::null(); } } const HASH_READ_SIZE: u32 = 8; /// Private C window fields projected for the shared window-update policy. /// Pointer arithmetic is intentionally performed as wrapping address math to /// mirror the original C implementation's pointer-overflow contract. #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ZSTD_rust_windowUpdateState { pub nextSrc: *const c_void, pub base: *const c_void, pub dictBase: *const c_void, pub dictLimit: u32, pub lowLimit: u32, } const _: () = { assert!(offset_of!(ZSTD_rust_windowUpdateState, nextSrc) == 0); assert!(offset_of!(ZSTD_rust_windowUpdateState, base) == size_of::()); assert!(offset_of!(ZSTD_rust_windowUpdateState, dictBase) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_windowUpdateState, dictLimit) == 3 * size_of::()); assert!( offset_of!(ZSTD_rust_windowUpdateState, lowLimit) == 3 * size_of::() + size_of::() ); assert!( size_of::() == 3 * size_of::() + 2 * size_of::() ); }; static WINDOW_INIT_SENTINEL: [u8; 2] = [b' ', 0]; /// Initialize the projected window fields. C retains the containing object /// and its overflow-correction counter. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_windowInit(state: *mut ZSTD_rust_windowUpdateState) { if state.is_null() { return; } let base = WINDOW_INIT_SENTINEL.as_ptr(); unsafe { *state = ZSTD_rust_windowUpdateState { nextSrc: base.wrapping_add(2).cast(), base: base.cast(), dictBase: base.cast(), dictLimit: ZSTD_WINDOW_START_INDEX, lowLimit: ZSTD_WINDOW_START_INDEX, }; } } #[no_mangle] pub extern "C" fn ZSTD_rust_windowIsEmpty( next_src: *const c_void, base: *const c_void, dict_limit: u32, low_limit: u32, ) -> c_uint { (dict_limit == ZSTD_WINDOW_START_INDEX && low_limit == ZSTD_WINDOW_START_INDEX && (next_src as usize).wrapping_sub(base as usize) == ZSTD_WINDOW_START_INDEX as usize) as c_uint } #[no_mangle] pub extern "C" fn ZSTD_rust_windowHasExtDict(dict_limit: u32, low_limit: u32) -> c_uint { (low_limit < dict_limit) as c_uint } /// Project the dictionary/window limit policy while C retains pointer-owned /// match-state invalidation and the private window layout. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ZSTD_rust_windowDictState { pub lowLimit: u32, pub dictLimit: u32, pub loadedDictEnd: u32, pub invalidate: u32, } const _: () = { assert!(offset_of!(ZSTD_rust_windowDictState, lowLimit) == 0); assert!(offset_of!(ZSTD_rust_windowDictState, dictLimit) == size_of::()); assert!(offset_of!(ZSTD_rust_windowDictState, loadedDictEnd) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_windowDictState, invalidate) == 3 * size_of::()); assert!(size_of::() == 4 * size_of::()); }; #[no_mangle] pub extern "C" fn ZSTD_rust_windowEnforceMaxDist( block_end_idx: u32, max_dist: u32, mut state: ZSTD_rust_windowDictState, ) -> ZSTD_rust_windowDictState { state.invalidate = 0; if block_end_idx > max_dist.wrapping_add(state.loadedDictEnd) { let new_low_limit = block_end_idx.wrapping_sub(max_dist); if state.lowLimit < new_low_limit { state.lowLimit = new_low_limit; } if state.dictLimit < state.lowLimit { state.dictLimit = state.lowLimit; } state.loadedDictEnd = 0; state.invalidate = 1; } state } #[no_mangle] pub extern "C" fn ZSTD_rust_windowCheckDictValidity( block_end_idx: u32, max_dist: u32, loaded_dict_end: u32, dict_limit: u32, ) -> c_uint { (block_end_idx > loaded_dict_end.wrapping_add(max_dist) || loaded_dict_end != dict_limit) as c_uint } /// Update the rolling prefix/ext-dictionary window for one input segment. /// C owns the containing match state; Rust owns this five-field transition. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_windowUpdate( state: *mut ZSTD_rust_windowUpdateState, src: *const c_void, src_size: usize, force_non_contiguous: c_int, ) -> c_uint { if state.is_null() { return 1; } let state = unsafe { &mut *state }; if src_size == 0 { return 1; } debug_assert!(!state.base.is_null()); debug_assert!(!state.dictBase.is_null()); let ip = src as usize; let input_end = ip.wrapping_add(src_size); let mut contiguous = 1; if ip != state.nextSrc as usize || force_non_contiguous != 0 { let distance_from_base = (state.nextSrc as usize).wrapping_sub(state.base as usize); state.lowLimit = state.dictLimit; debug_assert!(distance_from_base <= u32::MAX as usize); state.dictLimit = distance_from_base as u32; state.dictBase = state.base; state.base = ip.wrapping_sub(distance_from_base) as *const c_void; if state.dictLimit.wrapping_sub(state.lowLimit) < HASH_READ_SIZE { state.lowLimit = state.dictLimit; } contiguous = 0; } state.nextSrc = input_end as *const c_void; let dict_base = state.dictBase as usize; if input_end > dict_base.wrapping_add(state.lowLimit as usize) && ip < dict_base.wrapping_add(state.dictLimit as usize) { let high_input_idx = input_end.wrapping_sub(dict_base); debug_assert!(high_input_idx < u32::MAX as usize); let low_limit_max = if high_input_idx > state.dictLimit as usize { state.dictLimit } else { high_input_idx as u32 }; state.lowLimit = low_limit_max; } contiguous } #[inline] fn set_pledged_src_size( stream_stage: c_int, pledged_src_size: u64, pledged_src_size_plus_one: *mut u64, ) -> usize { if stream_stage != ZSTD_CSTREAM_STAGE_INIT { return ERROR(ZstdErrorCode::StageWrong); } debug_assert!(!pledged_src_size_plus_one.is_null()); unsafe { *pledged_src_size_plus_one = pledged_src_size.wrapping_add(1); } 0 } /// Set the pledged source size through scalar stage state and a C-owned field. /// The stage error remains the public `stage_wrong` encoding, while the /// unsigned increment preserves C's defined wrap at `U64::MAX`. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_setPledgedSrcSize( stream_stage: c_int, pledged_src_size: u64, pledged_src_size_plus_one: *mut u64, ) -> usize { set_pledged_src_size(stream_stage, pledged_src_size, pledged_src_size_plus_one) } #[inline] fn zeroed_state() -> ZSTD_compressedBlockState_t { /* The state contains only integer arrays and enum fields. */ unsafe { MaybeUninit::::zeroed().assume_init() } } #[inline] fn checked_table_size(log: u32) -> Option { 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::(); let mut op = dst.cast::(); 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::(); 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::().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 { 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::(); 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::(); 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::().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::()]; 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::(); let output = dst.cast::(); 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::().add(output_ref.pos).cast() } }; let src = if input_ref.src.is_null() { ptr::null() } else { unsafe { input_ref.src.cast::().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; #[derive(Default)] struct BuildSeqStoreSelectProbe { events: Vec<&'static str>, external_result: usize, ldm_result: usize, producer_result: usize, producer_complete: c_int, producer_allow_fallback: c_int, internal_result: usize, } unsafe fn build_seq_store_select_probe( context: *mut c_void, ) -> &'static mut BuildSeqStoreSelectProbe { unsafe { &mut *context.cast::() } } unsafe extern "C" fn build_seq_store_test_skip(_context: *mut c_void, _src_size: usize) {} unsafe extern "C" fn build_seq_store_test_prepare( _context: *mut c_void, _src: *const c_void, _src_size: usize, ) { } unsafe extern "C" fn build_seq_store_test_external( context: *mut c_void, _seq_store: *mut SeqStore_t, _next_rep: *mut u32, _src: *const c_void, _src_size: usize, ) -> usize { let probe = unsafe { build_seq_store_select_probe(context) }; probe.events.push("external"); probe.external_result } unsafe extern "C" fn build_seq_store_test_ldm( context: *mut c_void, _seq_store: *mut SeqStore_t, _next_rep: *mut u32, _src: *const c_void, _src_size: usize, ) -> usize { let probe = unsafe { build_seq_store_select_probe(context) }; probe.events.push("ldm"); probe.ldm_result } unsafe extern "C" fn build_seq_store_test_producer( context: *mut c_void, _src: *const c_void, _src_size: usize, seq_store_complete: *mut c_int, allow_fallback: *mut c_int, ) -> usize { let probe = unsafe { build_seq_store_select_probe(context) }; probe.events.push("producer"); unsafe { *seq_store_complete = probe.producer_complete; *allow_fallback = probe.producer_allow_fallback; } probe.producer_result } unsafe extern "C" fn build_seq_store_test_internal( context: *mut c_void, _seq_store: *mut SeqStore_t, _next_rep: *mut u32, _src: *const c_void, _src_size: usize, ) -> usize { let probe = unsafe { build_seq_store_select_probe(context) }; probe.events.push("internal"); probe.internal_result } unsafe extern "C" fn build_seq_store_test_clear(context: *mut c_void) { let probe = unsafe { build_seq_store_select_probe(context) }; probe.events.push("clear"); } fn build_seq_store_select_test_state( probe: &mut BuildSeqStoreSelectProbe, has_external_sequences: c_int, ldm_enabled: c_int, has_external_sequence_producer: c_int, enable_match_finder_fallback: c_int, ) -> ZSTD_rust_buildSeqStoreState { ZSTD_rust_buildSeqStoreState { seq_store: ptr::null_mut(), prev_c_block: ptr::null_mut(), next_c_block: ptr::null_mut(), callback_context: (probe as *mut BuildSeqStoreSelectProbe).cast(), min_match: 3, validate_seq_store: 0, skip_small_block: build_seq_store_test_skip, prepare_match_state: build_seq_store_test_prepare, has_external_sequences, ldm_enabled, has_external_sequence_producer, enable_match_finder_fallback, compress_external_sequences: build_seq_store_test_external, compress_ldm: build_seq_store_test_ldm, try_external_sequence_producer: build_seq_store_test_producer, compress_internal: build_seq_store_test_internal, clear_ldm_seq_store: build_seq_store_test_clear, } } fn run_build_seq_store_select_test(state: &ZSTD_rust_buildSeqStoreState) -> (usize, c_int) { let mut seq_store = unsafe { MaybeUninit::::zeroed().assume_init() }; let mut next_rep = [0u32; ZSTD_REP_NUM]; let source = [0u8; 1]; let mut seq_store_complete = 99; let result = unsafe { build_seq_store_select_sequences_body_with( state, &mut seq_store, next_rep.as_mut_ptr(), source.as_ptr().cast(), source.len(), &mut seq_store_complete, ) }; (result, seq_store_complete) } #[test] fn build_seq_store_selects_preloaded_external_sequences_first() { let mut probe = BuildSeqStoreSelectProbe { external_result: 11, ldm_result: 12, internal_result: 13, ..Default::default() }; let state = build_seq_store_select_test_state(&mut probe, 1, 1, 0, 0); let (result, seq_store_complete) = run_build_seq_store_select_test(&state); assert_eq!(result, probe.external_result); assert_eq!(seq_store_complete, 0); assert_eq!(probe.events, ["external"]); } #[test] fn build_seq_store_uses_ldm_when_no_external_sequences_are_pending() { let mut probe = BuildSeqStoreSelectProbe { ldm_result: 19, internal_result: 23, ..Default::default() }; let state = build_seq_store_select_test_state(&mut probe, 0, 1, 0, 0); let (result, seq_store_complete) = run_build_seq_store_select_test(&state); assert_eq!(result, probe.ldm_result); assert_eq!(seq_store_complete, 0); assert_eq!(probe.events, ["ldm"]); } #[test] fn build_seq_store_rejects_external_producer_with_preloaded_sequences_or_ldm() { for (has_external_sequences, ldm_enabled) in [(1, 0), (0, 1)] { let mut probe = BuildSeqStoreSelectProbe::default(); let state = build_seq_store_select_test_state( &mut probe, has_external_sequences, ldm_enabled, 1, 1, ); let (result, seq_store_complete) = run_build_seq_store_select_test(&state); assert_eq!( result, ERROR(ZstdErrorCode::ParameterCombinationUnsupported) ); assert_eq!(seq_store_complete, 0); assert!(probe.events.is_empty()); } } #[test] fn build_seq_store_clears_ldm_after_external_producer_completion() { let mut probe = BuildSeqStoreSelectProbe { producer_complete: 1, producer_allow_fallback: 1, producer_result: 0, ..Default::default() }; let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 1); let (result, seq_store_complete) = run_build_seq_store_select_test(&state); assert_eq!(result, 0); assert_eq!(seq_store_complete, 1); assert_eq!(probe.events, ["producer", "clear"]); } #[test] fn build_seq_store_does_not_fallback_when_producer_disallows_it() { let producer_result = ERROR(ZstdErrorCode::SequenceProducerFailed); let mut probe = BuildSeqStoreSelectProbe { producer_result, producer_allow_fallback: 1, internal_result: 17, ..Default::default() }; let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 0); let (result, seq_store_complete) = run_build_seq_store_select_test(&state); assert_eq!(result, producer_result); assert_eq!(seq_store_complete, 0); assert_eq!(probe.events, ["producer"]); } #[test] fn build_seq_store_falls_back_after_external_producer_error() { let producer_result = ERROR(ZstdErrorCode::SequenceProducerFailed); let mut probe = BuildSeqStoreSelectProbe { producer_result, producer_allow_fallback: 1, internal_result: 23, ..Default::default() }; let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 1); let (result, seq_store_complete) = run_build_seq_store_select_test(&state); assert_eq!(result, probe.internal_result); assert_eq!(seq_store_complete, 0); assert_eq!(probe.events, ["producer", "clear", "internal"]); } #[test] fn build_seq_store_clears_ldm_before_internal_compression() { let mut probe = BuildSeqStoreSelectProbe { internal_result: 29, ..Default::default() }; let state = build_seq_store_select_test_state(&mut probe, 0, 0, 0, 0); let (result, seq_store_complete) = run_build_seq_store_select_test(&state); assert_eq!(result, probe.internal_result); assert_eq!(seq_store_complete, 0); assert_eq!(probe.events, ["clear", "internal"]); } #[derive(Default)] struct ResetMatchStateTestContext { events: Vec, reserves: Vec<(c_int, usize)>, pointers: Vec<(c_int, *mut c_void)>, zeroed: Vec<(usize, usize)>, storage: [usize; 32], fail_after: usize, } unsafe fn reset_match_state_test_context( context: *mut c_void, ) -> &'static mut ResetMatchStateTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn reset_match_state_test_callback(context: *mut c_void) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(1); } unsafe extern "C" fn reset_match_state_test_mark_dirty(context: *mut c_void) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(2); } unsafe extern "C" fn reset_match_state_test_invalidate(context: *mut c_void) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(3); } unsafe extern "C" fn reset_match_state_test_clear(context: *mut c_void) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(4); } unsafe extern "C" fn reset_match_state_test_clean(context: *mut c_void) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(5); } unsafe extern "C" fn reset_match_state_test_advance(context: *mut c_void) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(6); } unsafe extern "C" fn reset_match_state_test_reserve( context: *mut c_void, reserve_kind: c_int, size: usize, ) -> *mut c_void { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(100 + reserve_kind); let index = context.reserves.len(); context.reserves.push((reserve_kind, size)); if context.fail_after != 0 && index + 1 >= context.fail_after { ptr::null_mut() } else { unsafe { context.storage.as_mut_ptr().add(index).cast() } } } unsafe extern "C" fn reset_match_state_test_reserve_failed(context: *mut c_void) -> c_int { let context = unsafe { reset_match_state_test_context(context) }; c_int::from(context.fail_after != 0 && context.reserves.len() >= context.fail_after) } unsafe extern "C" fn reset_match_state_test_set_pointer( context: *mut c_void, pointer_kind: c_int, pointer: *mut c_void, ) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(200 + pointer_kind); context.pointers.push((pointer_kind, pointer)); } unsafe extern "C" fn reset_match_state_test_zero( context: *mut c_void, pointer: *mut c_void, size: usize, ) { let context = unsafe { reset_match_state_test_context(context) }; context.events.push(7); context.zeroed.push((pointer as usize, size)); } #[allow(clippy::too_many_arguments)] fn reset_match_state_test_state( context: &mut ResetMatchStateTestContext, c_params: ZSTD_compressionParameters, dedicated_dict_search: c_int, use_row_match_finder: c_int, comp_reset_policy: c_int, index_reset_policy: c_int, reset_target: c_int, hash_log3: &mut c_uint, row_hash_log: &mut c_uint, hash_salt: &mut u64, lazy_skipping: &mut c_int, c_params_out: &mut ZSTD_compressionParameters, ) -> ZSTD_rust_resetMatchStateState { ZSTD_rust_resetMatchStateState { callback_context: (context as *mut ResetMatchStateTestContext).cast(), c_params, dedicated_dict_search, use_row_match_finder, comp_reset_policy, index_reset_policy, reset_target, hash_log3_max: 17, lit_freq_size: 256 * size_of::(), lit_length_freq_size: 36 * size_of::(), match_length_freq_size: 53 * size_of::(), off_code_freq_size: 32 * size_of::(), match_size: 4099 * 2 * size_of::(), optimal_size: 4099 * 8 * size_of::(), hash_log3, row_hash_log, hash_salt, lazy_skipping, c_params_out, set_pointer: Some(reset_match_state_test_set_pointer), window_init: Some(reset_match_state_test_callback), mark_tables_dirty: Some(reset_match_state_test_mark_dirty), invalidate_match_state: Some(reset_match_state_test_invalidate), clear_tables: Some(reset_match_state_test_clear), clean_tables: Some(reset_match_state_test_clean), advance_hash_salt: Some(reset_match_state_test_advance), reserve: Some(reset_match_state_test_reserve), reserve_failed: Some(reset_match_state_test_reserve_failed), zero: Some(reset_match_state_test_zero), } } fn reset_match_state_test_params( strategy: c_int, search_log: u32, min_match: u32, ) -> ZSTD_compressionParameters { ZSTD_compressionParameters { windowLog: 20, chainLog: 11, hashLog: 12, searchLog: search_log, minMatch: min_match, targetLength: 16, strategy, } } #[test] fn reset_match_state_orders_cctx_opt_allocations() { let c_params = reset_match_state_test_params(ZSTD_BTOPT, 5, 3); let mut context = ResetMatchStateTestContext::default(); let mut hash_log3 = 99; let mut row_hash_log = 99; let mut hash_salt = 7; let mut lazy_skipping = 9; let mut c_params_out = ZSTD_compressionParameters::default(); let state = reset_match_state_test_state( &mut context, c_params, 0, ZSTD_RUST_PS_DISABLE, 0, 1, 1, &mut hash_log3, &mut row_hash_log, &mut hash_salt, &mut lazy_skipping, &mut c_params_out, ); let result = unsafe { ZSTD_rust_resetMatchState(&state) }; assert_eq!(result, 0); assert_eq!(hash_log3, 17); assert_eq!(row_hash_log, 99); assert_eq!(hash_salt, 7); assert_eq!(lazy_skipping, 0); assert_eq!(c_params_out, c_params); assert_eq!( context.events, [ 1, 2, 3, 4, 100, 200, 100, 201, 100, 202, 5, 102, 204, 102, 205, 102, 206, 102, 207, 102, 208, 102, 209, ] ); assert_eq!( context.reserves, [ (0, (1 << 12) * size_of::()), (0, (1 << 11) * size_of::()), (0, (1 << 17) * size_of::()), (2, 256 * size_of::()), (2, 36 * size_of::()), (2, 53 * size_of::()), (2, 32 * size_of::()), (2, 4099 * 2 * size_of::()), (2, 4099 * 8 * size_of::()), ] ); assert_eq!(context.pointers.len(), 9); assert!(context.zeroed.is_empty()); } #[test] fn reset_match_state_salts_cctx_row_tables_without_zeroing() { let c_params = reset_match_state_test_params(ZSTD_GREEDY, 6, 4); let mut context = ResetMatchStateTestContext::default(); let mut hash_log3 = 99; let mut row_hash_log = 99; let mut hash_salt = 7; let mut lazy_skipping = 9; let mut c_params_out = ZSTD_compressionParameters::default(); let state = reset_match_state_test_state( &mut context, c_params, 0, ZSTD_RUST_PS_ENABLE, 0, 0, 1, &mut hash_log3, &mut row_hash_log, &mut hash_salt, &mut lazy_skipping, &mut c_params_out, ); let result = unsafe { ZSTD_rust_resetMatchState(&state) }; assert_eq!(result, 0); assert_eq!(hash_log3, 0); assert_eq!(row_hash_log, 6); assert_eq!(hash_salt, 7); assert_eq!(c_params_out, c_params); assert_eq!( context.events, [3, 4, 100, 200, 100, 201, 100, 202, 5, 101, 203, 6] ); assert_eq!( context.reserves, [ (0, (1 << 12) * size_of::()), (0, 0), (0, 0), (1, 1 << 12), ] ); assert_eq!(context.pointers.len(), 4); assert!(context.zeroed.is_empty()); } #[test] fn reset_match_state_uses_cdict_row_policy_without_opt_storage() { let c_params = reset_match_state_test_params(ZSTD_GREEDY, 6, 4); let mut context = ResetMatchStateTestContext::default(); let mut hash_log3 = 99; let mut row_hash_log = 99; let mut hash_salt = 7; let mut lazy_skipping = 9; let mut c_params_out = ZSTD_compressionParameters::default(); let state = reset_match_state_test_state( &mut context, c_params, 1, ZSTD_RUST_PS_ENABLE, 1, 0, 0, &mut hash_log3, &mut row_hash_log, &mut hash_salt, &mut lazy_skipping, &mut c_params_out, ); let result = unsafe { ZSTD_rust_resetMatchState(&state) }; assert_eq!(result, 0); assert_eq!(hash_log3, 0); assert_eq!(row_hash_log, 6); assert_eq!(hash_salt, 0); assert_eq!(c_params_out, c_params); assert_eq!( context.events, [3, 4, 100, 200, 100, 201, 100, 202, 102, 203, 7] ); assert_eq!( context.reserves, [ (0, (1 << 12) * size_of::()), (0, (1 << 11) * size_of::()), (0, 0), (2, 1 << 12), ] ); assert_eq!(context.pointers.len(), 4); assert_eq!(context.zeroed.len(), 1); assert_eq!(context.zeroed[0].1, 1 << 12); } #[test] fn reset_match_state_propagates_table_allocation_failure() { let c_params = reset_match_state_test_params(ZSTD_GREEDY, 4, 4); let mut context = ResetMatchStateTestContext { fail_after: 1, ..ResetMatchStateTestContext::default() }; let mut hash_log3 = 99; let mut row_hash_log = 99; let mut hash_salt = 7; let mut lazy_skipping = 9; let mut c_params_out = ZSTD_compressionParameters::default(); let state = reset_match_state_test_state( &mut context, c_params, 0, ZSTD_RUST_PS_DISABLE, 0, 1, 1, &mut hash_log3, &mut row_hash_log, &mut hash_salt, &mut lazy_skipping, &mut c_params_out, ); let result = unsafe { ZSTD_rust_resetMatchState(&state) }; assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation)); assert_eq!(context.events, [1, 2, 3, 4, 100, 200, 100, 201, 100, 202]); assert_eq!(context.pointers.len(), 3); assert_eq!(hash_log3, 0); assert_eq!(lazy_skipping, 0); assert_eq!(c_params_out, ZSTD_compressionParameters::default()); } #[test] fn reference_external_sequences_resets_store_state() { let mut source = [0u8; 3]; let sequence = source.as_mut_ptr().cast::(); 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, 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::() } } 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::() } } 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::() } } 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_create_result: usize, create_result: usize, mt_result: usize, begin_result: usize, pledged: u64, local_dict: *const c_void, local_dict_buffer: *mut c_void, local_dict_size: usize, local_dict_content_type: c_int, local_cdict: *mut c_void, context_cdict: *const c_void, prefix_dict: *const c_void, created_local_cdict: *mut c_void, cdict: *const c_void, cdict_is_local: c_int, cdict_compression_level: c_int, cdict_dict_content_size: usize, expected_prefix: *const c_void, nb_workers: c_uint, has_ext_seq_prod: c_int, mt_context: *mut c_void, buffer_mode: c_int, block_size: usize, ordinary_target: usize, compression_level: c_int, } unsafe fn compress_stream_init_test_context( context: *mut c_void, ) -> &'static mut CompressStreamInitTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn compress_stream_init_test_get_local_dict( context: *mut c_void, local_dict: *mut ZSTD_rust_compressStreamInitLocalDictState, ) { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("local-dict"); let local_dict = unsafe { &mut *local_dict }; local_dict.dict = context.local_dict; local_dict.dict_buffer = context.local_dict_buffer; local_dict.dict_size = context.local_dict_size; local_dict.dict_content_type = context.local_dict_content_type; local_dict.local_cdict = context.local_cdict; local_dict.cdict = context.context_cdict; local_dict.prefix_dict = context.prefix_dict; local_dict.created_cdict = ptr::null_mut(); } unsafe extern "C" fn compress_stream_init_test_create_local_dict( context: *mut c_void, local_dict: *mut ZSTD_rust_compressStreamInitLocalDictState, ) -> usize { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("create-local-dict"); unsafe { &mut *local_dict }.created_cdict = context.created_local_cdict; context.local_create_result } unsafe extern "C" fn compress_stream_init_test_publish_local_dict( context: *mut c_void, local_dict: *const ZSTD_rust_compressStreamInitLocalDictState, ) { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("publish-local-dict"); let local_dict = unsafe { &*local_dict }; context.local_cdict = local_dict.created_cdict; context.context_cdict = local_dict.created_cdict.cast_const(); context.cdict = local_dict.created_cdict.cast_const(); } unsafe extern "C" fn compress_stream_init_test_refresh_cdict( context: *mut c_void, dictionaries: *mut ZSTD_rust_compressStreamInitDictionaryState, ) { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("refresh-cdict"); let dictionaries = unsafe { &mut *dictionaries }; dictionaries.cdict = context.cdict; dictionaries.cdict_is_local = context.cdict_is_local; dictionaries.cdict_compression_level = context.cdict_compression_level; dictionaries.cdict_dict_content_size = context.cdict_dict_content_size; } unsafe extern "C" fn compress_stream_init_test_clear_prefix(context: *mut c_void) { unsafe { compress_stream_init_test_context(context) } .events .push("clear-prefix"); } unsafe extern "C" fn compress_stream_init_test_assert_dictionaries( context: *mut c_void, prefix: *const c_void, ) { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("assert-dictionaries"); assert_eq!(prefix, context.expected_prefix); } unsafe extern "C" fn compress_stream_init_test_set_level(params: *mut c_void, level: c_int) { let context = unsafe { &mut *params.cast::() }; 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::(); } context.create_result } unsafe extern "C" fn compress_stream_init_test_mt( context: *mut c_void, _mt_context: *mut c_void, _dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState, _params: *mut c_void, _pledged: u64, ) -> usize { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("init-mt"); context.mt_result } unsafe extern "C" fn compress_stream_init_test_commit_mt( context: *mut c_void, _dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState, _params: *mut c_void, ) { unsafe { compress_stream_init_test_context(context) } .events .push("commit-mt"); } unsafe extern "C" fn compress_stream_init_test_check_cparams(params: *mut c_void) { unsafe { compress_stream_init_test_context(params) } .events .push("check-cparams"); } unsafe extern "C" fn compress_stream_init_test_begin( context: *mut c_void, _dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState, _params: *mut c_void, _pledged: u64, ) -> usize { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("compress-begin"); context.begin_result } unsafe extern "C" fn compress_stream_init_test_assert_ordinary(context: *mut c_void) { unsafe { compress_stream_init_test_context(context) } .events .push("assert-ordinary"); } unsafe extern "C" fn compress_stream_init_test_get_buffer_mode(context: *mut c_void) -> c_int { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("get-buffer-mode"); context.buffer_mode } unsafe extern "C" fn compress_stream_init_test_get_block_size(context: *mut c_void) -> usize { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("get-block-size"); context.block_size } unsafe extern "C" fn compress_stream_init_test_commit_ordinary( context: *mut c_void, in_buff_target: usize, ) { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("commit-ordinary"); context.ordinary_target = in_buff_target; } fn compress_stream_init_test_state( context: &mut CompressStreamInitTestContext, dictionaries: &mut ZSTD_rust_compressStreamInitDictionaryState, end_op: c_int, in_size: usize, multithreaded: c_int, ) -> ZSTD_rust_compressStreamInitState { ZSTD_rust_compressStreamInitState { callback_context: (context as *mut CompressStreamInitTestContext).cast(), params: (context as *mut CompressStreamInitTestContext).cast(), dictionaries, end_op, in_size, multithreaded, mt_job_size_min: 10, get_local_dict: compress_stream_init_test_get_local_dict, create_local_dict: compress_stream_init_test_create_local_dict, publish_local_dict: compress_stream_init_test_publish_local_dict, refresh_cdict: compress_stream_init_test_refresh_cdict, clear_prefix: compress_stream_init_test_clear_prefix, assert_dictionaries: compress_stream_init_test_assert_dictionaries, set_compression_level: compress_stream_init_test_set_level, debug_init: compress_stream_init_test_debug, get_pledged_src_size_plus_one: compress_stream_init_test_get_pledged, set_pledged_src_size: compress_stream_init_test_set_pledged, get_cparam_mode: compress_stream_init_test_get_cparam_mode, build_cparams: compress_stream_init_test_build_cparams, resolve_params: compress_stream_init_test_resolve_params, get_nb_workers: compress_stream_init_test_get_nb_workers, set_nb_workers: compress_stream_init_test_set_nb_workers, has_ext_seq_prod: compress_stream_init_test_has_ext_seq_prod, trace_begin: compress_stream_init_test_trace, get_mt_context: compress_stream_init_test_get_mt_context, create_mt_context: compress_stream_init_test_create_mt_context, init_mt: compress_stream_init_test_mt, commit_mt: compress_stream_init_test_commit_mt, check_cparams: compress_stream_init_test_check_cparams, compress_begin: compress_stream_init_test_begin, assert_ordinary: compress_stream_init_test_assert_ordinary, get_buffer_mode: compress_stream_init_test_get_buffer_mode, get_block_size: compress_stream_init_test_get_block_size, commit_ordinary: compress_stream_init_test_commit_ordinary, } } #[test] fn compress_stream_init_preserves_ordinary_callback_order_and_policy() { let mut context = CompressStreamInitTestContext { pledged: 0, cdict: ptr::dangling::(), cdict_compression_level: 17, cdict_dict_content_size: 3, nb_workers: 0, buffer_mode: ZSTD_BM_BUFFERED, block_size: 4, ..CompressStreamInitTestContext::default() }; let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState { prefix_dict: ptr::null(), prefix_dict_size: 0, prefix_dict_content_type: 0, cdict: ptr::null(), cdict_is_local: 0, cdict_compression_level: 0, cdict_dict_content_size: 0, }; let state = compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_END, 4, 0); let result = unsafe { ZSTD_rust_compressStreamInit(&state) }; assert_eq!(result, 0); assert_eq!(context.compression_level, 17); assert_eq!(context.pledged, 5); assert_eq!(context.ordinary_target, 5); assert_eq!( context.events, [ "local-dict", "refresh-cdict", "clear-prefix", "assert-dictionaries", "set-level", "debug", "set-pledged", "get-pledged", "get-cparam-mode", "build-cparams", "resolve:block-splitter", "resolve:ldm", "resolve:row-match-finder", "resolve:validate-sequences", "resolve:max-block-size", "resolve:external-repcodes", "check-cparams", "compress-begin", "assert-ordinary", "get-buffer-mode", "get-block-size", "commit-ordinary", ] ); } #[test] fn compress_stream_init_orchestrates_local_dictionary_creation_and_publish() { let mut context = CompressStreamInitTestContext { local_dict: ptr::dangling::(), local_dict_size: 3, created_local_cdict: ptr::dangling_mut::(), cdict_is_local: 1, cdict_dict_content_size: 3, buffer_mode: ZSTD_BM_BUFFERED, block_size: 4, ..CompressStreamInitTestContext::default() }; let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState { prefix_dict: ptr::null(), prefix_dict_size: 0, prefix_dict_content_type: 0, cdict: ptr::null(), cdict_is_local: 0, cdict_compression_level: 0, cdict_dict_content_size: 0, }; let state = compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_CONTINUE, 0, 0); let result = unsafe { ZSTD_rust_compressStreamInit(&state) }; assert_eq!(result, 0); assert_eq!(context.local_cdict, context.created_local_cdict); assert_eq!( context.context_cdict, context.created_local_cdict.cast_const() ); assert_eq!( &context.events[..3], ["local-dict", "create-local-dict", "publish-local-dict"] ); assert_eq!(context.events[3], "refresh-cdict"); assert_eq!(dictionaries.cdict, context.created_local_cdict.cast_const()); assert_eq!(dictionaries.cdict_is_local, 1); } #[test] fn compress_stream_init_stops_before_later_callbacks_on_error() { let mut context = CompressStreamInitTestContext { local_dict: ptr::dangling::(), local_dict_size: 3, local_create_result: ERROR(ZstdErrorCode::MemoryAllocation), ..CompressStreamInitTestContext::default() }; let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState { prefix_dict: ptr::null(), prefix_dict_size: 0, prefix_dict_content_type: 0, cdict: ptr::null(), cdict_is_local: 0, cdict_compression_level: 0, cdict_dict_content_size: 0, }; let state = compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_CONTINUE, 0, 0); let result = unsafe { ZSTD_rust_compressStreamInit(&state) }; assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation)); assert_eq!(context.events, ["local-dict", "create-local-dict"]); } #[test] fn compress_stream_init_preserves_mt_branch_and_init_error_order() { let mut context = CompressStreamInitTestContext { cdict: ptr::dangling::(), 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> { 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 { let capacity = crate::zstd_compress_api::ZSTD_compressBound(input.len()); assert!(!ERR_isError(capacity)); let mut output = vec![0u8; capacity]; let written = unsafe { ZSTD_compress( output.as_mut_ptr().cast(), output.len(), input.as_ptr().cast(), input.len(), level, ) }; assert!(!ERR_isError(written)); output.truncate(written); output } #[derive(Default)] struct FrameChunkTestContext { prepare_calls: usize, prepared_sizes: [usize; 4], prepare_order: [u8; 16], prepare_order_len: usize, prepare_max_dist: c_uint, prepare_state: Option, clamp_state: Option, clamp_next_to_update: c_uint, clamp_low_limit: c_uint, target_calls: usize, split_calls: usize, internal_calls: usize, last_blocks: [c_uint; 4], checksum_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::() } } unsafe fn frame_chunk_test_record_prepare(context: *mut c_void, step: u8, block_size: usize) { let context = unsafe { frame_chunk_test_context(context) }; if context.prepare_order_len < context.prepare_order.len() { context.prepare_order[context.prepare_order_len] = step; } context.prepare_order_len += 1; if step == 1 { if context.prepare_calls < context.prepared_sizes.len() { context.prepared_sizes[context.prepare_calls] = block_size; } context.prepare_calls += 1; } } unsafe extern "C" fn frame_chunk_test_correct_overflow( context: *mut c_void, _src: *const c_void, block_size: usize, ) { unsafe { frame_chunk_test_record_prepare(context, 1, block_size) }; } unsafe extern "C" fn frame_chunk_test_check_dict_validity( context: *mut c_void, _src: *const c_void, block_size: usize, max_dist: c_uint, ) { unsafe { frame_chunk_test_record_prepare(context, 2, block_size) }; unsafe { frame_chunk_test_context(context) }.prepare_max_dist = max_dist; } unsafe extern "C" fn frame_chunk_test_enforce_max_dist( context: *mut c_void, _src: *const c_void, block_size: usize, max_dist: c_uint, ) { unsafe { frame_chunk_test_record_prepare(context, 3, block_size) }; unsafe { frame_chunk_test_context(context) }.prepare_max_dist = max_dist; } unsafe extern "C" fn frame_chunk_test_target( context: *mut c_void, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, last_block: c_uint, ) -> usize { let context = unsafe { frame_chunk_test_context(context) }; if context.target_calls < context.last_blocks.len() { context.last_blocks[context.target_calls] = last_block; } context.target_calls += 1; context.target_result } unsafe extern "C" fn frame_chunk_test_split( context: *mut c_void, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, last_block: c_uint, ) -> usize { let context = unsafe { frame_chunk_test_context(context) }; if context.split_calls < context.last_blocks.len() { context.last_blocks[context.split_calls] = last_block; } context.split_calls += 1; context.split_result } unsafe extern "C" fn frame_chunk_test_internal( context: *mut c_void, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, last_block: c_uint, ) -> usize { let context = unsafe { frame_chunk_test_context(context) }; if context.internal_calls < context.last_blocks.len() { context.last_blocks[context.internal_calls] = last_block; } context.internal_calls += 1; context.internal_result } 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_ptr = context as *mut FrameChunkTestContext; let callback_context = context_ptr.cast::(); unsafe { (*context_ptr).clamp_state = Some(ZSTD_rust_frameChunkClampState { next_to_update: &mut (*context_ptr).clamp_next_to_update, low_limit: &(*context_ptr).clamp_low_limit, }); (*context_ptr).prepare_state = Some(ZSTD_rust_frameChunkPrepareState { callback_context, max_dist: 64, correct_overflow: frame_chunk_test_correct_overflow, check_dict_validity: frame_chunk_test_check_dict_validity, enforce_max_dist: frame_chunk_test_enforce_max_dist, clamp_state: (*context_ptr) .clamp_state .as_ref() .map_or(ptr::null(), |state| state as *const _), }); } let prepare_state = unsafe { (*context_ptr) .prepare_state .as_ref() .map_or(ptr::null(), |state| state as *const _) }; ZSTD_rust_frameChunkState { callback_context, tmp_workspace: ptr::null_mut(), checksum_state: callback_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_state, 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, clamp_next_to_update: 1, clamp_low_limit: 4, ..FrameChunkTestContext::default() }; let mut is_first_block = 1; let mut stage = 0; let state = frame_chunk_test_state(&mut context, &mut is_first_block, &mut stage, 0, 0, 1); let source = [0x11u8; 8]; let mut output = [0xa5u8; 16]; let result = unsafe { compress_frame_chunk_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, ) }; assert_eq!(result, 10); assert_eq!(context.prepare_calls, 2); assert_eq!(context.prepared_sizes[..2], [4, 4]); assert_eq!(context.prepare_order[..6], [1, 2, 3, 1, 2, 3]); assert_eq!(context.prepare_max_dist, 64); assert_eq!(context.clamp_next_to_update, 4); assert_eq!(context.internal_calls, 2); assert_eq!(context.last_blocks[..2], [0, 1]); assert_eq!(context.checksum_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::() } } 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::() } } 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::(), 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::(), 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 reduce_index_for_match_state_selects_chain_and_hash3_tables() { let mut hash_table = [0u32; ZSTD_ROWSIZE]; let mut chain_table = [0u32; ZSTD_ROWSIZE]; let mut hash_table3 = [0u32; ZSTD_ROWSIZE]; hash_table[0] = ZSTD_DUBT_UNSORTED_MARK; chain_table[0] = ZSTD_DUBT_UNSORTED_MARK; hash_table3[0] = ZSTD_DUBT_UNSORTED_MARK; unsafe { ZSTD_rust_reduceIndexForMatchState( hash_table.as_mut_ptr(), 4, chain_table.as_mut_ptr(), 4, hash_table3.as_mut_ptr(), 4, ZSTD_BTLAZY2, ZSTD_RUST_PS_DISABLE, 0, 3, ); } assert_eq!(hash_table[0], 0); assert_eq!(chain_table[0], ZSTD_DUBT_UNSORTED_MARK); assert_eq!(hash_table3[0], 0); } #[test] fn copy_cdict_table_removes_short_cache_tags() { let source = [0x1234_56ff, 0xdead_beef, 0x0000_0100, u32::MAX]; let mut destination = [0u32; 4]; unsafe { ZSTD_rust_copyCDictTableIntoCCtx( destination.as_mut_ptr(), source.as_ptr(), source.len(), 1, ); } assert_eq!(destination, [0x0012_3456, 0x00de_adbe, 1, 0x00ff_ffff]); } #[test] fn copy_cdict_table_preserves_untagged_entries() { let source = [0, 2, 0x1234_5678, u32::MAX]; let mut destination = [0xa5a5_a5a5; 4]; unsafe { ZSTD_rust_copyCDictTableIntoCCtx( destination.as_mut_ptr(), source.as_ptr(), source.len(), 0, ); } assert_eq!(destination, source); } #[test] fn bitmix_and_hash_salt_match_the_c_arithmetic() { assert_eq!(bitmix(0x0123_4567_89ab_cdef, 8), 0xd498_d855_4e8d_d8cb); assert_eq!( advance_hash_salt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210), 0xe5ee_f172_e5ff_3e57 ); assert_eq!( ZSTD_rust_advanceHashSalt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210), 0xe5ee_f172_e5ff_3e57 ); let mut hash_salt = 0x0123_4567_89ab_cdef; unsafe { ZSTD_rust_advanceHashSaltInPlace(&mut hash_salt, 0xfedc_ba98_7654_3210) }; assert_eq!(hash_salt, 0xe5ee_f172_e5ff_3e57); } #[test] fn index_too_close_to_max_uses_a_strict_margin_boundary() { let threshold = ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN; assert!(!index_too_close_to_max(threshold)); assert!(index_too_close_to_max(threshold + 1)); assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold), 0); assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold + 1), 1); } #[derive(Default)] struct OverflowCorrectionTestContext { events: Vec<&'static str>, should_correct: c_int, correction: c_uint, loaded_dict_end: c_uint, dict_match_state: *const c_void, } unsafe fn overflow_correction_test_context( context: *mut c_void, ) -> &'static mut OverflowCorrectionTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn overflow_correction_test_need( context: *mut c_void, _src: *const c_void, _src_end: *const c_void, ) -> c_int { let context = unsafe { overflow_correction_test_context(context) }; context.events.push("need"); context.should_correct } unsafe extern "C" fn overflow_correction_test_correct( context: *mut c_void, _src: *const c_void, ) -> c_uint { let context = unsafe { overflow_correction_test_context(context) }; context.events.push("correct"); context.correction } unsafe extern "C" fn overflow_correction_test_mark_dirty(context: *mut c_void) { unsafe { overflow_correction_test_context(context) } .events .push("dirty"); } unsafe extern "C" fn overflow_correction_test_reduce( context: *mut c_void, _correction: c_uint, ) { unsafe { overflow_correction_test_context(context) } .events .push("reduce"); } unsafe extern "C" fn overflow_correction_test_mark_clean(context: *mut c_void) { unsafe { overflow_correction_test_context(context) } .events .push("clean"); } fn overflow_correction_test_state( context: &mut OverflowCorrectionTestContext, next_to_update: &mut c_uint, ) -> ZSTD_rust_overflowCorrectState { ZSTD_rust_overflowCorrectState { callback_context: (context as *mut OverflowCorrectionTestContext).cast(), next_to_update, need_correction: Some(overflow_correction_test_need), correct_overflow: Some(overflow_correction_test_correct), mark_tables_dirty: Some(overflow_correction_test_mark_dirty), reduce_index: Some(overflow_correction_test_reduce), mark_tables_clean: Some(overflow_correction_test_mark_clean), loaded_dict_end: &mut context.loaded_dict_end, dict_match_state: &mut context.dict_match_state, } } #[test] fn overflow_correction_preserves_order_and_saturates_next_to_update() { let mut context = OverflowCorrectionTestContext { should_correct: 1, correction: 10, loaded_dict_end: 33, dict_match_state: ptr::dangling(), ..Default::default() }; let mut next_to_update = 7; let state = overflow_correction_test_state(&mut context, &mut next_to_update); unsafe { ZSTD_rust_overflowCorrectIfNeeded(&state, ptr::null(), ptr::null()); } assert_eq!(next_to_update, 0); assert_eq!( context.events, ["need", "correct", "dirty", "reduce", "clean"] ); assert_eq!(context.loaded_dict_end, 0); assert!(context.dict_match_state.is_null()); } #[test] fn overflow_correction_skips_callbacks_when_window_is_safe() { let mut context = OverflowCorrectionTestContext::default(); let mut next_to_update = 123; let state = overflow_correction_test_state(&mut context, &mut next_to_update); unsafe { ZSTD_rust_overflowCorrectIfNeeded(&state, ptr::null(), ptr::null()); } assert_eq!(next_to_update, 123); assert_eq!(context.events, ["need"]); } #[test] fn window_correction_handles_current_cycle_start_boundary() { assert_eq!(window_correct_overflow(0x100, 3, 8), 0xf0); assert_eq!(window_correct_overflow(0x101, 3, 8), 0xf0); assert_eq!(window_correct_overflow(0x102, 3, 8), 0xf8); assert_eq!( ZSTD_rust_windowCorrectOverflow(0x100, 3, 8), window_correct_overflow(0x100, 3, 8) ); } #[test] fn window_correction_selects_larger_of_cycle_and_max_distance() { assert_eq!(window_correct_overflow(0x100, 5, 8), 0xc0); assert_eq!(window_correct_overflow(0x200, 5, 64), 0x1a0); } #[test] fn window_correction_wraps_u32_intermediates() { assert_eq!(window_correct_overflow(0, 0, 1), u32::MAX - 2); assert_eq!( window_correct_overflow(0x8000_0000, 31, 0x8000_0000), 0x8000_0000 ); assert_eq!( ZSTD_rust_windowCorrectOverflow(0x8000_0000, 31, 0x8000_0000), 0x8000_0000 ); } #[test] fn window_overflow_need_uses_the_explicit_current_max_boundary() { assert!(!window_need_overflow_correction(1024, false, 1024, false)); assert!(window_need_overflow_correction(1025, false, 1024, false)); assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1024, 1, 1024, 0), 0); assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1025, 0, 1024, 0), 1); } #[test] fn window_overflow_need_honors_frequent_dictionary_policy() { let can_correct = window_can_overflow_correct(27, 3, 16, 0, 0); let dictionary_still_valid = window_can_overflow_correct(27, 3, 16, 20, 0); assert!(can_correct); assert!(!dictionary_still_valid); assert!(window_need_overflow_correction(27, can_correct, 1024, true)); assert!(!window_need_overflow_correction( 27, dictionary_still_valid, 1024, true )); assert_eq!( ZSTD_rust_windowNeedOverflowCorrection(27, can_correct as u32, 1024, 1), 1 ); assert_eq!( ZSTD_rust_windowNeedOverflowCorrection(27, dictionary_still_valid as u32, 1024, 1,), 0 ); assert!(!window_need_overflow_correction(27, true, 1024, false)); } #[test] fn limit_next_to_update_preserves_threshold_and_bounded_catch_up() { assert_eq!(limit_next_to_update(884, 500), 500); assert_eq!(limit_next_to_update(885, 500), 884); assert_eq!(limit_next_to_update(1000, 500), 884); assert_eq!(limit_next_to_update(1200, 500), 1008); assert_eq!(ZSTD_rust_limitNextToUpdate(300, u32::MAX - 100), 283); } #[test] fn window_overflow_need_scales_the_frequency_with_correction_count() { assert!(window_can_overflow_correct(27, 3, 16, 0, 0)); assert!(!window_can_overflow_correct(27, 3, 16, 0, 1)); assert!(window_can_overflow_correct(53, 3, 16, 0, 1)); assert_eq!(ZSTD_rust_windowCanOverflowCorrect(53, 3, 16, 0, 1), 1); } #[test] fn window_overflow_need_preserves_u32_wrap_boundaries() { assert!(window_can_overflow_correct( 3, 31, 0x8000_0000, 0x8000_0000, 0, )); assert!(window_can_overflow_correct(27, 3, 16, 0, u32::MAX,)); assert_eq!( ZSTD_rust_windowCanOverflowCorrect(3, 31, 0x8000_0000, 0x8000_0000, 0), 1 ); } #[test] fn dict_too_big_uses_a_strict_chunk_size_boundary() { assert!(!dict_too_big(0)); assert!(!dict_too_big(ZSTD_CHUNKSIZE_MAX)); assert!(dict_too_big(ZSTD_CHUNKSIZE_MAX + 1)); assert_eq!(ZSTD_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::(); 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::(); 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::(); 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::(); let other_src = b"other".as_ptr().cast::(); 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::(); let other_src = b"other".as_ptr().cast::(); 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::(); 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::() = 0x11; *dst.cast::().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::::zeroed().assume_init() }; let mut curr_seq_store = unsafe { MaybeUninit::::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 = (0..12).collect(); let mut output = [0xa5u8; 32]; let result = unsafe { compress_block_split_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, 1, single_block_test_seams(), ) }; assert_eq!(result, 18); assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..8], &source[..5]); assert_eq!(&output[8 + ZSTD_BLOCK_HEADER_SIZE..result], &source[5..]); assert!(std::ptr::eq(prev_c_block, &prev_block)); } #[test] fn block_internal_body_collects_sequences_before_entropy_emission() { let (mut seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut prev_block = zeroed_state(); prev_block.entropy.fse.offcode_repeatMode = 2; let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let mut output_sequences = [ZSTD_Sequence { offset: 0xa5, litLength: 0xa5, matchLength: 0xa5, rep: 0xa5, }]; let mut seq_collector = SeqCollector { collectSequences: 1, seqStart: output_sequences.as_mut_ptr(), seqIndex: 0, maxSequences: output_sequences.len(), }; let state = block_internal_test_state( &mut seq_store, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, &mut seq_collector, 1, 0, ); let source = [0x5au8; 16]; let mut output = [0xa5u8; 16]; let result = unsafe { compress_block_internal_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, single_block_test_seams(), ) }; assert_eq!(result, 0); assert_eq!(seq_collector.seqIndex, 1); assert_eq!(output_sequences[0].litLength, 0); assert!(std::ptr::eq(prev_c_block, &next_block)); assert!(std::ptr::eq(next_c_block, &prev_block)); assert_eq!(prev_block.entropy.fse.offcode_repeatMode, 2); } #[test] fn block_internal_body_gates_rle_and_cleans_repeat_mode() { let (mut seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut prev_block = zeroed_state(); prev_block.entropy.fse.offcode_repeatMode = 2; let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let mut seq_collector = SeqCollector { collectSequences: 0, seqStart: ptr::null_mut(), seqIndex: 0, maxSequences: 0, }; let state = block_internal_test_state( &mut seq_store, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, &mut seq_collector, 1, 0, ); let source = [0x5au8; 16]; let mut output = [0xa5u8; 32]; let result = unsafe { compress_block_internal_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, single_block_test_seams(), ) }; assert_eq!(result, 1); assert_eq!(output[0], source[0]); assert_eq!(prev_block.entropy.fse.offcode_repeatMode, 1); } #[test] fn block_internal_body_propagates_entropy_error_through_repeat_cleanup() { let (mut seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut prev_block = zeroed_state(); prev_block.entropy.fse.offcode_repeatMode = 2; let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let mut seq_collector = SeqCollector { collectSequences: 0, seqStart: ptr::null_mut(), seqIndex: 0, maxSequences: 0, }; let state = block_internal_test_state( &mut seq_store, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, &mut seq_collector, -1, 0, ); let source = [0x5au8; 16]; let mut output = [0xa5u8; 32]; let result = unsafe { compress_block_internal_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, single_block_test_seams(), ) }; assert_eq!(result, ERROR(ZstdErrorCode::Generic)); assert_eq!(output, [0xa5u8; 32]); assert_eq!(prev_block.entropy.fse.offcode_repeatMode, 1); } #[test] fn single_block_body_propagates_entropy_errors_without_serializing() { let (seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut d_rep = [1, 4, 8]; let mut c_rep = d_rep; let mut prev_block = zeroed_state(); let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let mut seq_collector = SeqCollector { collectSequences: 0, seqStart: ptr::null_mut(), seqIndex: 0, maxSequences: 0, }; let state = single_block_test_state( &seq_store, &mut d_rep, &mut c_rep, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, &mut seq_collector, -1, 1, ); let source = *b"entropy error"; let mut output = [0xa5u8; 32]; let result = unsafe { compress_seq_store_single_block_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, 0, single_block_test_seams(), ) }; assert_eq!(result, ERROR(ZstdErrorCode::Generic)); assert_eq!(output, [0xa5u8; 32]); assert_eq!(d_rep, [1, 4, 8]); assert_eq!(c_rep, [1, 4, 8]); } #[test] fn single_block_body_serializes_raw_entropy_fallback() { let (seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut d_rep = [1, 4, 8]; let mut c_rep = d_rep; let mut prev_block = zeroed_state(); let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let mut seq_collector = SeqCollector { collectSequences: 0, seqStart: ptr::null_mut(), seqIndex: 0, maxSequences: 0, }; let state = single_block_test_state( &seq_store, &mut d_rep, &mut c_rep, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, &mut seq_collector, 0, 1, ); let source = *b"raw fallback"; let mut output = [0xa5u8; 32]; let result = unsafe { compress_seq_store_single_block_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, 0, single_block_test_seams(), ) }; assert_eq!(result, source.len() + ZSTD_BLOCK_HEADER_SIZE); assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..result], &source); } #[test] fn single_block_body_gates_rle_on_first_block_and_confirms_compressed_state() { for is_first_block in [0, 1] { let (seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut d_rep = [1, 4, 8]; let mut c_rep = d_rep; let mut prev_block = zeroed_state(); let mut next_block = zeroed_state(); next_block.entropy.fse.offcode_repeatMode = 2; let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let mut seq_collector = SeqCollector { collectSequences: 0, seqStart: ptr::null_mut(), seqIndex: 0, maxSequences: 0, }; let state = single_block_test_state( &seq_store, &mut d_rep, &mut c_rep, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, &mut seq_collector, 1, is_first_block, ); let source = [0x5au8; 16]; let mut output = [0xa5u8; 32]; let result = unsafe { compress_seq_store_single_block_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, 0, single_block_test_seams(), ) }; if is_first_block == 0 { assert_eq!(result, 4); assert_eq!(output[3], source[0]); assert!(std::ptr::eq(prev_c_block, &prev_block)); } else { assert_eq!(result, ZSTD_BLOCK_HEADER_SIZE + 2); assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..result], &[0x11, 0x22]); assert!(std::ptr::eq(prev_c_block, &next_block)); assert!(std::ptr::eq(next_c_block, &prev_block)); } let expected_repeat_mode = if is_first_block == 0 { 0 } else { 1 }; assert_eq!( unsafe { (*prev_c_block).entropy.fse.offcode_repeatMode }, expected_repeat_mode ); } } #[test] fn empty_sequence_block_preserves_header_and_capacity_contract() { let mut output = [0xa5; 4]; assert_eq!( unsafe { write_empty_sequence_block(output.as_mut_ptr(), output.len()) }, ZSTD_BLOCK_HEADER_SIZE ); assert_eq!(output, [1, 0, 0, 0]); let mut short_output = [0xa5; 3]; assert_eq!( unsafe { write_empty_sequence_block(short_output.as_mut_ptr(), short_output.len()) }, ERROR(ZstdErrorCode::DstSizeTooSmall) ); assert_eq!(short_output, [0xa5; 3]); } #[test] fn empty_sequence_literals_block_writes_only_the_three_byte_header() { let mut output = [0xa5; 4]; assert_eq!( unsafe { write_empty_sequence_literals_block(output.as_mut_ptr(), output.len()) }, ZSTD_BLOCK_HEADER_SIZE ); assert_eq!(output, [1, 0, 0, 0xa5]); let mut short_output = [0xa5; 2]; assert_eq!( unsafe { write_empty_sequence_literals_block(short_output.as_mut_ptr(), short_output.len()) }, ERROR(ZstdErrorCode::DstSizeTooSmall) ); assert_eq!(short_output, [0xa5; 2]); } #[test] fn invalidate_rep_codes_clears_all_entries() { let mut rep = [11u32, 22, 33]; unsafe { ZSTD_rust_invalidateRepCodes(rep.as_mut_ptr()) }; assert_eq!(rep, [0; ZSTD_REP_NUM]); } #[test] fn window_clear_writes_the_same_end_to_both_limits() { let mut low_limit = 11u32; let mut dict_limit = 22u32; unsafe { ZSTD_rust_windowClear(0x1234_5678, &mut low_limit, &mut dict_limit) }; assert_eq!(low_limit, 0x1234_5678); assert_eq!(dict_limit, 0x1234_5678); } #[cfg(target_pointer_width = "64")] #[test] fn window_clear_truncates_a_large_size_t_like_c_u32_cast() { let mut low_limit = 11u32; let mut dict_limit = 22u32; let end_t = 0x1_0000_0000usize + 0x89ab_cdef; unsafe { ZSTD_rust_windowClear(end_t, &mut low_limit, &mut dict_limit) }; assert_eq!(low_limit, 0x89ab_cdef); assert_eq!(dict_limit, 0x89ab_cdef); } #[test] fn invalidate_match_state_clears_window_and_private_fields() { let mut low_limit = 11u32; let mut dict_limit = 22u32; let mut next_to_update = 33u32; let mut loaded_dict_end = 44u32; let mut lit_length_sum = 55u32; let marker = 0x5au8; let mut dict_match_state = (&marker as *const u8).cast::(); unsafe { ZSTD_rust_invalidateMatchState( 0x1234_5678, &mut low_limit, &mut dict_limit, &mut next_to_update, &mut loaded_dict_end, &mut lit_length_sum, &mut dict_match_state, ) }; assert_eq!(low_limit, 0x1234_5678); assert_eq!(dict_limit, 0x1234_5678); assert_eq!(next_to_update, 0x1234_5678); assert_eq!(loaded_dict_end, 0); assert_eq!(lit_length_sum, 0); assert!(dict_match_state.is_null()); } #[test] fn window_update_leaves_empty_input_untouched() { let mut state = ZSTD_rust_windowUpdateState { nextSrc: ptr::null(), base: ptr::null(), dictBase: ptr::null(), dictLimit: 2, lowLimit: 2, }; let original = state; let result = unsafe { ZSTD_rust_windowUpdate(&mut state, ptr::null(), 0, 0) }; assert_eq!(result, 1); assert_eq!(state, original); } #[test] fn window_init_sets_empty_sentinel_and_dictionary_predicates() { let mut state = ZSTD_rust_windowUpdateState { nextSrc: ptr::null(), base: ptr::null(), dictBase: ptr::null(), dictLimit: 0, lowLimit: 0, }; unsafe { ZSTD_rust_windowInit(&mut state) }; assert_eq!(state.dictLimit, ZSTD_WINDOW_START_INDEX); assert_eq!(state.lowLimit, ZSTD_WINDOW_START_INDEX); assert_eq!(state.dictBase as usize, state.base as usize); assert_eq!( (state.nextSrc as usize).wrapping_sub(state.base as usize), ZSTD_WINDOW_START_INDEX as usize ); assert_eq!( ZSTD_rust_windowIsEmpty(state.nextSrc, state.base, state.dictLimit, state.lowLimit), 1 ); assert_eq!(ZSTD_rust_windowHasExtDict(10, 2), 1); assert_eq!(ZSTD_rust_windowHasExtDict(10, 10), 0); } #[test] fn window_enforce_max_dist_updates_limits_and_invalidates_dictionary() { let state = ZSTD_rust_windowEnforceMaxDist( 100, 64, ZSTD_rust_windowDictState { lowLimit: 2, dictLimit: 10, loadedDictEnd: 8, invalidate: 0, }, ); assert_eq!(state.lowLimit, 36); assert_eq!(state.dictLimit, 36); assert_eq!(state.loadedDictEnd, 0); assert_eq!(state.invalidate, 1); } #[test] fn window_dictionary_policy_preserves_valid_state_and_detects_mismatch() { let state = ZSTD_rust_windowEnforceMaxDist( 70, 64, ZSTD_rust_windowDictState { lowLimit: 2, dictLimit: 10, loadedDictEnd: 8, invalidate: 1, }, ); assert_eq!(state.lowLimit, 2); assert_eq!(state.dictLimit, 10); assert_eq!(state.loadedDictEnd, 8); assert_eq!(state.invalidate, 0); assert_eq!(ZSTD_rust_windowCheckDictValidity(70, 64, 8, 8), 0); assert_eq!(ZSTD_rust_windowCheckDictValidity(100, 64, 8, 8), 1); assert_eq!(ZSTD_rust_windowCheckDictValidity(70, 64, 8, 10), 1); } #[test] fn window_update_preserves_contiguous_input_and_clips_overlapping_extdict() { let storage = [0u8; 64]; let storage_start = storage.as_ptr() as usize; let base = unsafe { storage.as_ptr().add(16) }; let next_src = unsafe { base.add(2) }; let mut contiguous_state = ZSTD_rust_windowUpdateState { nextSrc: next_src.cast(), base: base.cast(), dictBase: base.cast(), dictLimit: 2, lowLimit: 2, }; let contiguous = unsafe { ZSTD_rust_windowUpdate(&mut contiguous_state, next_src.cast(), 3, 0) }; assert_eq!(contiguous, 1); assert_eq!(contiguous_state.nextSrc as usize, storage_start + 21); assert_eq!(contiguous_state.base as usize, storage_start + 16); assert_eq!(contiguous_state.dictBase as usize, storage_start + 16); assert_eq!(contiguous_state.dictLimit, 2); assert_eq!(contiguous_state.lowLimit, 2); let next_src = unsafe { base.add(6) }; let mut forced_state = ZSTD_rust_windowUpdateState { nextSrc: next_src.cast(), base: base.cast(), dictBase: base.cast(), dictLimit: 5, lowLimit: 2, }; let forced = unsafe { ZSTD_rust_windowUpdate(&mut forced_state, next_src.cast(), 3, 1) }; assert_eq!(forced, 0); assert_eq!(forced_state.base as usize, storage_start + 16); assert_eq!(forced_state.nextSrc as usize, storage_start + 25); assert_eq!(forced_state.dictBase as usize, storage_start + 16); assert_eq!(forced_state.dictLimit, 6); assert_eq!(forced_state.lowLimit, 6); let next_src = unsafe { base.add(20) }; let src = unsafe { base.add(4) }; let mut non_contiguous_state = ZSTD_rust_windowUpdateState { nextSrc: next_src.cast(), base: base.cast(), dictBase: base.cast(), dictLimit: 5, lowLimit: 2, }; let non_contiguous = unsafe { ZSTD_rust_windowUpdate(&mut non_contiguous_state, src.cast(), 10, 0) }; assert_eq!(non_contiguous, 0); assert_eq!(non_contiguous_state.base as usize, storage_start); assert_eq!(non_contiguous_state.nextSrc as usize, storage_start + 30); assert_eq!(non_contiguous_state.dictBase as usize, storage_start + 16); assert_eq!(non_contiguous_state.dictLimit, 20); assert_eq!(non_contiguous_state.lowLimit, 14); } #[derive(Default)] struct 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::() } } 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::() }; context.events.push("copy"); context.src_cctx = src_cctx; context.frame_params = unsafe { *f_params }; context.pledged_src_size = pledged_src_size; context.zbuff = zbuff; context.result } fn copy_cctx_test_state( context: &mut CopyCCtxTestContext, src_cctx: *const c_void, f_params: &ZSTD_frameParameters, pledged_src_size: &u64, zbuff: &c_int, ) -> ZSTD_rust_copyCCtxState { ZSTD_rust_copyCCtxState { callback_context: (context as *mut CopyCCtxTestContext).cast(), src_cctx, f_params, pledged_src_size, zbuff, copy_internal: Some(copy_cctx_test_internal), } } #[test] fn copy_cctx_converts_zero_pledge_to_unknown_and_clears_content_flag() { let mut context = CopyCCtxTestContext::default(); let f_params = ZSTD_frameParameters { contentSizeFlag: 1, checksumFlag: 1, noDictIDFlag: 1, }; let pledged_src_size = 0; let zbuff = 1; let src_cctx = 0x4000usize as *const c_void; let state = copy_cctx_test_state(&mut context, src_cctx, &f_params, &pledged_src_size, &zbuff); let result = unsafe { ZSTD_rust_copyCCtx(&state) }; assert_eq!(result, 0); assert_eq!(context.events, ["copy"]); assert_eq!(context.src_cctx, src_cctx); assert_eq!(context.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN); assert_eq!(context.zbuff, zbuff); assert_eq!(context.frame_params.contentSizeFlag, 0); assert_eq!(context.frame_params.checksumFlag, 1); assert_eq!(context.frame_params.noDictIDFlag, 1); } #[test] fn copy_cctx_forwards_nonzero_pledge_and_propagates_internal_error() { let mut context = CopyCCtxTestContext { result: ERROR(ZstdErrorCode::MemoryAllocation), ..CopyCCtxTestContext::default() }; let f_params = ZSTD_frameParameters { contentSizeFlag: 0, checksumFlag: 1, noDictIDFlag: 1, }; let pledged_src_size = 123; let zbuff = 7; let state = copy_cctx_test_state( &mut context, 0x4000usize as *const c_void, &f_params, &pledged_src_size, &zbuff, ); let result = unsafe { ZSTD_rust_copyCCtx(&state) }; assert_eq!(result, context.result); assert_eq!(context.events, ["copy"]); assert_eq!(context.pledged_src_size, pledged_src_size); assert_eq!(context.frame_params.contentSizeFlag, 1); assert_eq!(context.frame_params.checksumFlag, 1); assert_eq!(context.frame_params.noDictIDFlag, 1); } #[derive(Default)] struct CopyCCtxInternalTestContext { events: Vec<&'static str>, reset_result: usize, frame_params: ZSTD_frameParameters, pledged_src_size: u64, zbuff: c_int, } unsafe fn copy_cctx_internal_test_context( context: *mut c_void, ) -> &'static mut CopyCCtxInternalTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn copy_cctx_internal_test_reset( context: *mut c_void, _src_cctx: *const c_void, f_params: *const ZSTD_frameParameters, pledged_src_size: u64, zbuff: c_int, ) -> usize { let context = unsafe { copy_cctx_internal_test_context(context) }; context.events.push("reset"); context.frame_params = unsafe { *f_params }; context.pledged_src_size = pledged_src_size; context.zbuff = zbuff; context.reset_result } unsafe extern "C" fn copy_cctx_internal_test_mark_tables_dirty(context: *mut c_void) { unsafe { copy_cctx_internal_test_context(context) } .events .push("dirty"); } unsafe extern "C" fn copy_cctx_internal_test_copy_tables( context: *mut c_void, _src_cctx: *const c_void, ) { let context = unsafe { copy_cctx_internal_test_context(context) }; context.events.push("tables"); } unsafe extern "C" fn copy_cctx_internal_test_mark_tables_clean(context: *mut c_void) { unsafe { copy_cctx_internal_test_context(context) } .events .push("clean"); } fn copy_cctx_internal_test_state( context: &mut CopyCCtxInternalTestContext, src_cctx: *const c_void, f_params: &ZSTD_frameParameters, source_stage: *const c_int, destination_custom_mem: *mut c_void, source_custom_mem: *const c_void, destination_window: *mut c_void, source_window: *const c_void, destination_next_to_update: *mut c_uint, source_next_to_update: *const c_uint, destination_loaded_dict_end: *mut c_uint, source_loaded_dict_end: *const c_uint, destination_dict_id: *mut c_uint, source_dict_id: *const c_uint, destination_dict_content_size: *mut usize, source_dict_content_size: *const usize, destination_block_state: *mut *mut ZSTD_compressedBlockState_t, source_block_state: *const ZSTD_compressedBlockState_t, ) -> ZSTD_rust_copyCCtxInternalState { ZSTD_rust_copyCCtxInternalState { callback_context: (context as *mut CopyCCtxInternalTestContext).cast(), src_cctx, f_params, pledged_src_size: 123, source_stage, destination_custom_mem, source_custom_mem, reset: Some(copy_cctx_internal_test_reset), mark_tables_dirty: Some(copy_cctx_internal_test_mark_tables_dirty), copy_tables: Some(copy_cctx_internal_test_copy_tables), mark_tables_clean: Some(copy_cctx_internal_test_mark_tables_clean), destination_window, source_window, destination_next_to_update, source_next_to_update, destination_loaded_dict_end, source_loaded_dict_end, destination_dict_id, source_dict_id, destination_dict_content_size, source_dict_content_size, destination_block_state, source_block_state, zbuff: 7, } } #[test] fn copy_cctx_internal_runs_private_callbacks_in_original_order() { let mut context = CopyCCtxInternalTestContext::default(); let f_params = ZSTD_frameParameters { contentSizeFlag: 1, checksumFlag: 1, noDictIDFlag: 1, }; let source_stage = ZSTD_COMPRESSION_STAGE_INIT; let source_custom_mem = [0x11usize, 0x22, 0x33]; let mut destination_custom_mem = [0usize; 3]; let mut source_block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; source_block_state.rep = [15, 16, 17]; source_block_state.entropy.huf.repeatMode = 2; source_block_state.entropy.fse.offcode_repeatMode = FSE_REPEAT_VALID; let source_window = ZSTD_rust_copyWindowState { next_src: 0x1000usize as *const c_void, base: 0x2000usize as *const c_void, dict_base: 0x3000usize as *const c_void, dict_limit: 21, low_limit: 13, nb_overflow_corrections: 8, }; let mut destination_window = ZSTD_rust_copyWindowState { next_src: ptr::null(), base: ptr::null(), dict_base: ptr::null(), dict_limit: 0, low_limit: 0, nb_overflow_corrections: 0, }; let source_next_to_update = 34; let mut destination_next_to_update = 0; let source_loaded_dict_end = 55; let mut destination_loaded_dict_end = 0; let mut destination_block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let mut destination_block_state_slot = &mut destination_block_state as *mut ZSTD_compressedBlockState_t; let source_dict_id = 0x1234_5678; let mut destination_dict_id = 0; let source_dict_content_size = 9876; let mut destination_dict_content_size = 0; let state = copy_cctx_internal_test_state( &mut context, 0x5000usize as *const c_void, &f_params, &source_stage, (&mut destination_custom_mem as *mut [usize; 3]).cast(), (&source_custom_mem as *const [usize; 3]).cast(), (&mut destination_window as *mut ZSTD_rust_copyWindowState).cast(), (&source_window as *const ZSTD_rust_copyWindowState).cast(), &mut destination_next_to_update, &source_next_to_update, &mut destination_loaded_dict_end, &source_loaded_dict_end, &mut destination_dict_id, &source_dict_id, &mut destination_dict_content_size, &source_dict_content_size, &mut destination_block_state_slot, &source_block_state, ); let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) }; assert_eq!(result, 0); assert_eq!(context.events, ["reset", "dirty", "tables", "clean"]); assert_eq!(context.frame_params, f_params); assert_eq!(context.pledged_src_size, 123); assert_eq!(context.zbuff, 7); assert_eq!(destination_custom_mem, source_custom_mem); assert_eq!(destination_window, source_window); assert_eq!(destination_next_to_update, source_next_to_update); assert_eq!(destination_loaded_dict_end, source_loaded_dict_end); assert_eq!(destination_dict_id, source_dict_id); assert_eq!(destination_dict_content_size, source_dict_content_size); assert_eq!(destination_block_state.rep, source_block_state.rep); assert_eq!( destination_block_state.entropy.huf.repeatMode, source_block_state.entropy.huf.repeatMode ); assert_eq!( destination_block_state.entropy.fse.offcode_repeatMode, source_block_state.entropy.fse.offcode_repeatMode ); } #[test] fn copy_cctx_internal_rejects_stage_before_mutation() { let mut context = CopyCCtxInternalTestContext::default(); let f_params = ZSTD_frameParameters::default(); let source_stage = ZSTD_COMPRESSION_STAGE_ONGOING; let source_custom_mem = [0usize; 3]; let mut destination_custom_mem = [0usize; 3]; let mut destination_block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let mut destination_block_state_slot = &mut destination_block_state as *mut ZSTD_compressedBlockState_t; let source_block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let source_dict_id = 0x1234_5678; let mut destination_dict_id = 0; let source_dict_content_size = 9876; let mut destination_dict_content_size = 0; let state = copy_cctx_internal_test_state( &mut context, 0x5000usize as *const c_void, &f_params, &source_stage, (&mut destination_custom_mem as *mut [usize; 3]).cast(), (&source_custom_mem as *const [usize; 3]).cast(), ptr::dangling_mut(), ptr::dangling(), ptr::dangling_mut(), ptr::dangling(), ptr::dangling_mut(), ptr::dangling(), &mut destination_dict_id, &source_dict_id, &mut destination_dict_content_size, &source_dict_content_size, &mut destination_block_state_slot, &source_block_state, ); let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) }; assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); assert!(context.events.is_empty()); } #[test] fn copy_cctx_internal_propagates_reset_error_before_table_mutation() { let mut context = CopyCCtxInternalTestContext { reset_result: ERROR(ZstdErrorCode::MemoryAllocation), ..CopyCCtxInternalTestContext::default() }; let f_params = ZSTD_frameParameters::default(); let source_stage = ZSTD_COMPRESSION_STAGE_INIT; let source_custom_mem = [0usize; 3]; let mut destination_custom_mem = [0usize; 3]; let mut destination_block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let mut destination_block_state_slot = &mut destination_block_state as *mut ZSTD_compressedBlockState_t; let source_block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let source_dict_id = 0x1234_5678; let mut destination_dict_id = 0; let source_dict_content_size = 9876; let mut destination_dict_content_size = 0; let state = copy_cctx_internal_test_state( &mut context, 0x5000usize as *const c_void, &f_params, &source_stage, (&mut destination_custom_mem as *mut [usize; 3]).cast(), (&source_custom_mem as *const [usize; 3]).cast(), ptr::dangling_mut(), ptr::dangling(), ptr::dangling_mut(), ptr::dangling(), ptr::dangling_mut(), ptr::dangling(), &mut destination_dict_id, &source_dict_id, &mut destination_dict_content_size, &source_dict_content_size, &mut destination_block_state_slot, &source_block_state, ); let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) }; assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation)); assert_eq!(context.events, ["reset"]); } #[derive(Default)] struct CompressAdvancedTestContext { events: Vec<&'static str>, init_params: ZSTD_parameters, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, result: usize, } unsafe extern "C" fn compress_advanced_test_init( context: *mut c_void, params: *const ZSTD_parameters, ) { let context = unsafe { &mut *context.cast::() }; context.events.push("init"); context.init_params = unsafe { *params }; } unsafe extern "C" fn compress_advanced_test_internal( context: *mut c_void, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, ) -> usize { let context = unsafe { &mut *context.cast::() }; context.events.push("compress"); context.dst = dst; context.dst_capacity = dst_capacity; context.src = src; context.src_size = src_size; context.dict = dict; context.dict_size = dict_size; context.result } fn compress_advanced_test_state( context: &mut CompressAdvancedTestContext, ) -> ZSTD_rust_compressAdvancedState { ZSTD_rust_compressAdvancedState { callback_context: (context as *mut CompressAdvancedTestContext).cast(), init_params: Some(compress_advanced_test_init), compress_internal: Some(compress_advanced_test_internal), } } fn compress_advanced_test_params() -> ZSTD_parameters { ZSTD_parameters { cParams: ZSTD_compressionParameters { windowLog: 10, chainLog: 11, hashLog: 12, searchLog: 13, minMatch: 4, targetLength: 5, strategy: 1, }, fParams: ZSTD_frameParameters { contentSizeFlag: 1, checksumFlag: 1, noDictIDFlag: 0, }, } } #[test] fn compress_advanced_validates_then_initializes_and_compresses() { let mut context = CompressAdvancedTestContext { result: 17, ..CompressAdvancedTestContext::default() }; let state = compress_advanced_test_state(&mut context); let params = compress_advanced_test_params(); let mut dst = [0u8; 8]; let src = [1u8, 2, 3]; let dict = [4u8, 5]; let result = unsafe { ZSTD_rust_compressAdvanced( &state, dst.as_mut_ptr().cast(), dst.len(), src.as_ptr().cast(), src.len(), dict.as_ptr().cast(), dict.len(), ¶ms, ) }; assert_eq!(result, context.result); assert_eq!(context.events, ["init", "compress"]); assert_eq!(context.init_params, params); assert_eq!(context.dst, dst.as_mut_ptr().cast()); assert_eq!(context.dst_capacity, dst.len()); assert_eq!(context.src, src.as_ptr().cast()); assert_eq!(context.src_size, src.len()); assert_eq!(context.dict, dict.as_ptr().cast()); assert_eq!(context.dict_size, dict.len()); } #[test] fn compress_advanced_stops_before_callbacks_on_invalid_parameters() { let mut context = CompressAdvancedTestContext::default(); let state = compress_advanced_test_state(&mut context); let mut params = compress_advanced_test_params(); params.cParams.windowLog = 0; let result = unsafe { ZSTD_rust_compressAdvanced( &state, ptr::null_mut(), 0, ptr::null(), 0, ptr::null(), 0, ¶ms, ) }; assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound)); assert!(context.events.is_empty()); } #[derive(Default)] struct CompressBeginAdvancedTestContext { events: Vec<&'static str>, params: ZSTD_parameters, dict: *const c_void, dict_size: usize, cctx_params: *const c_void, pledged_src_size: u64, result: usize, } unsafe extern "C" fn compress_begin_advanced_test_init( context: *mut c_void, params: *const ZSTD_parameters, ) { let context = unsafe { &mut *context.cast::() }; context.events.push("init"); context.params = unsafe { *params }; } unsafe extern "C" fn compress_begin_advanced_test_begin( context: *mut c_void, dict: *const c_void, dict_size: usize, cctx_params: *const c_void, pledged_src_size: u64, ) -> usize { let context = unsafe { &mut *context.cast::() }; context.events.push("begin"); context.dict = dict; context.dict_size = dict_size; context.cctx_params = cctx_params; context.pledged_src_size = pledged_src_size; context.result } fn compress_begin_advanced_test_state( context: &mut CompressBeginAdvancedTestContext, ) -> ZSTD_rust_compressBeginAdvancedState { ZSTD_rust_compressBeginAdvancedState { cctx: (context as *mut CompressBeginAdvancedTestContext).cast(), cctx_params: (context as *mut CompressBeginAdvancedTestContext).cast(), init_params: compress_begin_advanced_test_init, begin: compress_begin_advanced_test_begin, } } #[test] fn compress_begin_advanced_validates_and_preserves_init_begin_order() { let mut context = CompressBeginAdvancedTestContext { result: 29, ..CompressBeginAdvancedTestContext::default() }; let state = compress_begin_advanced_test_state(&mut context); let params = compress_advanced_test_params(); let dict = [1u8, 2, 3]; let result = unsafe { ZSTD_rust_compressBeginAdvanced(&state, dict.as_ptr().cast(), dict.len(), ¶ms, 123) }; assert_eq!(result, context.result); assert_eq!(context.events, ["init", "begin"]); assert_eq!(context.params, params); assert_eq!(context.dict, dict.as_ptr().cast()); assert_eq!(context.dict_size, dict.len()); assert_eq!( context.cctx_params, (&context as *const CompressBeginAdvancedTestContext).cast() ); assert_eq!(context.pledged_src_size, 123); } #[test] fn compress_begin_advanced_stops_before_callbacks_on_invalid_parameters() { let mut context = CompressBeginAdvancedTestContext::default(); let state = compress_begin_advanced_test_state(&mut context); let mut params = compress_advanced_test_params(); params.cParams.windowLog = 0; let result = unsafe { ZSTD_rust_compressBeginAdvanced(&state, ptr::null(), 0, ¶ms, 0) }; assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound)); assert!(context.events.is_empty()); } #[derive(Default)] struct CompressUsingDictTestContext { events: Vec<&'static str>, init_params: ZSTD_parameters, init_level: c_int, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, result: usize, } unsafe extern "C" fn compress_using_dict_test_init( context: *mut c_void, params: *const ZSTD_parameters, compression_level: c_int, ) { let context = unsafe { &mut *context.cast::() }; context.events.push("init"); context.init_params = unsafe { *params }; context.init_level = compression_level; } unsafe extern "C" fn compress_using_dict_test_internal( context: *mut c_void, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, ) -> usize { let context = unsafe { &mut *context.cast::() }; context.events.push("compress"); context.dst = dst; context.dst_capacity = dst_capacity; context.src = src; context.src_size = src_size; context.dict = dict; context.dict_size = dict_size; context.result } fn compress_using_dict_test_state( context: &mut CompressUsingDictTestContext, exclusion_mask: &u32, ) -> ZSTD_rust_compressUsingDictState { ZSTD_rust_compressUsingDictState { callback_context: (context as *mut CompressUsingDictTestContext).cast(), exclusion_mask, init_params: Some(compress_using_dict_test_init), compress_internal: Some(compress_using_dict_test_internal), } } #[test] fn compress_using_dict_selects_params_and_normalizes_default_level() { let mut context = CompressUsingDictTestContext { result: 23, ..CompressUsingDictTestContext::default() }; let exclusion_mask = 0; let state = compress_using_dict_test_state(&mut context, &exclusion_mask); let mut dst = [0u8; 8]; let src = [1u8, 2, 3]; let dict = [4u8, 5]; let result = unsafe { ZSTD_rust_compressUsingDict( &state, dst.as_mut_ptr().cast(), dst.len(), src.as_ptr().cast(), src.len(), dict.as_ptr().cast(), dict.len(), 0, ) }; assert_eq!(result, context.result); assert_eq!(context.events, ["init", "compress"]); assert_eq!( context.init_params, ZSTD_rust_params_getParamsInternal( 0, src.len() as u64, dict.len(), ZSTD_RUST_CPM_NO_ATTACH_DICT, exclusion_mask, ) ); assert_eq!(context.init_level, ZSTD_rust_params_defaultCLevel()); assert_eq!(context.dst, dst.as_mut_ptr().cast()); assert_eq!(context.dst_capacity, dst.len()); assert_eq!(context.src, src.as_ptr().cast()); assert_eq!(context.src_size, src.len()); assert_eq!(context.dict, dict.as_ptr().cast()); assert_eq!(context.dict_size, dict.len()); } #[test] fn compress_using_dict_ignores_size_without_a_dictionary_buffer_for_params() { let mut context = CompressUsingDictTestContext::default(); let exclusion_mask = 0; let state = compress_using_dict_test_state(&mut context, &exclusion_mask); let expected = ZSTD_rust_params_getParamsInternal( 3, 0, 0, ZSTD_RUST_CPM_NO_ATTACH_DICT, exclusion_mask, ); let result = unsafe { ZSTD_rust_compressUsingDict( &state, ptr::null_mut(), 0, ptr::null(), 0, ptr::null(), 17, 3, ) }; assert_eq!(result, 0); assert_eq!(context.events, ["init", "compress"]); assert_eq!(context.init_params, expected); assert_eq!(context.dict, ptr::null()); assert_eq!(context.dict_size, 17); } #[derive(Default)] struct ResetCStreamTestContext { events: Vec<&'static str>, reset_result: usize, pledged_result: usize, pledged_src_size: u64, } unsafe extern "C" fn reset_cstream_test_reset(context: *mut c_void) -> usize { let context = unsafe { &mut *context.cast::() }; 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::() }; 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::() } } 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::(); 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::(); 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::(); 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::(); let mut context = InitCStreamUsingCDictAdvancedTestContext::default(); let state = init_cstream_advanced_test_state(&mut context); let result = unsafe { ZSTD_rust_initCStreamAdvanced(&state, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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::::zeroed().assume_init() }; let mut requested_params = unsafe { MaybeUninit::::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::::zeroed().assume_init() }; let mut requested_params = unsafe { MaybeUninit::::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::::zeroed().assume_init() }; let mut requested_params = unsafe { MaybeUninit::::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::::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::::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::::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::::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::::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::::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::(); let requested_pool = ptr::dangling_mut::(); 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::(); let mut pool = original_pool; let state = ref_thread_pool_test_state(&mut pool, ptr::null_mut(), ZSTD_CSTREAM_STAGE_LOAD); let result = unsafe { ZSTD_rust_refThreadPool(&state) }; assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); assert_eq!(pool, original_pool); } #[test] fn ref_thread_pool_rejects_null_state() { let result = unsafe { ZSTD_rust_refThreadPool(ptr::null()) }; assert_eq!(result, ERROR(ZstdErrorCode::Generic)); } #[derive(Default)] struct CreateCCtxTestContext { events: Vec<&'static str>, valid: c_int, allocation: *mut c_void, requested_size: usize, initialized: *mut c_void, } unsafe extern "C" fn create_cctx_test_validate(context: *mut c_void) -> c_int { let context = unsafe { &mut *context.cast::() }; context.events.push("validate"); context.valid } unsafe extern "C" fn create_cctx_test_allocate( context: *mut c_void, size: usize, ) -> *mut c_void { let context = unsafe { &mut *context.cast::() }; context.events.push("allocate"); context.requested_size = size; context.allocation } unsafe extern "C" fn create_cctx_test_init(context: *mut c_void, cctx: *mut c_void) { let context = unsafe { &mut *context.cast::() }; context.events.push("init"); context.initialized = cctx; } fn create_cctx_test_state(context: &mut CreateCCtxTestContext) -> ZSTD_rust_createCCtxState { ZSTD_rust_createCCtxState { callback_context: (context as *mut CreateCCtxTestContext).cast(), cctx_size: 123, validate_custom_mem: create_cctx_test_validate, allocate: create_cctx_test_allocate, init: create_cctx_test_init, } } #[test] fn create_cctx_validates_allocates_and_initializes_in_order() { let allocation = ptr::dangling_mut::(); let mut context = CreateCCtxTestContext { valid: 1, allocation, ..Default::default() }; let state = create_cctx_test_state(&mut context); let result = unsafe { ZSTD_rust_createCCtx(&state) }; assert_eq!(result, allocation); assert_eq!(context.events, ["validate", "allocate", "init"]); assert_eq!(context.requested_size, 123); assert_eq!(context.initialized, allocation); } #[test] fn create_cctx_stops_before_allocation_for_invalid_memory_callbacks() { let mut context = CreateCCtxTestContext { valid: 0, allocation: ptr::dangling_mut(), ..Default::default() }; let state = create_cctx_test_state(&mut context); let result = unsafe { ZSTD_rust_createCCtx(&state) }; assert!(result.is_null()); assert_eq!(context.events, ["validate"]); assert_eq!(context.requested_size, 0); assert!(context.initialized.is_null()); } #[test] fn create_cctx_stops_before_initialization_after_allocation_failure() { let mut context = CreateCCtxTestContext { valid: 1, ..Default::default() }; let state = create_cctx_test_state(&mut context); let result = unsafe { ZSTD_rust_createCCtx(&state) }; assert!(result.is_null()); assert_eq!(context.events, ["validate", "allocate"]); assert_eq!(context.requested_size, 123); assert!(context.initialized.is_null()); } #[derive(Default)] struct InitStaticCCtxTestContext { events: Vec<&'static str>, result: *mut c_void, } unsafe extern "C" fn init_static_cctx_test_init(context: *mut c_void) -> *mut c_void { let context = unsafe { &mut *context.cast::() }; context.events.push("init"); context.result } fn init_static_cctx_test_state( context: &mut InitStaticCCtxTestContext, workspace: *mut c_void, workspace_size: usize, cctx_size: usize, ) -> ZSTD_rust_initStaticCCtxState { ZSTD_rust_initStaticCCtxState { callback_context: (context as *mut InitStaticCCtxTestContext).cast(), workspace, workspace_size, cctx_size, init: init_static_cctx_test_init, } } #[test] fn init_static_cctx_rejects_an_unaligned_workspace_before_callback() { let mut context = InitStaticCCtxTestContext { result: ptr::dangling_mut(), ..Default::default() }; let mut workspace = [0usize; 2]; let unaligned_workspace = workspace.as_mut_ptr().cast::().wrapping_add(1).cast(); let state = init_static_cctx_test_state(&mut context, unaligned_workspace, usize::MAX, 0); let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; assert!(result.is_null()); assert!(context.events.is_empty()); } #[test] fn init_static_cctx_rejects_a_small_workspace_before_callback() { let mut context = InitStaticCCtxTestContext { result: ptr::dangling_mut(), ..Default::default() }; let mut workspace = [0usize; 1]; let state = init_static_cctx_test_state( &mut context, workspace.as_mut_ptr().cast(), size_of_val(&workspace), size_of_val(&workspace), ); let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; assert!(result.is_null()); assert!(context.events.is_empty()); } #[test] fn init_static_cctx_calls_the_initializer_after_workspace_validation() { let mut context = InitStaticCCtxTestContext { result: ptr::dangling_mut(), ..Default::default() }; let mut workspace = [0usize; 1]; let state = init_static_cctx_test_state( &mut context, workspace.as_mut_ptr().cast(), size_of_val(&workspace), size_of_val(&workspace) - 1, ); let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; assert_eq!(result, context.result); assert_eq!(context.events, ["init"]); } #[test] fn init_static_cctx_rejects_a_null_workspace() { let mut context = InitStaticCCtxTestContext { result: ptr::dangling_mut(), ..Default::default() }; let state = init_static_cctx_test_state(&mut context, ptr::null_mut(), usize::MAX, 0); let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; assert!(result.is_null()); assert!(context.events.is_empty()); } #[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::() }; context.events.push("content"); } unsafe extern "C" fn free_cctx_test_object(context: *mut c_void) { let context = unsafe { &mut *context.cast::() }; 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::() } } 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::() } } 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, received_fparams: Option, 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::() } } unsafe extern "C" fn set_params_test_check( context: *mut c_void, cparams: ZSTD_compressionParameters, ) -> usize { let context = unsafe { set_params_test_context(context) }; context.events.push("check"); context.received_cparams = Some(cparams); context.check_result } unsafe extern "C" fn set_params_test_set_f_params( context: *mut c_void, fparams: ZSTD_frameParameters, ) -> usize { let context = unsafe { set_params_test_context(context) }; context.events.push("f-params"); context.received_fparams = Some(fparams); context.f_params_result } unsafe extern "C" fn set_params_test_set_c_params( context: *mut c_void, cparams: ZSTD_compressionParameters, ) -> usize { let context = unsafe { set_params_test_context(context) }; context.events.push("c-params"); context.received_cparams = Some(cparams); context.c_params_result } fn set_params_test_state(context: &mut SetParamsTestContext) -> ZSTD_rust_setParamsState { ZSTD_rust_setParamsState { callback_context: (context as *mut SetParamsTestContext).cast(), check_c_params: set_params_test_check, set_f_params: set_params_test_set_f_params, set_c_params: set_params_test_set_c_params, } } fn set_params_test_params() -> ZSTD_parameters { ZSTD_parameters { cParams: ZSTD_compressionParameters { windowLog: 20, chainLog: 19, hashLog: 18, searchLog: 5, minMatch: 4, targetLength: 32, strategy: 3, }, fParams: ZSTD_frameParameters { contentSizeFlag: 1, checksumFlag: 0, noDictIDFlag: 1, }, } } #[test] fn set_params_preserves_check_frame_then_compression_order() { let params = set_params_test_params(); let mut context = SetParamsTestContext::default(); let state = set_params_test_state(&mut context); let result = unsafe { ZSTD_rust_setParams(&state, params) }; assert_eq!(result, 0); assert_eq!(context.events, ["check", "f-params", "c-params"]); assert_eq!(context.received_cparams, Some(params.cParams)); assert_eq!(context.received_fparams, Some(params.fParams)); } #[test] fn set_params_stops_before_frame_and_compression_after_check_error() { let params = set_params_test_params(); let mut context = SetParamsTestContext { check_result: ERROR(ZstdErrorCode::ParameterOutOfBound), ..SetParamsTestContext::default() }; let state = set_params_test_state(&mut context); let result = unsafe { ZSTD_rust_setParams(&state, params) }; assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound)); assert_eq!(context.events, ["check"]); assert!(context.received_fparams.is_none()); } #[test] fn set_params_stops_before_compression_after_frame_error() { let params = set_params_test_params(); let mut context = SetParamsTestContext { f_params_result: ERROR(ZstdErrorCode::StageWrong), ..SetParamsTestContext::default() }; let state = set_params_test_state(&mut context); let result = unsafe { ZSTD_rust_setParams(&state, params) }; assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); assert_eq!(context.events, ["check", "f-params"]); assert!(context.received_cparams.is_some()); } #[test] fn set_params_propagates_compression_error_after_prior_stages() { let params = set_params_test_params(); let mut context = SetParamsTestContext { c_params_result: ERROR(ZstdErrorCode::StageWrong), ..SetParamsTestContext::default() }; let state = set_params_test_state(&mut context); let result = unsafe { ZSTD_rust_setParams(&state, params) }; assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); assert_eq!(context.events, ["check", "f-params", "c-params"]); } #[test] fn pledged_src_size_writes_the_init_stage_value_plus_one() { let mut pledged_src_size_plus_one = 0; let result = unsafe { ZSTD_rust_setPledgedSrcSize( ZSTD_CSTREAM_STAGE_INIT, 123, &mut pledged_src_size_plus_one, ) }; assert_eq!(result, 0); assert_eq!(pledged_src_size_plus_one, 124); } #[test] fn pledged_src_size_wraps_at_u64_max() { let mut pledged_src_size_plus_one = 123; let result = unsafe { ZSTD_rust_setPledgedSrcSize( ZSTD_CSTREAM_STAGE_INIT, u64::MAX, &mut pledged_src_size_plus_one, ) }; assert_eq!(result, 0); assert_eq!(pledged_src_size_plus_one, 0); } #[test] fn pledged_src_size_rejects_non_init_stage_with_stage_wrong() { let mut pledged_src_size_plus_one = 123; let result = unsafe { ZSTD_rust_setPledgedSrcSize(1, 456, &mut pledged_src_size_plus_one) }; assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); assert_eq!( crate::errors::ERR_getErrorCode(result), ZstdErrorCode::StageWrong as i32 ); assert_eq!(pledged_src_size_plus_one, 123); } #[test] fn sizeof_local_dict_ignores_size_without_a_buffer() { assert_eq!(sizeof_local_dict(0, 37, 11), 11); assert_eq!(ZSTD_rust_sizeofLocalDict(0, 37, 11), 11); } #[test] fn sizeof_local_dict_adds_present_buffer_and_cdict_sizes() { assert_eq!(sizeof_local_dict(1, 37, 11), 48); assert_eq!(sizeof_local_dict(1, 0, 11), 11); assert_eq!(ZSTD_rust_sizeofLocalDict(1, 37, 11), 48); } #[test] fn sizeof_local_dict_wraps_like_c_size_t_addition() { assert_eq!(sizeof_local_dict(1, usize::MAX, 1), 0); assert_eq!(sizeof_local_dict(0, usize::MAX, usize::MAX), usize::MAX); } #[test] fn sizeof_cdict_handles_zero_components() { assert_eq!(sizeof_cdict(0, 0), 0); assert_eq!(ZSTD_rust_sizeofCDict(0, 0), 0); } #[test] fn sizeof_cdict_adds_components_in_order() { assert_eq!(sizeof_cdict(17, 25), 42); assert_eq!(ZSTD_rust_sizeofCDict(17, 25), 42); } #[test] fn sizeof_cdict_wraps_like_c_size_t_addition() { assert_eq!(sizeof_cdict(usize::MAX, 1), 0); assert_eq!(ZSTD_rust_sizeofCDict(usize::MAX, 1), 0); } #[test] fn sizeof_cctx_handles_zero_components() { assert_eq!(sizeof_cctx(0, 0, 0, 0), 0); assert_eq!(ZSTD_rust_sizeofCCtx(0, 0, 0, 0), 0); } #[test] fn sizeof_cctx_adds_components_in_order() { assert_eq!(sizeof_cctx(1, 2, 3, 4), 10); assert_eq!(ZSTD_rust_sizeofCCtx(1, 2, 3, 4), 10); } #[test] fn sizeof_cctx_wraps_like_c_size_t_addition() { assert_eq!(sizeof_cctx(usize::MAX, 1, 2, 3), 5); assert_eq!(ZSTD_rust_sizeofCCtx(usize::MAX, 1, 2, 3), 5); } #[test] fn estimate_workspace_size_handles_zero_components() { assert_eq!(estimate_workspace_size(0, 0, 0, 0, 0, 0, 0, 0, 0), 0); assert_eq!( ZSTD_rust_estimateWorkspaceSize(0, 0, 0, 0, 0, 0, 0, 0, 0), 0 ); } #[test] fn estimate_workspace_size_adds_components_in_order() { assert_eq!(estimate_workspace_size(1, 2, 3, 4, 5, 6, 7, 8, 9), 45); assert_eq!( ZSTD_rust_estimateWorkspaceSize(1, 2, 3, 4, 5, 6, 7, 8, 9), 45 ); } #[test] fn estimate_workspace_size_wraps_at_multiple_operand_positions() { assert_eq!( estimate_workspace_size(usize::MAX, 1, 0, 0, 0, 0, 0, 0, 0), 0 ); assert_eq!( estimate_workspace_size(0, usize::MAX, 1, 0, 0, 0, 0, 0, 0), 0 ); assert_eq!( estimate_workspace_size(0, 0, 0, 0, 0, usize::MAX, 1, 0, 0), 0 ); assert_eq!( ZSTD_rust_estimateWorkspaceSize(0, 0, 0, 0, 0, 0, 0, usize::MAX, 1), 0 ); } fn cctx_workspace_test_sizing() -> ZSTD_rustCCtxWorkspaceSizing { ZSTD_rustCCtxWorkspaceSizing { cctxSize: 32, compressedBlockStateSize: 24, seqDefSize: 8, rawSeqSize: 12, externalSequenceSize: 16, tmpWorkspaceSize: 64, wildcopyOverlength: 32, matchTSize: 4, optimalTSize: 8, asanRedzoneSize: 0, } } fn cctx_workspace_test_cparams() -> ZSTD_compressionParameters { ZSTD_compressionParameters { windowLog: 10, chainLog: 6, hashLog: 6, searchLog: 1, minMatch: 4, targetLength: 16, strategy: 3, } } fn cctx_workspace_test_estimate( sizing: &ZSTD_rustCCtxWorkspaceSizing, ldm_enable: c_int, ldm_hash_log: u32, ldm_bucket_size_log: u32, ldm_min_match_length: u32, is_static: c_int, buff_in_size: usize, buff_out_size: usize, use_sequence_producer: c_int, ) -> usize { unsafe { ZSTD_rust_estimateCCtxWorkspaceSize( cctx_workspace_test_cparams(), ldm_enable, ldm_hash_log, ldm_bucket_size_log, ldm_min_match_length, is_static, ZSTD_RUST_PS_DISABLE, buff_in_size, buff_out_size, 1000, use_sequence_producer, 0, sizing, ) } } fn cctx_reset_test_state( sizing: &ZSTD_rustCCtxWorkspaceSizing, plan: &mut ZSTD_rustCCtxResetPlan, pledged_src_size: u64, max_block_size: usize, ldm_enable: c_int, ldm_min_match_length: u32, in_buffer_buffered: c_int, out_buffer_buffered: c_int, use_sequence_producer: c_int, initialized: c_int, index_too_close: c_int, dict_too_big: c_int, ) -> ZSTD_rustCCtxResetState { ZSTD_rustCCtxResetState { cParams: cctx_workspace_test_cparams(), ldmEnable: ldm_enable, ldmHashLog: 4, ldmBucketSizeLog: 2, ldmMinMatchLength: ldm_min_match_length, isStatic: 0, useRowMatchFinder: ZSTD_RUST_PS_DISABLE, inBufferBuffered: in_buffer_buffered, outBufferBuffered: out_buffer_buffered, useSequenceProducer: use_sequence_producer, initialized, indexTooClose: index_too_close, dictTooBig: dict_too_big, pledgedSrcSize: pledged_src_size, maxBlockSize: max_block_size, sizing, plan, } } #[test] fn cctx_reset_plan_computes_window_buffers_and_sequence_capacities() { let sizing = cctx_workspace_test_sizing(); let mut plan = ZSTD_rustCCtxResetPlan::default(); let state = cctx_reset_test_state( &sizing, &mut plan, 1000, 512, ZSTD_RUST_PS_DISABLE, 0, 1, 1, 1, 1, 0, 0, ); assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0); assert_eq!(plan.windowSize, 1000); assert_eq!(plan.blockSize, 512); assert_eq!(plan.maxNbSeq, 512 / 3); assert_eq!(plan.buffInSize, 1000 + 512); assert_eq!( plan.buffOutSize, crate::zstd_compress_api::ZSTD_compressBound(512) + 1 ); assert_eq!( plan.maxNbExternalSeq, crate::zstd_compress_api::ZSTD_sequenceBound(512) ); assert_eq!(plan.maxNbLdmSeq, 0); assert_eq!(plan.needsIndexReset, 0); assert!(!ERR_isError(plan.neededSpace)); assert_ne!(plan.neededSpace, 0); } #[test] fn cctx_reset_plan_tracks_ldm_and_index_reset_policy() { let sizing = cctx_workspace_test_sizing(); let mut plan = ZSTD_rustCCtxResetPlan::default(); let mut state = cctx_reset_test_state( &sizing, &mut plan, u64::MAX, 2048, ZSTD_RUST_PS_ENABLE, 64, 0, 0, 0, 0, 0, 0, ); assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0); assert_eq!(plan.windowSize, 1 << 10); assert_eq!(plan.blockSize, 1 << 10); assert_eq!(plan.maxNbLdmSeq, (1 << 10) / 64); assert_eq!(plan.buffInSize, 0); assert_eq!(plan.buffOutSize, 0); assert_eq!(plan.needsIndexReset, 1); state.initialized = 1; state.indexTooClose = 1; assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0); assert_eq!(plan.needsIndexReset, 1); state.indexTooClose = 0; state.dictTooBig = 1; assert_eq!(unsafe { ZSTD_rust_planCCtxReset(&state) }, 0); assert_eq!(plan.needsIndexReset, 1); } #[test] fn cctx_reset_plan_rejects_unadjusted_enabled_ldm() { let sizing = cctx_workspace_test_sizing(); let mut plan = ZSTD_rustCCtxResetPlan::default(); let state = cctx_reset_test_state( &sizing, &mut plan, 1000, 512, ZSTD_RUST_PS_ENABLE, 0, 0, 0, 0, 1, 0, 0, ); assert_eq!( unsafe { ZSTD_rust_planCCtxReset(&state) }, ERROR(ZstdErrorCode::Generic) ); } #[derive(Debug, PartialEq)] enum ResetCCtxStorageTestEvent { Reserve(c_int, usize), Pointer(c_int), Size(c_int, usize), Int(c_int, c_int), Zero(usize), WindowInit, ResetExternalSequences, } #[derive(Default)] struct ResetCCtxStorageTestContext { events: Vec, reserve_count: usize, reserve_failure: bool, } unsafe fn reset_cctx_storage_test_context( context: *mut c_void, ) -> &'static mut ResetCCtxStorageTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn reset_cctx_storage_test_set_pointer( context: *mut c_void, kind: c_int, _pointer: *mut c_void, ) { let context = unsafe { reset_cctx_storage_test_context(context) }; context .events .push(ResetCCtxStorageTestEvent::Pointer(kind)); } unsafe extern "C" fn reset_cctx_storage_test_set_size( context: *mut c_void, kind: c_int, value: usize, ) { let context = unsafe { reset_cctx_storage_test_context(context) }; context .events .push(ResetCCtxStorageTestEvent::Size(kind, value)); } unsafe extern "C" fn reset_cctx_storage_test_set_int( context: *mut c_void, kind: c_int, value: c_int, ) { let context = unsafe { reset_cctx_storage_test_context(context) }; context .events .push(ResetCCtxStorageTestEvent::Int(kind, value)); } unsafe extern "C" fn reset_cctx_storage_test_reserve( context: *mut c_void, kind: c_int, size: usize, ) -> *mut c_void { let context = unsafe { reset_cctx_storage_test_context(context) }; context .events .push(ResetCCtxStorageTestEvent::Reserve(kind, size)); let pointer = (0x1000usize + context.reserve_count * 0x1000) as *mut c_void; context.reserve_count += 1; pointer } unsafe extern "C" fn reset_cctx_storage_test_reserve_failed(context: *mut c_void) -> c_int { let context = unsafe { reset_cctx_storage_test_context(context) }; c_int::from(context.reserve_failure) } unsafe extern "C" fn reset_cctx_storage_test_zero( context: *mut c_void, _pointer: *mut c_void, size: usize, ) { let context = unsafe { reset_cctx_storage_test_context(context) }; context.events.push(ResetCCtxStorageTestEvent::Zero(size)); } unsafe extern "C" fn reset_cctx_storage_test_window_init(context: *mut c_void) { let context = unsafe { reset_cctx_storage_test_context(context) }; context.events.push(ResetCCtxStorageTestEvent::WindowInit); } unsafe extern "C" fn reset_cctx_storage_test_reset_external_sequences(context: *mut c_void) { let context = unsafe { reset_cctx_storage_test_context(context) }; context .events .push(ResetCCtxStorageTestEvent::ResetExternalSequences); } fn reset_cctx_storage_test_state( context: &mut ResetCCtxStorageTestContext, ldm_enable: c_int, has_ext_seq_prod: c_int, block_size: usize, max_nb_seq: usize, max_nb_ldm_seq: usize, max_nb_external_seq: usize, buff_in_size: usize, buff_out_size: usize, ) -> ZSTD_rust_resetCCtxStorageState { ZSTD_rust_resetCCtxStorageState { callbackContext: (context as *mut ResetCCtxStorageTestContext).cast(), ldmEnable: ldm_enable, hasExtSeqProd: has_ext_seq_prod, hashLog: 4, bucketSizeLog: 2, blockSize: block_size, maxNbSeq: max_nb_seq, maxNbLdmSeq: max_nb_ldm_seq, maxNbExternalSeq: max_nb_external_seq, buffInSize: buff_in_size, buffOutSize: buff_out_size, seqDefSize: size_of::(), ldmEntrySize: 8, rawSeqSize: 12, externalSequenceSize: size_of::(), byteSize: 1, wildcopyOverlength: 8, bufferedPolicy: ZSTD_BM_BUFFERED, setPointer: Some(reset_cctx_storage_test_set_pointer), setSize: Some(reset_cctx_storage_test_set_size), setInt: Some(reset_cctx_storage_test_set_int), reserve: Some(reset_cctx_storage_test_reserve), reserveFailed: Some(reset_cctx_storage_test_reserve_failed), zero: Some(reset_cctx_storage_test_zero), windowInit: Some(reset_cctx_storage_test_window_init), resetExternalSequences: Some(reset_cctx_storage_test_reset_external_sequences), } } #[test] fn cctx_storage_preserves_ordinary_reservation_and_publication_order() { let mut context = ResetCCtxStorageTestContext::default(); let state = reset_cctx_storage_test_state( &mut context, ZSTD_RUST_PS_DISABLE, 0, 128, 4, 0, 0, 0, 0, ); assert_eq!(unsafe { ZSTD_rust_resetCCtxStorage(&state) }, 0); assert_eq!( context.events, [ ResetCCtxStorageTestEvent::Reserve( RESET_CCTX_RESERVE_ALIGNED64, 4 * size_of::() ), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_SEQ_START), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 136), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LITERALS), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_LIT, 128), ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_BUFFERED_POLICY, ZSTD_BM_BUFFERED), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 0), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_INPUT_BUFFER), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_INPUT_BUFFER, 0), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 0), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OUTPUT_BUFFER), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_OUTPUT_BUFFER, 0), ResetCCtxStorageTestEvent::ResetExternalSequences, ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_SEQ, 4), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LL_CODE), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_ML_CODE), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OF_CODE), ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_INITIALIZED, 1), ] ); } #[test] fn cctx_storage_preserves_ldm_and_external_sequence_order() { let mut context = ResetCCtxStorageTestContext::default(); let state = reset_cctx_storage_test_state( &mut context, ZSTD_RUST_PS_ENABLE, 1, 128, 4, 2, 3, 17, 19, ); let external_size = 3 * size_of::(); assert_eq!(unsafe { ZSTD_rust_resetCCtxStorage(&state) }, 0); assert_eq!( context.events, [ ResetCCtxStorageTestEvent::Reserve( RESET_CCTX_RESERVE_ALIGNED64, 4 * size_of::() ), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_SEQ_START), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_ALIGNED64, 16 * 8), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LDM_HASH), ResetCCtxStorageTestEvent::Zero(16 * 8), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_ALIGNED64, 2 * 12), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LDM_SEQUENCES), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_LDM_SEQ, 2), ResetCCtxStorageTestEvent::WindowInit, ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_ALIGNED64, external_size), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_EXTERNAL_SEQUENCES), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_EXTERNAL_SEQ_CAPACITY, 3), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 136), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LITERALS), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_LIT, 128), ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_BUFFERED_POLICY, ZSTD_BM_BUFFERED), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 17), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_INPUT_BUFFER), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_INPUT_BUFFER, 17), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 19), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OUTPUT_BUFFER), ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_OUTPUT_BUFFER, 19), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LDM_BUCKETS), ResetCCtxStorageTestEvent::Zero(4), ResetCCtxStorageTestEvent::ResetExternalSequences, ResetCCtxStorageTestEvent::Size(RESET_CCTX_SIZE_MAX_NB_SEQ, 4), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_LL_CODE), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_ML_CODE), ResetCCtxStorageTestEvent::Reserve(RESET_CCTX_RESERVE_BUFFER, 4), ResetCCtxStorageTestEvent::Pointer(RESET_CCTX_POINTER_OF_CODE), ResetCCtxStorageTestEvent::Int(RESET_CCTX_INT_INITIALIZED, 1), ] ); } #[test] fn cctx_storage_propagates_reservation_failure_before_publication() { let mut context = ResetCCtxStorageTestContext { reserve_failure: true, ..ResetCCtxStorageTestContext::default() }; let state = reset_cctx_storage_test_state( &mut context, ZSTD_RUST_PS_DISABLE, 0, 128, 4, 0, 0, 0, 0, ); assert_eq!( unsafe { ZSTD_rust_resetCCtxStorage(&state) }, ERROR(ZstdErrorCode::MemoryAllocation) ); assert_eq!( context.events, [ResetCCtxStorageTestEvent::Reserve( RESET_CCTX_RESERVE_ALIGNED64, 4 * size_of::() )] ); } #[derive(Debug, PartialEq)] enum ResetCCtxWorkspaceTestEvent { BumpOversizedDuration, FreeWorkspace, CreateWorkspace(usize), ReserveObject(usize), Pointer(c_int), Size(c_int, usize), ClearWorkspace, } #[derive(Default)] struct ResetCCtxWorkspaceTestContext { events: Vec, reserve_count: usize, create_result: usize, } unsafe fn reset_cctx_workspace_test_context( context: *mut c_void, ) -> &'static mut ResetCCtxWorkspaceTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn reset_cctx_workspace_test_bump(context: *mut c_void) { let context = unsafe { reset_cctx_workspace_test_context(context) }; context .events .push(ResetCCtxWorkspaceTestEvent::BumpOversizedDuration); } unsafe extern "C" fn reset_cctx_workspace_test_free(context: *mut c_void) { let context = unsafe { reset_cctx_workspace_test_context(context) }; context .events .push(ResetCCtxWorkspaceTestEvent::FreeWorkspace); } unsafe extern "C" fn reset_cctx_workspace_test_create( context: *mut c_void, needed_space: usize, ) -> usize { let context = unsafe { reset_cctx_workspace_test_context(context) }; context .events .push(ResetCCtxWorkspaceTestEvent::CreateWorkspace(needed_space)); context.create_result } unsafe extern "C" fn reset_cctx_workspace_test_reserve_object( context: *mut c_void, size: usize, ) -> *mut c_void { let context = unsafe { reset_cctx_workspace_test_context(context) }; context .events .push(ResetCCtxWorkspaceTestEvent::ReserveObject(size)); let pointer = (0x1000usize + context.reserve_count * 0x1000) as *mut c_void; context.reserve_count += 1; pointer } unsafe extern "C" fn reset_cctx_workspace_test_set_pointer( context: *mut c_void, kind: c_int, _pointer: *mut c_void, ) { let context = unsafe { reset_cctx_workspace_test_context(context) }; context .events .push(ResetCCtxWorkspaceTestEvent::Pointer(kind)); } unsafe extern "C" fn reset_cctx_workspace_test_set_size( context: *mut c_void, kind: c_int, value: usize, ) { let context = unsafe { reset_cctx_workspace_test_context(context) }; context .events .push(ResetCCtxWorkspaceTestEvent::Size(kind, value)); } unsafe extern "C" fn reset_cctx_workspace_test_clear(context: *mut c_void) { let context = unsafe { reset_cctx_workspace_test_context(context) }; context .events .push(ResetCCtxWorkspaceTestEvent::ClearWorkspace); } fn reset_cctx_workspace_test_state( context: &mut ResetCCtxWorkspaceTestContext, is_static: c_int, workspace_too_small: c_int, workspace_wasteful: c_int, needed_space: usize, compressed_block_state_size: usize, tmp_workspace_size: usize, needs_index_reset: &mut c_int, ) -> ZSTD_rust_resetCCtxWorkspaceState { ZSTD_rust_resetCCtxWorkspaceState { callbackContext: (context as *mut ResetCCtxWorkspaceTestContext).cast(), isStatic: is_static, workspaceTooSmall: workspace_too_small, workspaceWasteful: workspace_wasteful, neededSpace: needed_space, compressedBlockStateSize: compressed_block_state_size, tmpWorkspaceSize: tmp_workspace_size, needsIndexReset: needs_index_reset, bumpOversizedDuration: Some(reset_cctx_workspace_test_bump), freeWorkspace: Some(reset_cctx_workspace_test_free), createWorkspace: Some(reset_cctx_workspace_test_create), reserveObject: Some(reset_cctx_workspace_test_reserve_object), setPointer: Some(reset_cctx_workspace_test_set_pointer), setSize: Some(reset_cctx_workspace_test_set_size), clearWorkspace: Some(reset_cctx_workspace_test_clear), } } #[test] fn cctx_workspace_preserves_existing_workspace_and_clear_order() { let mut context = ResetCCtxWorkspaceTestContext::default(); let mut needs_index_reset = 0; let state = reset_cctx_workspace_test_state( &mut context, 0, 0, 0, 4096, 128, 64, &mut needs_index_reset, ); assert_eq!(unsafe { ZSTD_rust_resetCCtxWorkspace(&state) }, 0); assert_eq!(needs_index_reset, 0); assert_eq!( context.events, [ ResetCCtxWorkspaceTestEvent::BumpOversizedDuration, ResetCCtxWorkspaceTestEvent::ClearWorkspace, ] ); } #[test] fn cctx_workspace_preserves_resize_reservation_and_publication_order() { let mut context = ResetCCtxWorkspaceTestContext::default(); let mut needs_index_reset = 0; let state = reset_cctx_workspace_test_state( &mut context, 0, 1, 0, 4096, 128, 64, &mut needs_index_reset, ); assert_eq!(unsafe { ZSTD_rust_resetCCtxWorkspace(&state) }, 0); assert_eq!(needs_index_reset, RESET_CCTX_INDEX_RESET); assert_eq!( context.events, [ ResetCCtxWorkspaceTestEvent::BumpOversizedDuration, ResetCCtxWorkspaceTestEvent::FreeWorkspace, ResetCCtxWorkspaceTestEvent::CreateWorkspace(4096), ResetCCtxWorkspaceTestEvent::ReserveObject(128), ResetCCtxWorkspaceTestEvent::Pointer(RESET_CCTX_POINTER_PREV_CBLOCK), ResetCCtxWorkspaceTestEvent::ReserveObject(128), ResetCCtxWorkspaceTestEvent::Pointer(RESET_CCTX_POINTER_NEXT_CBLOCK), ResetCCtxWorkspaceTestEvent::ReserveObject(64), ResetCCtxWorkspaceTestEvent::Pointer(RESET_CCTX_POINTER_TMP_WORKSPACE), ResetCCtxWorkspaceTestEvent::Size(RESET_CCTX_SIZE_TMP_WORKSPACE, 64), ResetCCtxWorkspaceTestEvent::ClearWorkspace, ] ); } #[test] fn cctx_workspace_rejects_static_resize_before_mutating_workspace() { let mut context = ResetCCtxWorkspaceTestContext::default(); let mut needs_index_reset = 0; let state = reset_cctx_workspace_test_state( &mut context, 1, 1, 0, 4096, 128, 64, &mut needs_index_reset, ); assert_eq!( unsafe { ZSTD_rust_resetCCtxWorkspace(&state) }, ERROR(ZstdErrorCode::MemoryAllocation) ); assert_eq!(needs_index_reset, 0); assert!(context.events.is_empty()); } #[derive(Default)] struct ResetCCtxTailTestContext { events: Vec<&'static str>, match_result: usize, storage_result: usize, } unsafe fn reset_cctx_tail_test_context( context: *mut c_void, ) -> &'static mut ResetCCtxTailTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn reset_cctx_tail_test_initialize(context: *mut c_void) { let context = unsafe { reset_cctx_tail_test_context(context) }; context.events.push("initialize"); } unsafe extern "C" fn reset_cctx_tail_test_reset_match_state(context: *mut c_void) -> usize { let context = unsafe { reset_cctx_tail_test_context(context) }; context.events.push("reset-match-state"); context.match_result } unsafe extern "C" fn reset_cctx_tail_test_reset_storage(context: *mut c_void) -> usize { let context = unsafe { reset_cctx_tail_test_context(context) }; context.events.push("reset-storage"); context.storage_result } fn reset_cctx_tail_test_state( context: &mut ResetCCtxTailTestContext, compressed_block_state: *mut ZSTD_compressedBlockState_t, ) -> ZSTD_rust_resetCCtxTailState { ZSTD_rust_resetCCtxTailState { callbackContext: (context as *mut ResetCCtxTailTestContext).cast(), initialize: Some(reset_cctx_tail_test_initialize), compressedBlockState: compressed_block_state, resetMatchState: Some(reset_cctx_tail_test_reset_match_state), resetStorage: Some(reset_cctx_tail_test_reset_storage), } } #[test] fn cctx_reset_tail_preserves_post_workspace_callback_order() { let mut context = ResetCCtxTailTestContext::default(); let mut block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let state = reset_cctx_tail_test_state(&mut context, &mut block_state); assert_eq!(unsafe { ZSTD_rust_resetCCtxTail(&state) }, 0); assert_eq!( context.events, ["initialize", "reset-match-state", "reset-storage"] ); assert_eq!(block_state.rep, [1, 4, 8]); assert_eq!(block_state.entropy.huf.repeatMode, 0); } #[test] fn cctx_reset_tail_stops_before_storage_after_match_state_error() { let mut context = ResetCCtxTailTestContext { match_result: ERROR(ZstdErrorCode::StageWrong), ..ResetCCtxTailTestContext::default() }; let mut block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let state = reset_cctx_tail_test_state(&mut context, &mut block_state); assert_eq!( unsafe { ZSTD_rust_resetCCtxTail(&state) }, ERROR(ZstdErrorCode::StageWrong) ); assert_eq!(context.events, ["initialize", "reset-match-state"]); } #[test] fn cctx_reset_tail_propagates_storage_error_after_match_state() { let mut context = ResetCCtxTailTestContext { storage_result: ERROR(ZstdErrorCode::MemoryAllocation), ..ResetCCtxTailTestContext::default() }; let mut block_state = unsafe { MaybeUninit::::zeroed().assume_init() }; let state = reset_cctx_tail_test_state(&mut context, &mut block_state); assert_eq!( unsafe { ZSTD_rust_resetCCtxTail(&state) }, ERROR(ZstdErrorCode::MemoryAllocation) ); assert_eq!( context.events, ["initialize", "reset-match-state", "reset-storage"] ); } #[test] fn estimate_cctx_workspace_size_keeps_static_and_buffer_components_separate() { let sizing = cctx_workspace_test_sizing(); let ordinary = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 0, 0, 0, 0); let static_context = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 1, 0, 0, 0); let buffered = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 0, 17, 23, 0); let external = cctx_workspace_test_estimate(&sizing, 0, 0, 0, 0, 0, 0, 0, 1); assert_eq!(static_context - ordinary, sizing.cctxSize); assert_eq!(buffered - ordinary, 17 + 23); assert_eq!(external - ordinary, 6265); } #[test] fn estimate_cctx_workspace_size_adds_ldm_table_and_sequence_space_only_when_enabled() { let sizing = cctx_workspace_test_sizing(); let ordinary = cctx_workspace_test_estimate(&sizing, 0, 4, 2, 64, 0, 0, 0, 0); let ldm = cctx_workspace_test_estimate(&sizing, ZSTD_RUST_PS_ENABLE, 4, 2, 64, 0, 0, 0, 0); assert_eq!(ldm - ordinary, 324); } #[test] fn max_estimate_cctx_size_handles_zero_values() { assert_eq!(max_estimate_cctx_size(0, 0, 0, 0), 0); assert_eq!(ZSTD_rust_maxEstimateCCtxSize(0, 0, 0, 0), 0); } #[test] fn max_estimate_cctx_size_selects_the_largest_ordinary_value() { assert_eq!(max_estimate_cctx_size(17, 42, 9, 31), 42); assert_eq!(ZSTD_rust_maxEstimateCCtxSize(17, 42, 9, 31), 42); } #[test] fn max_estimate_cctx_size_preserves_equal_values() { assert_eq!(max_estimate_cctx_size(42, 42, 42, 42), 42); assert_eq!(ZSTD_rust_maxEstimateCCtxSize(42, 42, 42, 42), 42); } #[test] fn max_estimate_cctx_size_accepts_size_max() { assert_eq!(max_estimate_cctx_size(1, usize::MAX, 3, 2), usize::MAX); assert_eq!( ZSTD_rust_maxEstimateCCtxSize(1, usize::MAX, 3, 2), usize::MAX ); } #[test] fn max_estimate_cctx_size_compares_error_like_raw_values() { let smaller_error = ERROR(ZstdErrorCode::DstSizeTooSmall); let larger_error = ERROR(ZstdErrorCode::ParameterUnsupported); assert_eq!( max_estimate_cctx_size(128, smaller_error, 256, larger_error), larger_error ); assert_eq!( ZSTD_rust_maxEstimateCCtxSize(128, smaller_error, 256, larger_error), larger_error ); } #[test] fn 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::(), 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::(), 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) ); } }