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);
|
U64 ZSTD_rust_advanceHashSalt(U64 hashSalt, U64 hashSaltEntropy);
|
||||||
int ZSTD_rust_indexTooCloseToMax(size_t nextSrcBaseOffset);
|
int ZSTD_rust_indexTooCloseToMax(size_t nextSrcBaseOffset);
|
||||||
int ZSTD_rust_dictTooBig(size_t loadedDictSize);
|
int ZSTD_rust_dictTooBig(size_t loadedDictSize);
|
||||||
enum {
|
/* The target-sized block body only needs this narrow projection of ZSTD_CCtx.
|
||||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_RAW = 0,
|
* Matchfinder/window state, sequence-store construction, and outer repeat-mode
|
||||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_RLE = 1,
|
* cleanup remain in C. */
|
||||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_COMPRESSED = 2,
|
typedef struct {
|
||||||
ZSTD_RUST_TARGET_CBLOCK_ACTION_ERROR = 3
|
SeqStore_t* seqStore;
|
||||||
};
|
ZSTD_compressedBlockState_t** prevCBlock;
|
||||||
int ZSTD_rust_targetCBlockSizeAction(int bss, int isFirstBlock,
|
ZSTD_compressedBlockState_t** nextCBlock;
|
||||||
int maybeRLE, int isRLE,
|
void* tmpWorkspace;
|
||||||
size_t cSize, size_t srcSize,
|
size_t tmpWkspSize;
|
||||||
int strategy);
|
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 ZSTD_rust_nextInputSizeHint(int inBufferMode,
|
||||||
size_t blockSizeMax,
|
size_t blockSizeMax,
|
||||||
size_t stableInNotConsumed,
|
size_t stableInNotConsumed,
|
||||||
@@ -373,7 +404,6 @@ size_t ZSTD_rust_convertSequencesNoRepcodes(
|
|||||||
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
||||||
size_t nbSeqs);
|
size_t nbSeqs);
|
||||||
int ZSTD_rust_isRLE(const BYTE* src, size_t length);
|
int ZSTD_rust_isRLE(const BYTE* src, size_t length);
|
||||||
int ZSTD_rust_maybeRLE(const SeqStore_t* seqStore);
|
|
||||||
size_t ZSTD_rust_postProcessSequenceProducerResult(
|
size_t ZSTD_rust_postProcessSequenceProducerResult(
|
||||||
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs,
|
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs,
|
||||||
size_t outSeqsCapacity, size_t srcSize);
|
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);
|
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
|
static void
|
||||||
ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
|
ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
|
||||||
{
|
{
|
||||||
@@ -3065,70 +3086,6 @@ out:
|
|||||||
return cSize;
|
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,
|
static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
|
||||||
void* dst, size_t dstCapacity,
|
void* dst, size_t dstCapacity,
|
||||||
const void* src, size_t srcSize,
|
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);
|
(unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate, srcSize);
|
||||||
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
|
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");
|
FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize_body failed");
|
||||||
|
|
||||||
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
|
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_resetSeqStore, ZSTD_rust_transferSequencesNoDelim,
|
||||||
ZSTD_rust_transferSequencesWBlockDelim,
|
ZSTD_rust_transferSequencesWBlockDelim,
|
||||||
};
|
};
|
||||||
|
use crate::zstd_compress_superblock::ZSTD_rust_compressSuperBlock;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::mem::{offset_of, size_of, MaybeUninit};
|
use std::mem::{offset_of, size_of, MaybeUninit};
|
||||||
use std::os::raw::{c_int, c_uint};
|
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)]
|
#[repr(i32)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
enum TargetCBlockAction {
|
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.
|
/// Select the strategy used by the simple compression entry points.
|
||||||
///
|
///
|
||||||
/// This is the Rust equivalent of the strategy portion of
|
/// 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]
|
#[test]
|
||||||
fn frame_progress_unknown_pledge_updates_counters() {
|
fn frame_progress_unknown_pledge_updates_counters() {
|
||||||
let mut consumed = 7;
|
let mut consumed = 7;
|
||||||
|
|||||||
Reference in New Issue
Block a user