feat(compress): move target block emission into Rust
Move the target-compressed-block body out of zstd_compress.c while keeping sequence-store construction, matchfinding, context lifetime, and the outer offcode repeat-mode cleanup in C. Rust now owns the target policy, repeated block decision, Rust superblock call, compressed-state confirmation, and raw fallback. The narrow C projection asserts the cross-language layout instead of exposing ZSTD_CCtx. Add focused tests for first-block RLE suppression, RLE output, superblock errors, raw fallback, and state-slot swapping. Remove the C-only maybeRLE wrapper that became unreachable after the extraction. Test Plan: - cargo test --manifest-path rust/Cargo.toml --lib -- --test-threads=1 - cargo clippy --manifest-path rust/Cargo.toml --lib -- -D warnings - cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check - make -B -C lib -j2 lib - make -B -C tests -j2 test-cli-tests - ZSTREAM_TESTTIME=-T2s make -B -C tests -j2 test-zstream - FUZZERTEST=-T5s make -B -C tests -j2 test-fuzzer
This commit is contained in:
@@ -79,16 +79,47 @@ void ZSTD_rust_copyCDictTableIntoCCtx(U32* dst, U32 const* src,
|
||||
U64 ZSTD_rust_advanceHashSalt(U64 hashSalt, U64 hashSaltEntropy);
|
||||
int ZSTD_rust_indexTooCloseToMax(size_t nextSrcBaseOffset);
|
||||
int ZSTD_rust_dictTooBig(size_t loadedDictSize);
|
||||
enum {
|
||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_RAW = 0,
|
||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_RLE = 1,
|
||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_COMPRESSED = 2,
|
||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_ERROR = 3
|
||||
};
|
||||
int ZSTD_rust_targetCBlockSizeAction(int bss, int isFirstBlock,
|
||||
int maybeRLE, int isRLE,
|
||||
size_t cSize, size_t srcSize,
|
||||
int strategy);
|
||||
/* The target-sized block body only needs this narrow projection of ZSTD_CCtx.
|
||||
* Matchfinder/window state, sequence-store construction, and outer repeat-mode
|
||||
* cleanup remain in C. */
|
||||
typedef struct {
|
||||
SeqStore_t* seqStore;
|
||||
ZSTD_compressedBlockState_t** prevCBlock;
|
||||
ZSTD_compressedBlockState_t** nextCBlock;
|
||||
void* tmpWorkspace;
|
||||
size_t tmpWkspSize;
|
||||
int strategy;
|
||||
int disableLiteralCompression;
|
||||
int bmi2;
|
||||
U32 windowLog;
|
||||
size_t targetCBlockSize;
|
||||
int isFirstBlock;
|
||||
} ZSTD_rust_targetCBlockSizeState;
|
||||
size_t ZSTD_rust_compressBlockTargetCBlockSize(
|
||||
const ZSTD_rust_targetCBlockSizeState* state,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize,
|
||||
int bss, U32 lastBlock);
|
||||
typedef char ZSTD_rust_target_cblock_state_layout[
|
||||
(offsetof(ZSTD_rust_targetCBlockSizeState, seqStore) == 0
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, prevCBlock) == sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, nextCBlock) == 2 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, tmpWorkspace) == 3 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, tmpWkspSize) == 4 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, strategy) == 5 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, disableLiteralCompression)
|
||||
== 5 * sizeof(void*) + sizeof(int)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, bmi2)
|
||||
== 5 * sizeof(void*) + 2 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, windowLog)
|
||||
== 5 * sizeof(void*) + 3 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, targetCBlockSize)
|
||||
== 5 * sizeof(void*) + 4 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_targetCBlockSizeState, isFirstBlock)
|
||||
== 5 * sizeof(void*) + 4 * sizeof(int) + sizeof(size_t)
|
||||
&& sizeof(ZSTD_rust_targetCBlockSizeState)
|
||||
== (sizeof(void*) == 8 ? 72 : 44))
|
||||
? 1 : -1];
|
||||
size_t ZSTD_rust_nextInputSizeHint(int inBufferMode,
|
||||
size_t blockSizeMax,
|
||||
size_t stableInNotConsumed,
|
||||
@@ -373,7 +404,6 @@ size_t ZSTD_rust_convertSequencesNoRepcodes(
|
||||
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
||||
size_t nbSeqs);
|
||||
int ZSTD_rust_isRLE(const BYTE* src, size_t length);
|
||||
int ZSTD_rust_maybeRLE(const SeqStore_t* seqStore);
|
||||
size_t ZSTD_rust_postProcessSequenceProducerResult(
|
||||
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs,
|
||||
size_t outSeqsCapacity, size_t srcSize);
|
||||
@@ -2634,15 +2664,6 @@ static int ZSTD_isRLE(const BYTE* src, size_t length) {
|
||||
return ZSTD_rust_isRLE(src, length);
|
||||
}
|
||||
|
||||
/* Returns true if the given block may be RLE.
|
||||
* This is just a heuristic based on the compressibility.
|
||||
* It may return both false positives and false negatives.
|
||||
*/
|
||||
static int ZSTD_maybeRLE(SeqStore_t const* seqStore)
|
||||
{
|
||||
return ZSTD_rust_maybeRLE(seqStore);
|
||||
}
|
||||
|
||||
static void
|
||||
ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
|
||||
{
|
||||
@@ -3065,70 +3086,6 @@ out:
|
||||
return cSize;
|
||||
}
|
||||
|
||||
static size_t ZSTD_compressBlock_targetCBlockSize_body(ZSTD_CCtx* zc,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize,
|
||||
const size_t bss, U32 lastBlock)
|
||||
{
|
||||
int const isCompress = bss == ZSTDbss_compress;
|
||||
int maybeRLE = 0;
|
||||
int isRLE = 0;
|
||||
int action;
|
||||
|
||||
DEBUGLOG(6, "Attempting ZSTD_compressSuperBlock()");
|
||||
if (isCompress) {
|
||||
maybeRLE = ZSTD_maybeRLE(&zc->seqStore);
|
||||
isRLE = ZSTD_isRLE((BYTE const*)src, srcSize);
|
||||
}
|
||||
|
||||
action = ZSTD_rust_targetCBlockSizeAction(
|
||||
(int)bss, (int)zc->isFirstBlock, maybeRLE, isRLE,
|
||||
0, srcSize, (int)zc->appliedParams.cParams.strategy);
|
||||
if (action == ZSTD_RUST_TARGET_CBLOCK_ACTION_RLE) {
|
||||
return ZSTD_rust_rleCompressBlock(dst, dstCapacity, *(BYTE const*)src, srcSize, lastBlock);
|
||||
}
|
||||
|
||||
if (isCompress) {
|
||||
/* Attempt superblock compression.
|
||||
*
|
||||
* Note that compressed size of ZSTD_compressSuperBlock() is not bound by the
|
||||
* standard ZSTD_compressBound(). This is a problem, because even if we have
|
||||
* space now, taking an extra byte now could cause us to run out of space later
|
||||
* and violate ZSTD_compressBound().
|
||||
*
|
||||
* Define blockBound(blockSize) = blockSize + ZSTD_blockHeaderSize.
|
||||
*
|
||||
* In order to respect ZSTD_compressBound() we must attempt to emit a raw
|
||||
* uncompressed block in these cases:
|
||||
* * cSize == 0: Return code for an uncompressed block.
|
||||
* * cSize == dstSize_tooSmall: We may have expanded beyond blockBound(srcSize).
|
||||
* ZSTD_noCompressBlock() will return dstSize_tooSmall if we are really out of
|
||||
* output space.
|
||||
* * cSize >= blockBound(srcSize): We have expanded the block too much so
|
||||
* emit an uncompressed block.
|
||||
*/
|
||||
{ size_t const cSize =
|
||||
ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock);
|
||||
action = ZSTD_rust_targetCBlockSizeAction(
|
||||
(int)bss, (int)zc->isFirstBlock, 0, 0,
|
||||
cSize, srcSize, (int)zc->appliedParams.cParams.strategy);
|
||||
if (action == ZSTD_RUST_TARGET_CBLOCK_ACTION_ERROR) {
|
||||
FORWARD_IF_ERROR(cSize, "ZSTD_compressSuperBlock failed");
|
||||
}
|
||||
if (action == ZSTD_RUST_TARGET_CBLOCK_ACTION_COMPRESSED) {
|
||||
ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
|
||||
return cSize;
|
||||
}
|
||||
}
|
||||
} /* if (bss == ZSTDbss_compress)*/
|
||||
|
||||
DEBUGLOG(6, "Resorting to ZSTD_noCompressBlock()");
|
||||
/* Superblock compression failed, attempt to emit a single no compress block.
|
||||
* The decoder will be able to stream this block since it is uncompressed.
|
||||
*/
|
||||
return ZSTD_rust_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock);
|
||||
}
|
||||
|
||||
static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize,
|
||||
@@ -3140,7 +3097,21 @@ static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
|
||||
(unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate, srcSize);
|
||||
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
|
||||
|
||||
cSize = ZSTD_compressBlock_targetCBlockSize_body(zc, dst, dstCapacity, src, srcSize, bss, lastBlock);
|
||||
{ ZSTD_rust_targetCBlockSizeState state;
|
||||
state.seqStore = &zc->seqStore;
|
||||
state.prevCBlock = &zc->blockState.prevCBlock;
|
||||
state.nextCBlock = &zc->blockState.nextCBlock;
|
||||
state.tmpWorkspace = zc->tmpWorkspace;
|
||||
state.tmpWkspSize = zc->tmpWkspSize;
|
||||
state.strategy = (int)zc->appliedParams.cParams.strategy;
|
||||
state.disableLiteralCompression = ZSTD_literalsCompressionIsDisabled(&zc->appliedParams);
|
||||
state.bmi2 = zc->bmi2;
|
||||
state.windowLog = zc->appliedParams.cParams.windowLog;
|
||||
state.targetCBlockSize = zc->appliedParams.targetCBlockSize;
|
||||
state.isFirstBlock = zc->isFirstBlock;
|
||||
cSize = ZSTD_rust_compressBlockTargetCBlockSize(
|
||||
&state, dst, dstCapacity, src, srcSize, (int)bss, lastBlock);
|
||||
}
|
||||
FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize_body failed");
|
||||
|
||||
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
|
||||
|
||||
@@ -33,6 +33,7 @@ use crate::zstd_compress_stats::{
|
||||
ZSTD_rust_resetSeqStore, ZSTD_rust_transferSequencesNoDelim,
|
||||
ZSTD_rust_transferSequencesWBlockDelim,
|
||||
};
|
||||
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_uint};
|
||||
@@ -163,6 +164,59 @@ const _: () = {
|
||||
);
|
||||
};
|
||||
|
||||
/// Explicit projection of the state used by the target-sized block body.
|
||||
///
|
||||
/// Sequence-store construction and the matchfinder remain in C. This state
|
||||
/// contains only the already-Rust-owned block-compression inputs and the two
|
||||
/// pointer slots that the compressed-block confirmation leaf swaps.
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_rust_targetCBlockSizeState {
|
||||
seq_store: *mut SeqStore_t,
|
||||
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
|
||||
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
|
||||
tmp_workspace: *mut c_void,
|
||||
tmp_wksp_size: usize,
|
||||
strategy: c_int,
|
||||
disable_literal_compression: c_int,
|
||||
bmi2: c_int,
|
||||
window_log: c_uint,
|
||||
target_c_block_size: usize,
|
||||
is_first_block: c_int,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, seq_store) == 0);
|
||||
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, prev_c_block) == size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, next_c_block) == 2 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, tmp_workspace) == 3 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, tmp_wksp_size) == 4 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_targetCBlockSizeState, strategy) == 5 * size_of::<usize>());
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_targetCBlockSizeState, disable_literal_compression)
|
||||
== 5 * size_of::<usize>() + size_of::<c_int>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_targetCBlockSizeState, bmi2)
|
||||
== 5 * size_of::<usize>() + 2 * size_of::<c_int>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_targetCBlockSizeState, window_log)
|
||||
== 5 * size_of::<usize>() + 3 * size_of::<c_int>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_targetCBlockSizeState, target_c_block_size)
|
||||
== 5 * size_of::<usize>() + 4 * size_of::<c_int>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_targetCBlockSizeState, is_first_block)
|
||||
== 5 * size_of::<usize>() + 4 * size_of::<c_int>() + size_of::<usize>()
|
||||
);
|
||||
assert!(
|
||||
size_of::<ZSTD_rust_targetCBlockSizeState>()
|
||||
== if size_of::<usize>() == 8 { 72 } else { 44 }
|
||||
);
|
||||
};
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum TargetCBlockAction {
|
||||
@@ -227,6 +281,149 @@ fn target_c_block_size_action(
|
||||
}
|
||||
}
|
||||
|
||||
type TargetCBlockSuperBlockFn = unsafe extern "C" fn(
|
||||
*const c_void,
|
||||
*const c_void,
|
||||
*mut c_void,
|
||||
c_int,
|
||||
c_int,
|
||||
*mut c_void,
|
||||
usize,
|
||||
c_int,
|
||||
c_uint,
|
||||
usize,
|
||||
*mut c_void,
|
||||
usize,
|
||||
*const c_void,
|
||||
usize,
|
||||
c_uint,
|
||||
) -> usize;
|
||||
|
||||
/// Rust implementation of `ZSTD_compressBlock_targetCBlockSize_body()`.
|
||||
///
|
||||
/// The C caller has already built the sequence store. This function owns
|
||||
/// only the RLE/superblock/raw decision and leaves context construction,
|
||||
/// matchfinder state, frame progress, and outer repeat-mode cleanup in C.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
unsafe fn compress_block_target_c_block_size_body_with(
|
||||
state: &ZSTD_rust_targetCBlockSizeState,
|
||||
dst: *mut c_void,
|
||||
dst_capacity: usize,
|
||||
src: *const c_void,
|
||||
src_size: usize,
|
||||
bss: c_int,
|
||||
last_block: c_uint,
|
||||
compress_super_block: TargetCBlockSuperBlockFn,
|
||||
) -> usize {
|
||||
if state.seq_store.is_null() || state.prev_c_block.is_null() || state.next_c_block.is_null() {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
|
||||
let prev_c_block = unsafe { *state.prev_c_block };
|
||||
let next_c_block = unsafe { *state.next_c_block };
|
||||
if prev_c_block.is_null() || next_c_block.is_null() {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
|
||||
let is_compress = bss == ZSTD_TARGET_CBLOCK_BSS_COMPRESS;
|
||||
let (maybe_rle, is_rle) = if is_compress {
|
||||
let maybe_rle = unsafe { ZSTD_rust_maybeRLE(state.seq_store) };
|
||||
let is_rle = unsafe { ZSTD_rust_isRLE(src.cast(), src_size) };
|
||||
(maybe_rle, is_rle)
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
|
||||
let action = target_c_block_size_action(
|
||||
bss,
|
||||
state.is_first_block,
|
||||
maybe_rle,
|
||||
is_rle,
|
||||
0,
|
||||
src_size,
|
||||
state.strategy,
|
||||
);
|
||||
if action == TargetCBlockAction::Rle {
|
||||
return unsafe {
|
||||
ZSTD_rust_rleCompressBlock(dst, dst_capacity, *src.cast::<u8>(), src_size, last_block)
|
||||
};
|
||||
}
|
||||
|
||||
if is_compress {
|
||||
/* The superblock result is not bounded by ZSTD_compressBound(). The
|
||||
* policy helper therefore falls back to a raw block for zero,
|
||||
* dstSize_tooSmall, or an expansion beyond blockBound(srcSize). */
|
||||
let c_size = unsafe {
|
||||
compress_super_block(
|
||||
state.seq_store.cast(),
|
||||
prev_c_block.cast(),
|
||||
next_c_block.cast(),
|
||||
state.strategy,
|
||||
state.disable_literal_compression,
|
||||
state.tmp_workspace,
|
||||
state.tmp_wksp_size,
|
||||
state.bmi2,
|
||||
state.window_log,
|
||||
state.target_c_block_size,
|
||||
dst,
|
||||
dst_capacity,
|
||||
src,
|
||||
src_size,
|
||||
last_block,
|
||||
)
|
||||
};
|
||||
let action = target_c_block_size_action(
|
||||
bss,
|
||||
state.is_first_block,
|
||||
0,
|
||||
0,
|
||||
c_size,
|
||||
src_size,
|
||||
state.strategy,
|
||||
);
|
||||
if action == TargetCBlockAction::Error {
|
||||
return c_size;
|
||||
}
|
||||
if action == TargetCBlockAction::Compressed {
|
||||
unsafe {
|
||||
ZSTD_rust_confirmRepcodesAndEntropyTables(state.prev_c_block, state.next_c_block);
|
||||
}
|
||||
return c_size;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { ZSTD_rust_noCompressBlock(dst, dst_capacity, src, src_size, last_block) }
|
||||
}
|
||||
|
||||
/// 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Select the strategy used by the simple compression entry points.
|
||||
///
|
||||
/// This is the Rust equivalent of the strategy portion of
|
||||
@@ -2167,6 +2364,285 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[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;
|
||||
|
||||
Reference in New Issue
Block a user