#![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(clippy::missing_safety_doc)] #![allow(clippy::too_many_arguments)] //! First high-level compression slice. //! //! The layout-independent one-shot entry point in this module drives the //! already migrated compression leaves. `ZSTD_compressCCtx` uses the same //! path after a narrow C-owned context reset; the public context lifecycle //! remains C-owned because it shares the configuration-dependent private //! `ZSTD_CCtx_s` layout. `ZSTD_compress2` and the complete-input simple //! `ZSTD_compressStream2(..., ZSTD_e_end)` path dispatch through Rust while //! retaining the C implementation for advanced and partial-stream cases. use crate::common::MINMATCH; use crate::errors::{ERR_isError, ZstdErrorCode, ERROR}; #[cfg(not(test))] use crate::zstd_compress_api::ZSTD_compressBound; use crate::zstd_compress_frame::{ write_raw_block, ZSTD_rust_noCompressBlock, ZSTD_rust_rleCompressBlock, ZSTD_rust_writeBlockHeader, ZSTD_rust_writeFrameHeader, ZSTD_writeLastEmptyBlock, }; use crate::zstd_compress_literals::min_gain; use crate::zstd_compress_params::{ ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters, ZSTD_rust_params_adjustCParams, ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_selectCParams, ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE, }; use crate::zstd_compress_params_api::{ ZSTD_CCtxParams_setParameter, ZSTD_CCtx_params, ZSTD_rust_isUpdateAuthorized, }; use crate::zstd_compress_sequences::SeqDef; use crate::zstd_compress_stats::{ update_rep, SeqCollector, SeqStore_t, ZSTD_Sequence, ZSTD_SequencePosition, ZSTD_compressedBlockState_t, ZSTD_entropyCTables_t, ZSTD_rust_confirmRepcodesAndEntropyTables, ZSTD_rust_convertSequencesNoRepcodes, ZSTD_rust_copyBlockSequences, ZSTD_rust_countSeqStoreLiteralsBytes, ZSTD_rust_countSeqStoreMatchBytes, ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize, ZSTD_rust_entropyCompressSeqStore, ZSTD_rust_entropyCompressSeqStore_internal, ZSTD_rust_fastSequenceLengthSum, ZSTD_rust_finalizeOffBase, ZSTD_rust_get1BlockSummary, ZSTD_rust_isRLE, ZSTD_rust_maybeRLE, ZSTD_rust_postProcessSequenceProducerResult, ZSTD_rust_resetSeqStore, ZSTD_rust_seqStore_resolveOffCodes, ZSTD_rust_storeLastLiterals, ZSTD_rust_transferSequencesNoDelim, ZSTD_rust_transferSequencesWBlockDelim, ZSTD_rust_validateSeqStore, ZSTD_LLT_LITERAL_LENGTH, ZSTD_LLT_MATCH_LENGTH, }; use crate::zstd_compress_superblock::ZSTD_rust_compressSuperBlock; use std::ffi::c_void; use std::mem::{offset_of, size_of, MaybeUninit}; use std::os::raw::{c_int, c_longlong, c_uint}; use std::ptr; #[cfg(not(test))] unsafe extern "C" { fn ZSTD_createCCtx() -> *mut c_void; fn ZSTD_freeCCtx(cctx: *mut c_void) -> usize; fn ZSTD_rust_resetCCtxForSimpleCompression(cctx: *mut c_void) -> usize; fn ZSTD_rust_prepareCCtxForSimpleCompression( cctx: *mut c_void, src_size: usize, compression_level: c_int, ) -> usize; fn ZSTD_rust_resetCCtxForSimpleCompressionSession(cctx: *mut c_void) -> usize; fn ZSTD_rust_markSimpleCompression2Complete(cctx: *mut c_void); fn ZSTD_rust_simpleCompress2Level(cctx: *const c_void, src_size: usize) -> c_int; fn ZSTD_compress_usingDict( cctx: *mut c_void, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, dict: *const c_void, dict_size: usize, compression_level: c_int, ) -> usize; fn ZSTD_rust_simpleCompressStream2Level(cctx: *const c_void, src_size: usize) -> c_int; fn ZSTD_compress2_c( cctx: *mut c_void, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, ) -> usize; fn ZSTD_compressStream2_c( cctx: *mut c_void, output: *mut ZSTD_outBuffer, input: *mut ZSTD_inBuffer, end_op: c_int, ) -> usize; } const ZSTD_FAST: c_int = 1; const ZSTD_DFAST: c_int = 2; const ZSTD_REP_NUM: usize = 3; const ZSTD_BM_BUFFERED: c_int = 0; const ZSTD_BM_STABLE: c_int = 1; const ZSTD_SF_NO_BLOCK_DELIMITERS: c_int = 0; const ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS: c_int = 1; const ZSTD_BLOCKSIZE_MAX: usize = 1 << 17; const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX; const ZSTD_TARGET_CBLOCK_BSS_COMPRESS: c_int = 0; const ZSTD_BLOCK_HEADER_SIZE: usize = 3; const MIN_CBLOCK_SIZE: usize = 2; const MIN_COMPRESSIBLE_BLOCK_SIZE: usize = MIN_CBLOCK_SIZE + ZSTD_BLOCK_HEADER_SIZE + 1 + 1; const ZSTD_ROWSIZE: usize = 16; const ZSTD_CSTREAM_STAGE_INIT: c_int = 0; const ZSTD_WINDOW_START_INDEX: u32 = 2; const ZSTD_DUBT_UNSORTED_MARK: u32 = 1; const ZSTD_INDEXOVERFLOW_MARGIN: usize = 16usize << 20; const ZSTD_SHORT_CACHE_TAG_BITS: u32 = 8; const ZSTD_CURRENT_MAX: usize = if size_of::() == 8 { 3500usize << 20 } else { 2000usize << 20 }; const ZSTD_CHUNKSIZE_MAX: usize = u32::MAX as usize - ZSTD_CURRENT_MAX; const ZSTD_E_END: c_int = 2; const ZSTD_E_CONTINUE: c_int = 0; const ZSTD_E_FLUSH: c_int = 1; const ZSTD_C_WINDOW_LOG: c_int = 101; const ZSTD_C_HASH_LOG: c_int = 102; const ZSTD_C_CHAIN_LOG: c_int = 103; const ZSTD_C_SEARCH_LOG: c_int = 104; const ZSTD_C_MIN_MATCH: c_int = 105; const ZSTD_C_TARGET_LENGTH: c_int = 106; const ZSTD_C_STRATEGY: c_int = 107; const ZSTD_C_FORMAT: c_int = 10; const ZSTD_C_COMPRESSION_LEVEL: c_int = 100; const ZSTD_C_TARGET_C_BLOCK_SIZE: c_int = 130; const ZSTD_C_ENABLE_LDM: c_int = 160; const ZSTD_C_LDM_HASH_LOG: c_int = 161; const ZSTD_C_LDM_MIN_MATCH: c_int = 162; const ZSTD_C_LDM_BUCKET_SIZE_LOG: c_int = 163; const ZSTD_C_LDM_HASH_RATE_LOG: c_int = 164; const ZSTD_C_CONTENT_SIZE_FLAG: c_int = 200; const ZSTD_C_CHECKSUM_FLAG: c_int = 201; const ZSTD_C_DICT_ID_FLAG: c_int = 202; const ZSTD_C_NB_WORKERS: c_int = 400; const ZSTD_C_JOB_SIZE: c_int = 401; const ZSTD_C_OVERLAP_LOG: c_int = 402; const ZSTD_C_RSYNCABLE: c_int = 500; const ZSTD_C_FORCE_MAX_WINDOW: c_int = 1000; const ZSTD_C_FORCE_ATTACH_DICT: c_int = 1001; const ZSTD_C_LITERAL_COMPRESSION_MODE: c_int = 1002; const ZSTD_C_SRC_SIZE_HINT: c_int = 1004; const ZSTD_C_ENABLE_DEDICATED_DICT_SEARCH: c_int = 1005; const ZSTD_C_STABLE_IN_BUFFER: c_int = 1006; const ZSTD_C_STABLE_OUT_BUFFER: c_int = 1007; const ZSTD_C_BLOCK_DELIMITERS: c_int = 1008; const ZSTD_C_VALIDATE_SEQUENCES: c_int = 1009; const ZSTD_C_SPLIT_AFTER_SEQUENCES: c_int = 1010; const ZSTD_C_USE_ROW_MATCH_FINDER: c_int = 1011; const ZSTD_C_DETERMINISTIC_REF_PREFIX: c_int = 1012; const ZSTD_C_PREFETCH_CDICT_TABLES: c_int = 1013; const ZSTD_C_ENABLE_SEQ_PRODUCER_FALLBACK: c_int = 1014; const ZSTD_C_MAX_BLOCK_SIZE: c_int = 1015; const ZSTD_C_REPCODE_RESOLUTION: c_int = 1016; const ZSTD_C_BLOCK_SPLITTER_LEVEL: c_int = 1017; const ZSTD_RESET_SESSION_ONLY: c_int = 1; const ZSTD_RESET_PARAMETERS: c_int = 2; const ZSTD_RESET_SESSION_AND_PARAMETERS: c_int = 3; const ZSTD_CSTREAM_STAGE_LOAD: c_int = 1; const ZSTD_CSTREAM_STAGE_FLUSH: c_int = 2; const ZSTD_BSS_COMPRESS: c_int = 0; const ZSTD_BSS_NO_COMPRESS: c_int = 1; const FSE_REPEAT_CHECK: c_int = 1; const FSE_REPEAT_VALID: c_int = 2; type FrameChunkPrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize); type FrameChunkCompressFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize; type FrameChunkChecksumFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize); type BuildSeqStoreSkipFn = unsafe extern "C" fn(*mut c_void, usize); type BuildSeqStorePrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize); type BuildSeqStoreSelectFn = unsafe extern "C" fn( *mut c_void, *mut SeqStore_t, *mut u32, *const c_void, usize, *mut c_int, ) -> usize; /// Explicit projection for the sequence-store builder. /// /// Rust owns the threshold/reset/repcode/literal-store orchestration. The /// callbacks retain the private matchfinder, LDM, and external-producer /// fallback operations in C without passing `ZSTD_CCtx` across the ABI. #[repr(C)] pub struct ZSTD_rust_buildSeqStoreState { seq_store: *mut SeqStore_t, prev_c_block: *mut *mut ZSTD_compressedBlockState_t, next_c_block: *mut *mut ZSTD_compressedBlockState_t, callback_context: *mut c_void, min_match: c_uint, validate_seq_store: c_int, skip_small_block: BuildSeqStoreSkipFn, prepare_match_state: BuildSeqStorePrepareFn, select_sequences: BuildSeqStoreSelectFn, } const _: () = { assert!(offset_of!(ZSTD_rust_buildSeqStoreState, seq_store) == 0); assert!(offset_of!(ZSTD_rust_buildSeqStoreState, prev_c_block) == size_of::()); 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, select_sequences) == 6 * size_of::() + size_of::() + size_of::() ); assert!( size_of::() == 7 * size_of::() + size_of::() + 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; } } /// Explicit projection of the state used by `ZSTD_compress_frameChunk`. /// /// The Rust side owns the per-frame block loop and its savings/dispatch /// policy. The callback context is opaque to Rust and is only returned to /// C-owned callbacks, which retain private window, workspace, and CCtx /// layout-sensitive operations. #[repr(C)] pub struct ZSTD_rust_frameChunkState { callback_context: *mut c_void, tmp_workspace: *mut c_void, checksum_state: *mut c_void, is_first_block: *mut c_int, stage: *mut c_int, tmp_wksp_size: usize, block_size_max: usize, savings: c_longlong, pre_block_splitter_level: c_int, strategy: c_int, use_target_c_block_size: c_int, block_splitter_enabled: c_int, checksum_flag: c_int, ending_stage: c_int, prepare_block: FrameChunkPrepareFn, compress_target: FrameChunkCompressFn, compress_split: FrameChunkCompressFn, compress_internal: FrameChunkCompressFn, update_checksum: FrameChunkChecksumFn, } const _: () = { assert!(offset_of!(ZSTD_rust_frameChunkState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_frameChunkState, tmp_workspace) == size_of::()); 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_block) == 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 prepares match-state windows and invokes the selected block body /// through callbacks. Rust owns the block-size heuristic call, output /// framing, savings accounting, checksum sequencing, and frame-state update. #[allow(clippy::too_many_arguments)] unsafe fn compress_frame_chunk_body_with( state: &ZSTD_rust_frameChunkState, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, last_frame_chunk: c_uint, ) -> usize { if state.is_first_block.is_null() || state.stage.is_null() { return ERROR(ZstdErrorCode::Generic); } if state.checksum_flag != 0 && src_size != 0 { if state.checksum_state.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { (state.update_checksum)(state.checksum_state, src, src_size) }; } let mut remaining = src_size; let mut ip = src.cast::(); 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 { (state.prepare_block)(state.callback_context, ip.cast(), block_size) }; let c_size = if state.use_target_c_block_size != 0 { unsafe { (state.compress_target)( state.callback_context, op.cast(), remaining_capacity, ip.cast(), block_size, last_block, ) } } else if state.block_splitter_enabled != 0 { unsafe { (state.compress_split)( state.callback_context, op.cast(), remaining_capacity, ip.cast(), block_size, last_block, ) } } else { let compressed_size = unsafe { (state.compress_internal)( state.callback_context, op.add(ZSTD_BLOCK_HEADER_SIZE).cast(), remaining_capacity - ZSTD_BLOCK_HEADER_SIZE, ip.cast(), block_size, last_block, ) }; if ERR_isError(compressed_size) { return compressed_size; } if compressed_size == 0 { unsafe { ZSTD_rust_noCompressBlock( op.cast(), remaining_capacity, ip.cast(), block_size, last_block, ) } } else { unsafe { ZSTD_rust_writeBlockHeader(op.cast(), compressed_size, block_size, last_block) }; compressed_size + ZSTD_BLOCK_HEADER_SIZE } }; if ERR_isError(c_size) { return c_size; } savings = savings.wrapping_add((block_size as c_longlong).wrapping_sub(c_size as c_longlong)); unsafe { ip = ip.add(block_size); op = op.add(c_size); } remaining -= block_size; debug_assert!(c_size <= remaining_capacity); remaining_capacity = remaining_capacity.wrapping_sub(c_size); compressed_size_total = compressed_size_total.wrapping_add(c_size); unsafe { *state.is_first_block = 0 }; } if last_frame_chunk != 0 && compressed_size_total != 0 { unsafe { *state.stage = state.ending_stage }; } compressed_size_total } #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_compressFrameChunk( state: *const ZSTD_rust_frameChunkState, dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, last_frame_chunk: c_uint, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { compress_frame_chunk_body_with(&*state, dst, dst_capacity, src, src_size, last_frame_chunk) } } type CompressContinueHeaderFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize) -> usize; type CompressContinueWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize); type CompressContinueBlockFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize; /// Explicit projection for the high-level continue and deprecated block APIs. /// /// Rust owns stage transitions, frame-header sequencing, input progression, /// and dispatch between the already-migrated frame-chunk/block bodies. The /// opaque callback context remains in C, where callbacks update the private /// match-state windows and invoke the C-owned context-sensitive operations. #[repr(C)] pub struct ZSTD_rust_compressContinueState { callback_context: *mut c_void, write_frame_header: CompressContinueHeaderFn, update_window: CompressContinueWindowFn, correct_overflow: CompressContinueWindowFn, compress_frame_chunk: CompressContinueBlockFn, compress_block: CompressContinueBlockFn, stage: *mut c_int, consumed_src_size: *mut u64, produced_c_size: *mut u64, pledged_src_size_plus_one: u64, block_size_max: usize, check_block_size: c_int, } const _: () = { assert!(offset_of!(ZSTD_rust_compressContinueState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_compressContinueState, write_frame_header) == size_of::()); 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 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 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 CompressStreamInitLocalDictFn = unsafe extern "C" fn(*mut c_void) -> usize; type CompressStreamInitRefreshCDictFn = unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitDictionaryState); type CompressStreamInitClearPrefixFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitAssertDictionariesFn = unsafe extern "C" fn(*mut c_void, *const c_void); type CompressStreamInitSetLevelFn = unsafe extern "C" fn(*mut c_void, c_int); type CompressStreamInitDebugFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitGetPledgedFn = unsafe extern "C" fn(*mut c_void) -> u64; type CompressStreamInitSetPledgedFn = unsafe extern "C" fn(*mut c_void, usize); type CompressStreamInitGetCParamModeFn = unsafe extern "C" fn(*mut c_void, *const c_void, u64) -> c_int; type CompressStreamInitBuildCParamsFn = unsafe extern "C" fn(*mut c_void, u64, usize, c_int); type CompressStreamInitResolveParamsFn = unsafe extern "C" fn(*mut c_void, c_int); type CompressStreamInitGetNbWorkersFn = unsafe extern "C" fn(*mut c_void) -> c_uint; type CompressStreamInitSetNbWorkersFn = unsafe extern "C" fn(*mut c_void, c_uint); type CompressStreamInitHasExtSeqProdFn = unsafe extern "C" fn(*mut c_void) -> c_int; type CompressStreamInitTraceFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitGetMTContextFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void; type CompressStreamInitCreateMTContextFn = unsafe extern "C" fn(*mut c_void, c_uint) -> usize; type CompressStreamInitMTFn = unsafe extern "C" fn( *mut c_void, *mut c_void, *const ZSTD_rust_compressStreamInitDictionaryState, *mut c_void, u64, ) -> usize; type CompressStreamInitCommitMTFn = unsafe extern "C" fn( *mut c_void, *const ZSTD_rust_compressStreamInitDictionaryState, *mut c_void, ); type CompressStreamInitCheckCParamsFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitBeginFn = unsafe extern "C" fn( *mut c_void, *const ZSTD_rust_compressStreamInitDictionaryState, *mut c_void, u64, ) -> usize; type CompressStreamInitAssertOrdinaryFn = unsafe extern "C" fn(*mut c_void); type CompressStreamInitGetBufferModeFn = unsafe extern "C" fn(*mut c_void) -> c_int; type CompressStreamInitGetBlockSizeFn = unsafe extern "C" fn(*mut c_void) -> usize; type CompressStreamInitCommitOrdinaryFn = unsafe extern "C" fn(*mut c_void, usize); /// Scalar dictionary snapshot used by transparent stream initialization. /// /// The prefix is populated by C before local-dictionary initialization, which /// preserves the original single-use snapshot order. C refreshes only the /// CDict fields after local-dictionary initialization because that operation /// may create the local CDict. #[repr(C)] pub struct ZSTD_rust_compressStreamInitDictionaryState { prefix_dict: *const c_void, prefix_dict_size: usize, prefix_dict_content_type: c_int, cdict: *const c_void, cdict_is_local: c_int, cdict_compression_level: c_int, cdict_dict_content_size: usize, } const _: () = { assert!(offset_of!(ZSTD_rust_compressStreamInitDictionaryState, prefix_dict) == 0); assert!( offset_of!( ZSTD_rust_compressStreamInitDictionaryState, prefix_dict_size ) == size_of::() ); 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, init_local_dict: CompressStreamInitLocalDictFn, refresh_cdict: CompressStreamInitRefreshCDictFn, clear_prefix: CompressStreamInitClearPrefixFn, assert_dictionaries: CompressStreamInitAssertDictionariesFn, set_compression_level: CompressStreamInitSetLevelFn, debug_init: CompressStreamInitDebugFn, get_pledged_src_size_plus_one: CompressStreamInitGetPledgedFn, set_pledged_src_size: CompressStreamInitSetPledgedFn, get_cparam_mode: CompressStreamInitGetCParamModeFn, build_cparams: CompressStreamInitBuildCParamsFn, resolve_params: CompressStreamInitResolveParamsFn, get_nb_workers: CompressStreamInitGetNbWorkersFn, set_nb_workers: CompressStreamInitSetNbWorkersFn, has_ext_seq_prod: CompressStreamInitHasExtSeqProdFn, trace_begin: CompressStreamInitTraceFn, get_mt_context: CompressStreamInitGetMTContextFn, create_mt_context: CompressStreamInitCreateMTContextFn, init_mt: CompressStreamInitMTFn, commit_mt: CompressStreamInitCommitMTFn, check_cparams: CompressStreamInitCheckCParamsFn, compress_begin: CompressStreamInitBeginFn, assert_ordinary: CompressStreamInitAssertOrdinaryFn, get_buffer_mode: CompressStreamInitGetBufferModeFn, get_block_size: CompressStreamInitGetBlockSizeFn, commit_ordinary: CompressStreamInitCommitOrdinaryFn, } const _: () = { assert!(offset_of!(ZSTD_rust_compressStreamInitState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_compressStreamInitState, params) == size_of::()); 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 { 256 } else { 128 } ); }; #[inline] unsafe fn compress_stream_init_body_with(state: &ZSTD_rust_compressStreamInitState) -> usize { if state.params.is_null() || state.dictionaries.is_null() { return ERROR(ZstdErrorCode::Generic); } let result = unsafe { (state.init_local_dict)(state.callback_context) }; if ERR_isError(result) { return result; } unsafe { (state.refresh_cdict)(state.callback_context, state.dictionaries) }; let dictionaries = unsafe { &*state.dictionaries }; unsafe { (state.clear_prefix)(state.callback_context) }; unsafe { (state.assert_dictionaries)(state.callback_context, dictionaries.prefix_dict) }; if !dictionaries.cdict.is_null() && dictionaries.cdict_is_local == 0 { unsafe { (state.set_compression_level)(state.params, dictionaries.cdict_compression_level) }; } unsafe { (state.debug_init)(state.callback_context) }; if state.end_op == ZSTD_E_END { unsafe { (state.set_pledged_src_size)(state.callback_context, state.in_size) }; } let pledged_src_size_plus_one = unsafe { (state.get_pledged_src_size_plus_one)(state.callback_context) }; let pledged_src_size = pledged_src_size_plus_one.wrapping_sub(1); let dict_size = if !dictionaries.prefix_dict.is_null() { dictionaries.prefix_dict_size } else if !dictionaries.cdict.is_null() { dictionaries.cdict_dict_content_size } else { 0 }; let mode = unsafe { (state.get_cparam_mode)(state.params, dictionaries.cdict, pledged_src_size) }; unsafe { (state.build_cparams)(state.params, pledged_src_size, dict_size, mode); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_LDM); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE); (state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH); } if state.multithreaded != 0 { let has_ext_seq_prod = unsafe { (state.has_ext_seq_prod)(state.params) }; let mut nb_workers = unsafe { (state.get_nb_workers)(state.params) }; if has_ext_seq_prod != 0 && nb_workers >= 1 { return ERROR(ZstdErrorCode::ParameterCombinationUnsupported); } if pledged_src_size <= state.mt_job_size_min as u64 { nb_workers = 0; unsafe { (state.set_nb_workers)(state.params, 0) }; } if nb_workers > 0 { unsafe { (state.trace_begin)(state.callback_context) }; if unsafe { (state.get_mt_context)(state.callback_context) }.is_null() { let result = unsafe { (state.create_mt_context)(state.callback_context, nb_workers) }; if ERR_isError(result) { return result; } } let result = unsafe { (state.init_mt)( state.callback_context, (state.get_mt_context)(state.callback_context), dictionaries, state.params, pledged_src_size, ) }; if ERR_isError(result) { return result; } unsafe { (state.commit_mt)(state.callback_context, dictionaries, state.params) }; return 0; } } unsafe { (state.check_cparams)(state.params) }; let result = unsafe { (state.compress_begin)( state.callback_context, dictionaries, state.params, pledged_src_size, ) }; if ERR_isError(result) { return result; } unsafe { (state.assert_ordinary)(state.callback_context) }; let buffer_mode = unsafe { (state.get_buffer_mode)(state.callback_context) }; let block_size = unsafe { (state.get_block_size)(state.callback_context) }; let in_buff_target = if buffer_mode == ZSTD_BM_BUFFERED { block_size.wrapping_add(usize::from(block_size as u64 == pledged_src_size)) } else { 0 }; unsafe { (state.commit_ordinary)(state.callback_context, in_buff_target) }; 0 } /// Rust-owned high-level policy for transparent stream initialization. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_compressStreamInit( state: *const ZSTD_rust_compressStreamInitState, ) -> usize { if state.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { compress_stream_init_body_with(&*state) } } #[inline] unsafe fn stream_limit_copy( dst: *mut c_void, dst_capacity: usize, src: *const c_void, src_size: usize, ) -> usize { let length = dst_capacity.min(src_size); if length != 0 { unsafe { ptr::copy_nonoverlapping(src.cast::(), 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 boundary. C callbacks perform the operations /// which need the private matchfinder or CCtx parameter/function-pointer state. #[allow(clippy::too_many_arguments)] unsafe fn build_seq_store_body_with( state: &ZSTD_rust_buildSeqStoreState, src: *const c_void, src_size: usize, ) -> usize { if state.seq_store.is_null() || state.skip_small_block as usize == 0 || state.prepare_match_state as usize == 0 || state.select_sequences as usize == 0 { return ERROR(ZstdErrorCode::Generic); } if src_size > ZSTD_BLOCKSIZE_MAX { return ERROR(ZstdErrorCode::SrcSizeWrong); } if src_size < MIN_COMPRESSIBLE_BLOCK_SIZE { unsafe { (state.skip_small_block)(state.callback_context, src_size) }; return ZSTD_BSS_NO_COMPRESS as usize; } if src.is_null() || state.prev_c_block.is_null() || state.next_c_block.is_null() { return ERROR(ZstdErrorCode::Generic); } let prev_c_block = unsafe { *state.prev_c_block }; let next_c_block = unsafe { *state.next_c_block }; if prev_c_block.is_null() || next_c_block.is_null() { return ERROR(ZstdErrorCode::Generic); } unsafe { ZSTD_rust_resetSeqStore(state.seq_store) }; unsafe { (state.prepare_match_state)(state.callback_context, src, src_size); } unsafe { ptr::copy_nonoverlapping( (*prev_c_block).rep.as_ptr(), (*next_c_block).rep.as_mut_ptr(), ZSTD_REP_NUM, ); } let mut seq_store_complete = 0; let last_literals_size = unsafe { (state.select_sequences)( state.callback_context, state.seq_store, (*next_c_block).rep.as_mut_ptr(), src, src_size, &mut seq_store_complete, ) }; if ERR_isError(last_literals_size) { return last_literals_size; } if seq_store_complete != 0 { return ZSTD_BSS_COMPRESS as usize; } if last_literals_size > src_size { return ERROR(ZstdErrorCode::Generic); } unsafe { ZSTD_rust_storeLastLiterals( state.seq_store, src.cast::().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 } #[inline] fn next_input_size_hint( in_buffer_mode: c_int, block_size_max: usize, stable_in_not_consumed: usize, in_buff_target: usize, in_buff_pos: usize, ) -> usize { if in_buffer_mode == ZSTD_BM_STABLE { return block_size_max.wrapping_sub(stable_in_not_consumed); } let hint_in_size = in_buff_target.wrapping_sub(in_buff_pos); if hint_in_size == 0 { block_size_max } else { hint_in_size } } /// Return the next input size required by the C streaming state machine. #[no_mangle] pub extern "C" fn ZSTD_rust_nextInputSizeHint( in_buffer_mode: c_int, block_size_max: usize, stable_in_not_consumed: usize, in_buff_target: usize, in_buff_pos: usize, ) -> usize { next_input_size_hint( in_buffer_mode, block_size_max, stable_in_not_consumed, in_buff_target, in_buff_pos, ) } #[inline] fn mt_next_input_size_hint(target_section_size: usize, in_buff_filled: usize) -> usize { let hint_in_size = target_section_size.wrapping_sub(in_buff_filled); if hint_in_size == 0 { target_section_size } else { hint_in_size } } /// Return the next input size required by the C multithreaded streaming state. #[no_mangle] pub extern "C" fn ZSTDMT_rust_nextInputSizeHint( target_section_size: usize, in_buff_filled: usize, ) -> usize { mt_next_input_size_hint(target_section_size, in_buff_filled) } #[inline] fn mt_sizeof_cctx( mtctx_size: usize, factory_size: usize, buffer_pool_size: usize, jobs_size: usize, cctx_pool_size: usize, seq_pool_size: usize, cdict_size: usize, round_buff_size: usize, ) -> usize { mtctx_size .wrapping_add(factory_size) .wrapping_add(buffer_pool_size) .wrapping_add(jobs_size) .wrapping_add(cctx_pool_size) .wrapping_add(seq_pool_size) .wrapping_add(cdict_size) .wrapping_add(round_buff_size) } /// Aggregate C-owned multithreaded context size components with C `size_t` /// wrapping semantics. #[no_mangle] pub extern "C" fn ZSTDMT_rust_sizeofCCtx( mtctx_size: usize, factory_size: usize, buffer_pool_size: usize, jobs_size: usize, cctx_pool_size: usize, seq_pool_size: usize, cdict_size: usize, round_buff_size: usize, ) -> usize { mt_sizeof_cctx( mtctx_size, factory_size, buffer_pool_size, jobs_size, cctx_pool_size, seq_pool_size, cdict_size, round_buff_size, ) } #[inline] fn bitmix(mut val: u64, len: u64) -> u64 { val ^= val.rotate_right(49) ^ val.rotate_right(24); val = val.wrapping_mul(0x9FB21C651E98DF25); val ^= (val >> 35).wrapping_add(len); val = val.wrapping_mul(0x9FB21C651E98DF25); val ^ (val >> 28) } #[inline] fn advance_hash_salt(hash_salt: u64, hash_salt_entropy: u64) -> u64 { bitmix(hash_salt, 8) ^ bitmix(hash_salt_entropy, 4) } /// Advance the row-matchfinder salt without exposing C's private match state. #[no_mangle] pub extern "C" fn ZSTD_rust_advanceHashSalt(hash_salt: u64, hash_salt_entropy: u64) -> u64 { advance_hash_salt(hash_salt, hash_salt_entropy) } #[inline] fn index_too_close_to_max(next_src_base_offset: usize) -> bool { next_src_base_offset > ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN } /// Return whether a scalar C window offset is within the overflow margin. #[no_mangle] pub extern "C" fn ZSTD_rust_indexTooCloseToMax(next_src_base_offset: usize) -> c_int { index_too_close_to_max(next_src_base_offset) as c_int } #[inline] fn dict_too_big(loaded_dict_size: usize) -> bool { loaded_dict_size > ZSTD_CHUNKSIZE_MAX } /// Return whether a dictionary exceeds the maximum loadable chunk size. #[no_mangle] pub extern "C" fn ZSTD_rust_dictTooBig(loaded_dict_size: usize) -> c_int { dict_too_big(loaded_dict_size) as c_int } #[inline] fn sizeof_local_dict(dict_buffer_present: c_int, dict_size: usize, cdict_size: usize) -> usize { let buffer_size = if dict_buffer_present != 0 { dict_size } else { 0 }; buffer_size.wrapping_add(cdict_size) } /// Add the C-owned local-dictionary sizes with C `size_t` wrapping semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_sizeofLocalDict( dict_buffer_present: c_int, dict_size: usize, cdict_size: usize, ) -> usize { sizeof_local_dict(dict_buffer_present, dict_size, cdict_size) } #[inline] fn sizeof_cdict(object_size: usize, workspace_size: usize) -> usize { object_size.wrapping_add(workspace_size) } /// Aggregate C-owned dictionary size components with C `size_t` wrapping /// semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_sizeofCDict(objectSize: usize, workspaceSize: usize) -> usize { sizeof_cdict(objectSize, workspaceSize) } #[inline] fn sizeof_cctx( object_size: usize, workspace_size: usize, local_dict_size: usize, mtctx_size: usize, ) -> usize { object_size .wrapping_add(workspace_size) .wrapping_add(local_dict_size) .wrapping_add(mtctx_size) } /// Aggregate C-owned context size components with C `size_t` wrapping /// semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_sizeofCCtx( object_size: usize, workspace_size: usize, local_dict_size: usize, mtctx_size: usize, ) -> usize { sizeof_cctx(object_size, workspace_size, local_dict_size, mtctx_size) } #[inline] fn estimate_workspace_size( cctx_space: usize, tmp_work_space: usize, block_state_space: usize, ldm_space: usize, ldm_seq_space: usize, match_state_size: usize, token_space: usize, buffer_space: usize, external_seq_space: usize, ) -> usize { cctx_space .wrapping_add(tmp_work_space) .wrapping_add(block_state_space) .wrapping_add(ldm_space) .wrapping_add(ldm_seq_space) .wrapping_add(match_state_size) .wrapping_add(token_space) .wrapping_add(buffer_space) .wrapping_add(external_seq_space) } /// Aggregate workspace-size components with C `size_t` wrapping semantics. #[no_mangle] pub extern "C" fn ZSTD_rust_estimateWorkspaceSize( cctx_space: usize, tmp_work_space: usize, block_state_space: usize, ldm_space: usize, ldm_seq_space: usize, match_state_size: usize, token_space: usize, buffer_space: usize, external_seq_space: usize, ) -> usize { estimate_workspace_size( cctx_space, tmp_work_space, block_state_space, ldm_space, ldm_seq_space, match_state_size, token_space, buffer_space, external_seq_space, ) } #[inline] fn max_estimate_cctx_size( estimate0: usize, estimate1: usize, estimate2: usize, estimate3: usize, ) -> usize { estimate0.max(estimate1).max(estimate2).max(estimate3) } /// Return the largest raw estimate, including any C size_t error values. #[no_mangle] pub extern "C" fn ZSTD_rust_maxEstimateCCtxSize( estimate0: usize, estimate1: usize, estimate2: usize, estimate3: usize, ) -> usize { max_estimate_cctx_size(estimate0, estimate1, estimate2, estimate3) } #[inline] fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) { debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0); debug_assert!(table.len() < (1usize << 31)); /* Protect special index values < ZSTD_WINDOW_START_INDEX. */ let reducer_threshold = reducer_value.wrapping_add(ZSTD_WINDOW_START_INDEX); let mut rows = table.chunks_exact_mut(ZSTD_ROWSIZE); for row in &mut rows { for cell in row { let value = *cell; *cell = if preserve_mark && value == ZSTD_DUBT_UNSORTED_MARK { /* Keep the btlazy2 unsorted marker across table reduction. */ ZSTD_DUBT_UNSORTED_MARK } else if value < reducer_threshold { 0 } else { value.wrapping_sub(reducer_value) }; } } debug_assert!(rows.into_remainder().is_empty()); } /// Rust implementation of the C match-table reduction leaf. /// /// The C caller supplies a clear zero/one `preserve_mark` value for the /// strategy-specific btlazy2 policy. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_reduceTable( table: *mut u32, size: u32, reducer_value: u32, preserve_mark: c_int, ) { debug_assert!(!table.is_null() || size == 0); debug_assert_eq!(size % ZSTD_ROWSIZE as u32, 0); debug_assert!(size < (1u32 << 31)); if size == 0 { return; } let table = unsafe { std::slice::from_raw_parts_mut(table, size as usize) }; reduce_table_internal(table, reducer_value, preserve_mark != 0); } /// Reduce the match tables selected by C's stateful overflow-correction path. /// /// C retains match-state access and table selection. Each non-zero size is /// paired with a validated mutable table pointer; zero-sized optional tables /// may use null pointers. Only the chain table receives the btlazy2 marker /// policy. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_reduceIndex( hash_table: *mut u32, hash_size: u32, chain_table: *mut u32, chain_size: u32, hash_table3: *mut u32, hash_size3: u32, reducer_value: u32, preserve_chain_mark: c_int, ) { debug_assert!(!hash_table.is_null() || hash_size == 0); debug_assert!(!chain_table.is_null() || chain_size == 0); debug_assert!(!hash_table3.is_null() || hash_size3 == 0); debug_assert!(preserve_chain_mark == 0 || preserve_chain_mark == 1); unsafe { ZSTD_rust_reduceTable(hash_table, hash_size, reducer_value, 0); ZSTD_rust_reduceTable(chain_table, chain_size, reducer_value, preserve_chain_mark); ZSTD_rust_reduceTable(hash_table3, hash_size3, reducer_value, 0); } } /// Copies a CDict match table into a CCtx, removing short-cache tags when the /// C-owned compression parameters say the source table is tagged. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_copyCDictTableIntoCCtx( dst: *mut u32, src: *const u32, table_size: usize, tagged: c_int, ) { if tagged != 0 { for i in 0..table_size { let index = unsafe { *src.add(i) } >> ZSTD_SHORT_CACHE_TAG_BITS; unsafe { *dst.add(i) = index }; } } else if table_size != 0 { unsafe { ptr::copy_nonoverlapping(src, dst, table_size) }; } } /// Clear the previous block's repcodes before the next regular compression. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_invalidateRepCodes(rep: *mut u32) { debug_assert!(!rep.is_null()); let rep = unsafe { std::slice::from_raw_parts_mut(rep, ZSTD_REP_NUM) }; rep.fill(0); } /// Clear a C-owned compression window using the original `size_t`-to-`U32` /// conversion. The caller computes the pointer difference while the private /// `ZSTD_window_t` layout remains entirely on the C side. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_windowClear( end_t: usize, low_limit: *mut u32, dict_limit: *mut u32, ) { debug_assert!(!low_limit.is_null()); debug_assert!(!dict_limit.is_null()); let end = end_t as u32; unsafe { *low_limit = end; *dict_limit = end; } } #[inline] fn set_pledged_src_size( stream_stage: c_int, pledged_src_size: u64, pledged_src_size_plus_one: *mut u64, ) -> usize { if stream_stage != ZSTD_CSTREAM_STAGE_INIT { return ERROR(ZstdErrorCode::StageWrong); } debug_assert!(!pledged_src_size_plus_one.is_null()); unsafe { *pledged_src_size_plus_one = pledged_src_size.wrapping_add(1); } 0 } /// Set the pledged source size through scalar stage state and a C-owned field. /// The stage error remains the public `stage_wrong` encoding, while the /// unsigned increment preserves C's defined wrap at `U64::MAX`. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_setPledgedSrcSize( stream_stage: c_int, pledged_src_size: u64, pledged_src_size_plus_one: *mut u64, ) -> usize { set_pledged_src_size(stream_stage, pledged_src_size, pledged_src_size_plus_one) } #[inline] fn zeroed_state() -> ZSTD_compressedBlockState_t { /* The state contains only integer arrays and enum fields. */ unsafe { MaybeUninit::::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; #[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_result: usize, create_result: usize, mt_result: usize, begin_result: usize, pledged: u64, cdict: *const c_void, cdict_is_local: c_int, cdict_compression_level: c_int, cdict_dict_content_size: usize, expected_prefix: *const c_void, nb_workers: c_uint, has_ext_seq_prod: c_int, mt_context: *mut c_void, buffer_mode: c_int, block_size: usize, ordinary_target: usize, compression_level: c_int, } unsafe fn compress_stream_init_test_context( context: *mut c_void, ) -> &'static mut CompressStreamInitTestContext { unsafe { &mut *context.cast::() } } unsafe extern "C" fn compress_stream_init_test_local_dict(context: *mut c_void) -> usize { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("local-dict"); context.local_result } unsafe extern "C" fn compress_stream_init_test_refresh_cdict( context: *mut c_void, dictionaries: *mut ZSTD_rust_compressStreamInitDictionaryState, ) { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("refresh-cdict"); let dictionaries = unsafe { &mut *dictionaries }; dictionaries.cdict = context.cdict; dictionaries.cdict_is_local = context.cdict_is_local; dictionaries.cdict_compression_level = context.cdict_compression_level; dictionaries.cdict_dict_content_size = context.cdict_dict_content_size; } unsafe extern "C" fn compress_stream_init_test_clear_prefix(context: *mut c_void) { unsafe { compress_stream_init_test_context(context) } .events .push("clear-prefix"); } unsafe extern "C" fn compress_stream_init_test_assert_dictionaries( context: *mut c_void, prefix: *const c_void, ) { let context = unsafe { compress_stream_init_test_context(context) }; context.events.push("assert-dictionaries"); assert_eq!(prefix, context.expected_prefix); } unsafe extern "C" fn compress_stream_init_test_set_level(params: *mut c_void, level: c_int) { let context = unsafe { &mut *params.cast::() }; 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, init_local_dict: compress_stream_init_test_local_dict, refresh_cdict: compress_stream_init_test_refresh_cdict, clear_prefix: compress_stream_init_test_clear_prefix, assert_dictionaries: compress_stream_init_test_assert_dictionaries, set_compression_level: compress_stream_init_test_set_level, debug_init: compress_stream_init_test_debug, get_pledged_src_size_plus_one: compress_stream_init_test_get_pledged, set_pledged_src_size: compress_stream_init_test_set_pledged, get_cparam_mode: compress_stream_init_test_get_cparam_mode, build_cparams: compress_stream_init_test_build_cparams, resolve_params: compress_stream_init_test_resolve_params, get_nb_workers: compress_stream_init_test_get_nb_workers, set_nb_workers: compress_stream_init_test_set_nb_workers, has_ext_seq_prod: compress_stream_init_test_has_ext_seq_prod, trace_begin: compress_stream_init_test_trace, get_mt_context: compress_stream_init_test_get_mt_context, create_mt_context: compress_stream_init_test_create_mt_context, init_mt: compress_stream_init_test_mt, commit_mt: compress_stream_init_test_commit_mt, check_cparams: compress_stream_init_test_check_cparams, compress_begin: compress_stream_init_test_begin, assert_ordinary: compress_stream_init_test_assert_ordinary, get_buffer_mode: compress_stream_init_test_get_buffer_mode, get_block_size: compress_stream_init_test_get_block_size, commit_ordinary: compress_stream_init_test_commit_ordinary, } } #[test] fn compress_stream_init_preserves_ordinary_callback_order_and_policy() { let mut context = CompressStreamInitTestContext { pledged: 0, cdict: ptr::dangling::(), cdict_compression_level: 17, cdict_dict_content_size: 3, nb_workers: 0, buffer_mode: ZSTD_BM_BUFFERED, block_size: 4, ..CompressStreamInitTestContext::default() }; let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState { prefix_dict: ptr::null(), prefix_dict_size: 0, prefix_dict_content_type: 0, cdict: ptr::null(), cdict_is_local: 0, cdict_compression_level: 0, cdict_dict_content_size: 0, }; let state = compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_END, 4, 0); let result = unsafe { ZSTD_rust_compressStreamInit(&state) }; assert_eq!(result, 0); assert_eq!(context.compression_level, 17); assert_eq!(context.pledged, 5); assert_eq!(context.ordinary_target, 5); assert_eq!( context.events, [ "local-dict", "refresh-cdict", "clear-prefix", "assert-dictionaries", "set-level", "debug", "set-pledged", "get-pledged", "get-cparam-mode", "build-cparams", "resolve:block-splitter", "resolve:ldm", "resolve:row-match-finder", "resolve:validate-sequences", "resolve:max-block-size", "resolve:external-repcodes", "check-cparams", "compress-begin", "assert-ordinary", "get-buffer-mode", "get-block-size", "commit-ordinary", ] ); } #[test] fn compress_stream_init_stops_before_later_callbacks_on_error() { let mut context = CompressStreamInitTestContext { local_result: ERROR(ZstdErrorCode::MemoryAllocation), ..CompressStreamInitTestContext::default() }; let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState { prefix_dict: ptr::null(), prefix_dict_size: 0, prefix_dict_content_type: 0, cdict: ptr::null(), cdict_is_local: 0, cdict_compression_level: 0, cdict_dict_content_size: 0, }; let state = compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_CONTINUE, 0, 0); let result = unsafe { ZSTD_rust_compressStreamInit(&state) }; assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation)); assert_eq!(context.events, ["local-dict"]); } #[test] fn compress_stream_init_preserves_mt_branch_and_init_error_order() { let mut context = CompressStreamInitTestContext { cdict: ptr::dangling::(), 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], 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 extern "C" fn frame_chunk_test_prepare( context: *mut c_void, _src: *const c_void, block_size: usize, ) { let context = unsafe { frame_chunk_test_context(context) }; if context.prepare_calls < context.prepared_sizes.len() { context.prepared_sizes[context.prepare_calls] = block_size; } context.prepare_calls += 1; } unsafe extern "C" fn frame_chunk_test_target( context: *mut c_void, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, last_block: c_uint, ) -> usize { let context = unsafe { frame_chunk_test_context(context) }; if context.target_calls < context.last_blocks.len() { context.last_blocks[context.target_calls] = last_block; } context.target_calls += 1; context.target_result } unsafe extern "C" fn frame_chunk_test_split( context: *mut c_void, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, last_block: c_uint, ) -> usize { let context = unsafe { frame_chunk_test_context(context) }; if context.split_calls < context.last_blocks.len() { context.last_blocks[context.split_calls] = last_block; } context.split_calls += 1; context.split_result } unsafe extern "C" fn frame_chunk_test_internal( context: *mut c_void, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, last_block: c_uint, ) -> usize { let context = unsafe { frame_chunk_test_context(context) }; if context.internal_calls < context.last_blocks.len() { context.last_blocks[context.internal_calls] = last_block; } context.internal_calls += 1; context.internal_result } unsafe extern "C" fn frame_chunk_test_checksum( context: *mut c_void, _src: *const c_void, src_size: usize, ) { let context = unsafe { frame_chunk_test_context(context) }; context.checksum_calls += 1; context.checksum_size = context.checksum_size.wrapping_add(src_size); } fn frame_chunk_test_state( context: &mut FrameChunkTestContext, is_first_block: &mut c_int, stage: &mut c_int, use_target_c_block_size: c_int, block_splitter_enabled: c_int, checksum_flag: c_int, ) -> ZSTD_rust_frameChunkState { let context = context as *mut FrameChunkTestContext as *mut c_void; ZSTD_rust_frameChunkState { callback_context: context, tmp_workspace: ptr::null_mut(), checksum_state: context, is_first_block, stage, tmp_wksp_size: 0, block_size_max: 4, savings: 0, pre_block_splitter_level: 1, strategy: ZSTD_FAST, use_target_c_block_size, block_splitter_enabled, checksum_flag, ending_stage: 77, prepare_block: frame_chunk_test_prepare, compress_target: frame_chunk_test_target, compress_split: frame_chunk_test_split, compress_internal: frame_chunk_test_internal, update_checksum: frame_chunk_test_checksum, } } #[test] fn frame_chunk_internal_path_emits_blocks_and_updates_state_once() { let mut context = FrameChunkTestContext { internal_result: 2, ..FrameChunkTestContext::default() }; let mut is_first_block = 1; let mut stage = 0; let state = frame_chunk_test_state(&mut context, &mut is_first_block, &mut stage, 0, 0, 1); let source = [0x11u8; 8]; let mut output = [0xa5u8; 16]; let result = unsafe { compress_frame_chunk_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, ) }; assert_eq!(result, 10); assert_eq!(context.prepare_calls, 2); assert_eq!(context.prepared_sizes[..2], [4, 4]); assert_eq!(context.internal_calls, 2); assert_eq!(context.last_blocks[..2], [0, 1]); assert_eq!(context.checksum_calls, 1); assert_eq!(context.checksum_size, source.len()); assert_eq!(is_first_block, 0); assert_eq!(stage, 77); assert_ne!(output[..3], [0xa5; 3]); assert_ne!(output[5..8], [0xa5; 3]); } #[test] fn frame_chunk_dispatches_target_and_split_paths_before_internal() { let source = [0x22u8; 4]; let mut target_context = FrameChunkTestContext { target_result: 4, split_result: 5, internal_result: 6, ..FrameChunkTestContext::default() }; let mut target_first = 1; let mut target_stage = 0; let target_state = frame_chunk_test_state( &mut target_context, &mut target_first, &mut target_stage, 1, 1, 0, ); let mut target_output = [0xa5u8; 8]; let target_result = unsafe { compress_frame_chunk_body_with( &target_state, target_output.as_mut_ptr().cast(), target_output.len(), source.as_ptr().cast(), source.len(), 0, ) }; assert_eq!(target_result, 4); assert_eq!(target_context.target_calls, 1); assert_eq!(target_context.split_calls, 0); assert_eq!(target_context.internal_calls, 0); let mut split_context = FrameChunkTestContext { split_result: 5, internal_result: 6, ..FrameChunkTestContext::default() }; let mut split_first = 1; let mut split_stage = 0; let split_state = frame_chunk_test_state( &mut split_context, &mut split_first, &mut split_stage, 0, 1, 0, ); let mut split_output = [0xa5u8; 8]; let split_result = unsafe { compress_frame_chunk_body_with( &split_state, split_output.as_mut_ptr().cast(), split_output.len(), source.as_ptr().cast(), source.len(), 0, ) }; assert_eq!(split_result, 5); assert_eq!(split_context.target_calls, 0); assert_eq!(split_context.split_calls, 1); assert_eq!(split_context.internal_calls, 0); } #[test] fn frame_chunk_keeps_state_unchanged_on_early_capacity_error() { let mut context = FrameChunkTestContext::default(); let mut is_first_block = 1; let mut stage = 23; let state = frame_chunk_test_state(&mut context, &mut is_first_block, &mut stage, 0, 0, 1); let source = [0x33u8; 4]; let mut output = [0xa5u8; 5]; let result = unsafe { compress_frame_chunk_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), 1, ) }; assert_eq!(result, ERROR(ZstdErrorCode::DstSizeTooSmall)); assert_eq!(context.checksum_calls, 1); assert_eq!(context.prepare_calls, 0); assert_eq!(context.internal_calls, 0); assert_eq!(is_first_block, 1); assert_eq!(stage, 23); assert_eq!(output, [0xa5; 5]); } #[derive(Default)] struct CompressContinueTestContext { header_calls: usize, update_window_calls: usize, overflow_calls: usize, frame_calls: usize, block_calls: usize, last_frame_chunk: c_uint, header_result: usize, frame_result: usize, block_result: usize, } unsafe fn compress_continue_test_context( context: *mut c_void, ) -> &'static mut CompressContinueTestContext { unsafe { &mut *context.cast::() } } 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 copy_cdict_table_removes_short_cache_tags() { let source = [0x1234_56ff, 0xdead_beef, 0x0000_0100, u32::MAX]; let mut destination = [0u32; 4]; unsafe { ZSTD_rust_copyCDictTableIntoCCtx( destination.as_mut_ptr(), source.as_ptr(), source.len(), 1, ); } assert_eq!(destination, [0x0012_3456, 0x00de_adbe, 1, 0x00ff_ffff]); } #[test] fn copy_cdict_table_preserves_untagged_entries() { let source = [0, 2, 0x1234_5678, u32::MAX]; let mut destination = [0xa5a5_a5a5; 4]; unsafe { ZSTD_rust_copyCDictTableIntoCCtx( destination.as_mut_ptr(), source.as_ptr(), source.len(), 0, ); } assert_eq!(destination, source); } #[test] fn bitmix_and_hash_salt_match_the_c_arithmetic() { assert_eq!(bitmix(0x0123_4567_89ab_cdef, 8), 0xd498_d855_4e8d_d8cb); assert_eq!( advance_hash_salt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210), 0xe5ee_f172_e5ff_3e57 ); assert_eq!( ZSTD_rust_advanceHashSalt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210), 0xe5ee_f172_e5ff_3e57 ); } #[test] fn index_too_close_to_max_uses_a_strict_margin_boundary() { let threshold = ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN; assert!(!index_too_close_to_max(threshold)); assert!(index_too_close_to_max(threshold + 1)); assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold), 0); assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold + 1), 1); } #[test] fn window_correction_handles_current_cycle_start_boundary() { assert_eq!(window_correct_overflow(0x100, 3, 8), 0xf0); assert_eq!(window_correct_overflow(0x101, 3, 8), 0xf0); assert_eq!(window_correct_overflow(0x102, 3, 8), 0xf8); assert_eq!( ZSTD_rust_windowCorrectOverflow(0x100, 3, 8), window_correct_overflow(0x100, 3, 8) ); } #[test] fn window_correction_selects_larger_of_cycle_and_max_distance() { assert_eq!(window_correct_overflow(0x100, 5, 8), 0xc0); assert_eq!(window_correct_overflow(0x200, 5, 64), 0x1a0); } #[test] fn window_correction_wraps_u32_intermediates() { assert_eq!(window_correct_overflow(0, 0, 1), u32::MAX - 2); assert_eq!( window_correct_overflow(0x8000_0000, 31, 0x8000_0000), 0x8000_0000 ); assert_eq!( ZSTD_rust_windowCorrectOverflow(0x8000_0000, 31, 0x8000_0000), 0x8000_0000 ); } #[test] fn window_overflow_need_uses_the_explicit_current_max_boundary() { assert!(!window_need_overflow_correction(1024, false, 1024, false)); assert!(window_need_overflow_correction(1025, false, 1024, false)); assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1024, 1, 1024, 0), 0); assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1025, 0, 1024, 0), 1); } #[test] fn window_overflow_need_honors_frequent_dictionary_policy() { let can_correct = window_can_overflow_correct(27, 3, 16, 0, 0); let dictionary_still_valid = window_can_overflow_correct(27, 3, 16, 20, 0); assert!(can_correct); assert!(!dictionary_still_valid); assert!(window_need_overflow_correction(27, can_correct, 1024, true)); assert!(!window_need_overflow_correction( 27, dictionary_still_valid, 1024, true )); assert_eq!( ZSTD_rust_windowNeedOverflowCorrection(27, can_correct as u32, 1024, 1), 1 ); assert_eq!( ZSTD_rust_windowNeedOverflowCorrection(27, dictionary_still_valid as u32, 1024, 1,), 0 ); assert!(!window_need_overflow_correction(27, true, 1024, false)); } #[test] fn window_overflow_need_scales_the_frequency_with_correction_count() { assert!(window_can_overflow_correct(27, 3, 16, 0, 0)); assert!(!window_can_overflow_correct(27, 3, 16, 0, 1)); assert!(window_can_overflow_correct(53, 3, 16, 0, 1)); assert_eq!(ZSTD_rust_windowCanOverflowCorrect(53, 3, 16, 0, 1), 1); } #[test] fn window_overflow_need_preserves_u32_wrap_boundaries() { assert!(window_can_overflow_correct( 3, 31, 0x8000_0000, 0x8000_0000, 0, )); assert!(window_can_overflow_correct(27, 3, 16, 0, u32::MAX,)); assert_eq!( ZSTD_rust_windowCanOverflowCorrect(3, 31, 0x8000_0000, 0x8000_0000, 0), 1 ); } #[test] fn dict_too_big_uses_a_strict_chunk_size_boundary() { assert!(!dict_too_big(0)); assert!(!dict_too_big(ZSTD_CHUNKSIZE_MAX)); assert!(dict_too_big(ZSTD_CHUNKSIZE_MAX + 1)); assert_eq!(ZSTD_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX), 0); assert_eq!(ZSTD_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX + 1), 1); } #[test] fn target_block_policy_requires_compression_and_nonfirst_rle() { assert_eq!( target_c_block_size_action(0, 0, 1, 1, 0, 128, ZSTD_FAST), TargetCBlockAction::Rle ); assert_eq!( target_c_block_size_action(0, 1, 1, 1, 0, 128, ZSTD_FAST), TargetCBlockAction::Raw ); assert_eq!( target_c_block_size_action(0, 0, 0, 1, 0, 128, ZSTD_FAST), TargetCBlockAction::Raw ); assert_eq!( target_c_block_size_action(0, 0, 1, 0, 0, 128, ZSTD_FAST), TargetCBlockAction::Raw ); assert_eq!( ZSTD_rust_targetCBlockSizeAction(0, 0, 1, 1, 0, 128, ZSTD_FAST), TargetCBlockAction::Rle as c_int ); } #[test] fn target_block_policy_falls_back_for_uncompressed_and_small_results() { assert_eq!( target_c_block_size_action(1, 0, 0, 0, 0, 128, ZSTD_FAST), TargetCBlockAction::Raw ); assert_eq!( target_c_block_size_action( ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, ERROR(ZstdErrorCode::DstSizeTooSmall), 128, ZSTD_FAST, ), TargetCBlockAction::Raw ); assert_eq!( target_c_block_size_action( ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, ERROR(ZstdErrorCode::Generic), 128, ZSTD_FAST, ), TargetCBlockAction::Error ); assert_eq!( target_c_block_size_action(ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, 0, 128, ZSTD_FAST,), TargetCBlockAction::Raw ); } #[test] fn target_block_policy_uses_a_strict_three_byte_header_boundary() { let src_size = 128; let max_c_size = src_size - min_gain(src_size, ZSTD_FAST); let compressed = max_c_size + ZSTD_BLOCK_HEADER_SIZE - 1; let raw = max_c_size + ZSTD_BLOCK_HEADER_SIZE; assert_eq!( target_c_block_size_action( ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, compressed, src_size, ZSTD_FAST, ), TargetCBlockAction::Compressed ); assert_eq!( target_c_block_size_action( ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, raw, src_size, ZSTD_FAST, ), TargetCBlockAction::Raw ); assert_eq!( ZSTD_rust_targetCBlockSizeAction( ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, compressed, src_size, ZSTD_FAST, ), TargetCBlockAction::Compressed as c_int ); } #[allow(clippy::too_many_arguments)] unsafe extern "C" fn target_block_test_superblock_error( _seq_store: *const c_void, _prev_cblock: *const c_void, _next_cblock: *mut c_void, _strategy: c_int, _disable_literal_compression: c_int, _workspace: *mut c_void, _wksp_size: usize, _bmi2: c_int, _window_log: c_uint, _target_cblock_size: usize, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, _last_block: c_uint, ) -> usize { ERROR(ZstdErrorCode::Generic) } #[allow(clippy::too_many_arguments)] unsafe extern "C" fn target_block_test_superblock_empty( _seq_store: *const c_void, _prev_cblock: *const c_void, _next_cblock: *mut c_void, _strategy: c_int, _disable_literal_compression: c_int, _workspace: *mut c_void, _wksp_size: usize, _bmi2: c_int, _window_log: c_uint, _target_cblock_size: usize, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, _last_block: c_uint, ) -> usize { 0 } #[allow(clippy::too_many_arguments)] unsafe extern "C" fn target_block_test_superblock_too_small( _seq_store: *const c_void, _prev_cblock: *const c_void, _next_cblock: *mut c_void, _strategy: c_int, _disable_literal_compression: c_int, _workspace: *mut c_void, _wksp_size: usize, _bmi2: c_int, _window_log: c_uint, _target_cblock_size: usize, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, _last_block: c_uint, ) -> usize { ERROR(ZstdErrorCode::DstSizeTooSmall) } #[allow(clippy::too_many_arguments)] unsafe extern "C" fn target_block_test_superblock_compressed( _seq_store: *const c_void, _prev_cblock: *const c_void, _next_cblock: *mut c_void, _strategy: c_int, _disable_literal_compression: c_int, _workspace: *mut c_void, _wksp_size: usize, _bmi2: c_int, _window_log: c_uint, _target_cblock_size: usize, _dst: *mut c_void, _dst_capacity: usize, _src: *const c_void, _src_size: usize, _last_block: c_uint, ) -> usize { 1 } fn target_block_test_state( seq_store: &mut SeqStore_t, prev_block: &mut ZSTD_compressedBlockState_t, next_block: &mut ZSTD_compressedBlockState_t, prev_c_block: &mut *mut ZSTD_compressedBlockState_t, next_c_block: &mut *mut ZSTD_compressedBlockState_t, is_first_block: c_int, ) -> ZSTD_rust_targetCBlockSizeState { *prev_c_block = prev_block as *mut ZSTD_compressedBlockState_t; *next_c_block = next_block as *mut ZSTD_compressedBlockState_t; ZSTD_rust_targetCBlockSizeState { seq_store, prev_c_block, next_c_block, tmp_workspace: ptr::null_mut(), tmp_wksp_size: 0, strategy: ZSTD_FAST, disable_literal_compression: 0, bmi2: 0, window_log: 20, target_c_block_size: 0, is_first_block, } } fn target_block_test_seq_store() -> (SeqStore_t, [SeqDef; 1], [u8; 16]) { let mut sequences = [SeqDef::default(); 1]; let mut literals = [0u8; 16]; let sequences_start = sequences.as_mut_ptr(); let literals_start = literals.as_mut_ptr(); let seq_store = SeqStore_t { sequencesStart: sequences_start, sequences: sequences_start, litStart: literals_start, lit: literals_start, llCode: ptr::null_mut(), mlCode: ptr::null_mut(), ofCode: ptr::null_mut(), maxNbSeq: sequences.len(), maxNbLit: literals.len(), longLengthType: 0, longLengthPos: 0, }; (seq_store, sequences, literals) } #[test] fn target_block_body_emits_rle_only_for_nonfirst_repeated_blocks() { let (mut seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut prev_block = zeroed_state(); let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let state = target_block_test_state( &mut seq_store, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, 0, ); let source = [0x5au8; 16]; let mut output = [0xa5u8; 4]; let result = unsafe { compress_block_target_c_block_size_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 1, target_block_test_superblock_error, ) }; assert_eq!(result, 4); assert_eq!(output[3], source[0]); } #[test] fn target_block_body_suppresses_first_block_rle_and_propagates_superblock_error() { let (mut seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut prev_block = zeroed_state(); let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let state = target_block_test_state( &mut seq_store, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, 1, ); let source = [0x5au8; 16]; let mut output = [0xa5u8; 16]; let result = unsafe { compress_block_target_c_block_size_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 1, target_block_test_superblock_error, ) }; assert_eq!(result, ERROR(ZstdErrorCode::Generic)); assert_eq!(output, [0xa5u8; 16]); } #[test] fn target_block_body_falls_back_to_raw_for_empty_or_small_superblocks() { let (mut seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut prev_block = zeroed_state(); let mut next_block = zeroed_state(); let mut prev_c_block = ptr::null_mut(); let mut next_c_block = ptr::null_mut(); let state = target_block_test_state( &mut seq_store, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, 1, ); let source = *b"raw fallback"; for superblock in [ target_block_test_superblock_empty as TargetCBlockSuperBlockFn, target_block_test_superblock_too_small, ] { let mut output = [0xa5u8; 32]; let result = unsafe { compress_block_target_c_block_size_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, superblock, ) }; assert_eq!(result, source.len() + ZSTD_BLOCK_HEADER_SIZE); assert_eq!(&output[ZSTD_BLOCK_HEADER_SIZE..result], &source); } } #[test] fn target_block_body_swaps_state_only_for_compressed_output() { let (mut seq_store, _sequences, _literals) = target_block_test_seq_store(); let mut prev_block = zeroed_state(); let mut next_block = zeroed_state(); let mut prev_c_block = &mut prev_block as *mut ZSTD_compressedBlockState_t; let mut next_c_block = &mut next_block as *mut ZSTD_compressedBlockState_t; let state = target_block_test_state( &mut seq_store, &mut prev_block, &mut next_block, &mut prev_c_block, &mut next_c_block, 1, ); let prev_ptr = prev_c_block; let next_ptr = next_c_block; let source = [0x3cu8; 128]; let mut output = [0xa5u8; 128]; let result = unsafe { compress_block_target_c_block_size_body_with( &state, output.as_mut_ptr().cast(), output.len(), source.as_ptr().cast(), source.len(), ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, target_block_test_superblock_compressed, ) }; assert_eq!(result, 1); assert_eq!(unsafe { *state.prev_c_block }, next_ptr); assert_eq!(unsafe { *state.next_c_block }, prev_ptr); assert_eq!(output, [0xa5u8; 128]); } #[test] fn frame_progress_unknown_pledge_updates_counters() { let mut consumed = 7; let mut produced = 11; let result = unsafe { ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 0, 5, 13, 2) }; assert_eq!(result, 0); assert_eq!(consumed, 12); assert_eq!(produced, 26); } #[test] fn frame_progress_exact_pledge_is_accepted() { let mut consumed = 7; let mut produced = 11; let result = unsafe { ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 13, 5, 13, 2) }; assert_eq!(result, 0); assert_eq!(consumed, 12); assert_eq!(produced, 26); } #[test] fn frame_progress_one_byte_overrun_is_reported() { let mut consumed = 7; let mut produced = 11; let result = unsafe { ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 13, 6, 13, 2) }; assert_eq!(result, 1); assert_eq!(consumed, 13); } #[test] fn frame_progress_counters_update_before_overrun_result() { let mut consumed = 100; let mut produced = 200; let result = unsafe { ZSTD_rust_updateFrameProgression(&mut consumed, &mut produced, 106, 7, 17, 3) }; assert_eq!(result, 1); assert_eq!(consumed, 107); assert_eq!(produced, 220); } #[test] fn frame_progression_constructs_single_thread_values() { let consumed = 11_u64; let buffered = 37_usize; let produced = 53_u64; let expected = ZSTD_frameProgression { ingested: consumed + buffered as u64, consumed, produced, flushed: produced, currentJobID: 0, nbActiveWorkers: 0, }; assert_eq!(frame_progression(consumed, buffered, produced), expected); assert_eq!( ZSTD_rust_frameProgression(consumed, buffered, produced), expected ); } #[test] fn frame_progression_wraps_buffered_input_to_u64() { let consumed = u64::MAX - 3; let buffered = 8_usize; let progression = ZSTD_rust_frameProgression(consumed, buffered, 17); assert_eq!(progression.ingested, consumed.wrapping_add(buffered as u64)); assert_eq!(progression.consumed, consumed); assert_eq!(progression.produced, 17); assert_eq!(progression.flushed, 17); assert_eq!(progression.currentJobID, 0); assert_eq!(progression.nbActiveWorkers, 0); } #[test] fn next_input_size_hint_uses_remaining_stable_block_capacity() { let hint = ZSTD_rust_nextInputSizeHint(ZSTD_BM_STABLE, 256, 37, 99, 12); assert_eq!(next_input_size_hint(ZSTD_BM_STABLE, 256, 37, 99, 12), 219); assert_eq!(hint, 219); } #[test] fn next_input_size_hint_replaces_empty_buffered_hint_with_block_size() { assert_eq!( ZSTD_rust_nextInputSizeHint(ZSTD_BM_BUFFERED, 256, 37, 128, 128), 256 ); } #[test] fn next_input_size_hint_returns_nonzero_buffered_hint() { assert_eq!( ZSTD_rust_nextInputSizeHint(ZSTD_BM_BUFFERED, 256, 37, 128, 32), 96 ); } #[test] fn mt_next_input_size_hint_handles_empty_input_buffer() { assert_eq!(mt_next_input_size_hint(128, 0), 128); assert_eq!(ZSTDMT_rust_nextInputSizeHint(128, 0), 128); } #[test] fn mt_next_input_size_hint_returns_remaining_capacity() { assert_eq!(mt_next_input_size_hint(128, 37), 91); assert_eq!(ZSTDMT_rust_nextInputSizeHint(128, 37), 91); } #[test] fn mt_next_input_size_hint_replaces_full_buffer_with_target_size() { assert_eq!(ZSTDMT_rust_nextInputSizeHint(128, 128), 128); } #[test] fn mt_next_input_size_hint_preserves_zero_target_behavior() { assert_eq!(ZSTDMT_rust_nextInputSizeHint(0, 0), 0); } #[test] fn mt_next_input_size_hint_preserves_wrapped_overfill() { assert_eq!(ZSTDMT_rust_nextInputSizeHint(3, 4), usize::MAX); } #[test] fn mt_sizeof_cctx_handles_zero_components() { assert_eq!(mt_sizeof_cctx(0, 0, 0, 0, 0, 0, 0, 0), 0); assert_eq!(ZSTDMT_rust_sizeofCCtx(0, 0, 0, 0, 0, 0, 0, 0), 0); } #[test] fn mt_sizeof_cctx_adds_all_components_in_order() { assert_eq!(mt_sizeof_cctx(1, 2, 3, 4, 5, 6, 7, 8), 36); assert_eq!(ZSTDMT_rust_sizeofCCtx(1, 2, 3, 4, 5, 6, 7, 8), 36); } #[test] fn mt_sizeof_cctx_wraps_like_c_size_t_addition() { assert_eq!(mt_sizeof_cctx(usize::MAX, 1, 2, 3, 4, 5, 6, 7), 27); assert_eq!(ZSTDMT_rust_sizeofCCtx(usize::MAX, 1, 2, 3, 4, 5, 6, 7), 27); } #[test] fn in_buffer_for_end_flush_returns_stable_expected_buffer() { let expected_src = b"input".as_ptr().cast::(); 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); } #[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 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 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 ); } #[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) ); } }