#![allow(clippy::field_reassign_with_default)]
use crate::compress::seq_store::{SeqDef, SeqStore_t, ZSTD_getSequenceLength, MINMATCH};
use crate::compress::zstd_compress::{
ZSTD_CCtx_params, ZSTD_entropyCTablesMetadata_t, ZSTD_entropyCTables_t,
ZSTD_fseCTablesMetadata_t, ZSTD_hufCTablesMetadata_t, ZSTD_hufCTables_t,
};
use crate::decompress::zstd_decompress_block::SymbolEncodingType_e;
const BYTESCALE: usize = 256;
#[inline]
const fn STREAM_ACCUMULATOR_MIN() -> u32 {
if crate::common::mem::MEM_32bits() != 0 {
25
} else {
57
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct EstimatedBlockSize {
pub estLitSize: usize,
pub estBlockSize: usize,
}
#[allow(clippy::too_many_arguments)]
pub fn ZSTD_compressSubBlock_literal(
hufTable: &[crate::compress::huf_compress::HUF_CElt],
hufMetadata: &ZSTD_hufCTablesMetadata_t,
literals: &[u8],
dst: &mut [u8],
bmi2: i32,
writeEntropy: bool,
entropyWritten: &mut i32,
) -> usize {
use crate::common::mem::{MEM_writeLE24, MEM_writeLE32};
use crate::compress::huf_compress::{HUF_compress1X_usingCTable, HUF_compress4X_usingCTable};
use crate::compress::zstd_compress_literals::{
ZSTD_compressRleLiteralsBlock, ZSTD_noCompressLiterals,
};
let litSize = literals.len();
let header = if writeEntropy { 200 } else { 0 };
let lhSize = 3
+ (litSize >= (1024usize.saturating_sub(header))) as usize
+ (litSize >= (16 * 1024usize).saturating_sub(header)) as usize;
let singleStream = lhSize == 3;
let hType = if writeEntropy {
hufMetadata.hType
} else {
SymbolEncodingType_e::set_repeat
};
let mut op = lhSize;
let mut cLitSize = 0usize;
*entropyWritten = 0;
if litSize == 0 || hufMetadata.hType == SymbolEncodingType_e::set_basic {
return ZSTD_noCompressLiterals(dst, literals);
} else if hufMetadata.hType == SymbolEncodingType_e::set_rle {
return ZSTD_compressRleLiteralsBlock(dst, literals);
}
if dst.len() < op {
return 0;
}
if writeEntropy && hufMetadata.hType == SymbolEncodingType_e::set_compressed {
if hufMetadata.hufDesSize > dst.len() - op {
return 0;
}
dst[op..op + hufMetadata.hufDesSize]
.copy_from_slice(&hufMetadata.hufDesBuffer[..hufMetadata.hufDesSize]);
op += hufMetadata.hufDesSize;
cLitSize += hufMetadata.hufDesSize;
}
let cSize = if singleStream {
HUF_compress1X_usingCTable(
&mut dst[op..],
literals,
hufTable,
if bmi2 != 0 { 1 } else { 0 },
)
} else {
HUF_compress4X_usingCTable(
&mut dst[op..],
literals,
hufTable,
if bmi2 != 0 { 1 } else { 0 },
)
};
if cSize == 0 || crate::common::error::ERR_isError(cSize) {
return 0;
}
op += cSize;
cLitSize += cSize;
if !writeEntropy && cLitSize >= litSize {
return ZSTD_noCompressLiterals(dst, literals);
}
if lhSize < (3 + (cLitSize >= 1024) as usize + (cLitSize >= 16 * 1024) as usize) {
return ZSTD_noCompressLiterals(dst, literals);
}
match lhSize {
3 => {
let lhc = (hType as u32)
.wrapping_add((singleStream as u32 ^ 1) << 2)
.wrapping_add((litSize as u32) << 4)
.wrapping_add((cLitSize as u32) << 14);
MEM_writeLE24(&mut dst[..3], lhc);
}
4 => {
let lhc = (hType as u32)
.wrapping_add(2 << 2)
.wrapping_add((litSize as u32) << 4)
.wrapping_add((cLitSize as u32) << 18);
MEM_writeLE32(&mut dst[..4], lhc);
}
5 => {
let lhc = (hType as u32)
.wrapping_add(3 << 2)
.wrapping_add((litSize as u32) << 4)
.wrapping_add((cLitSize as u32) << 22);
MEM_writeLE32(&mut dst[..4], lhc);
dst[4] = (cLitSize >> 10) as u8;
}
_ => unreachable!(),
}
*entropyWritten = 1;
op
}
#[allow(clippy::too_many_arguments)]
pub fn ZSTD_compressSubBlock_sequences(
fseTables: &crate::compress::zstd_compress::ZSTD_fseCTables_t,
fseMetadata: &ZSTD_fseCTablesMetadata_t,
sequences: &[SeqDef],
nbSeq: usize,
llCode: &[u8],
mlCode: &[u8],
ofCode: &[u8],
cctxParams: &ZSTD_CCtx_params,
dst: &mut [u8],
bmi2: i32,
writeEntropy: bool,
entropyWritten: &mut i32,
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
use crate::common::mem::MEM_writeLE16;
use crate::compress::zstd_compress_sequences::ZSTD_encodeSequences;
use crate::decompress::zstd_decompress_block::LONGNBSEQ;
let longOffsets = (cctxParams.cParams.windowLog > STREAM_ACCUMULATOR_MIN()) as i32;
let mut op = 0usize;
*entropyWritten = 0;
if dst.len() < 4 {
return ERROR(ErrorCode::DstSizeTooSmall);
}
if nbSeq < 128 {
dst[op] = nbSeq as u8;
op += 1;
} else if nbSeq < LONGNBSEQ as usize {
dst[op] = ((nbSeq >> 8) + 0x80) as u8;
dst[op + 1] = nbSeq as u8;
op += 2;
} else {
dst[op] = 0xFF;
MEM_writeLE16(&mut dst[op + 1..], (nbSeq - LONGNBSEQ as usize) as u16);
op += 3;
}
if nbSeq == 0 {
return op;
}
let seqHead = op;
op += 1;
if writeEntropy {
if fseMetadata.fseTablesSize > dst.len().saturating_sub(op) {
return 0;
}
dst[seqHead] = ((fseMetadata.llType as u8) << 6)
| ((fseMetadata.ofType as u8) << 4)
| ((fseMetadata.mlType as u8) << 2);
dst[op..op + fseMetadata.fseTablesSize]
.copy_from_slice(&fseMetadata.fseTablesBuffer[..fseMetadata.fseTablesSize]);
op += fseMetadata.fseTablesSize;
} else {
let repeat = SymbolEncodingType_e::set_repeat as u8;
dst[seqHead] = (repeat << 6) | (repeat << 4) | (repeat << 2);
}
let bitstreamSize = ZSTD_encodeSequences(
&mut dst[op..],
&fseTables.matchlengthCTable,
mlCode,
&fseTables.offcodeCTable,
ofCode,
&fseTables.litlengthCTable,
llCode,
sequences,
nbSeq,
longOffsets,
bmi2,
);
if crate::common::error::ERR_isError(bitstreamSize) {
return bitstreamSize;
}
op += bitstreamSize;
if writeEntropy
&& fseMetadata.lastCountSize != 0
&& fseMetadata.lastCountSize + bitstreamSize < 4
{
return 0;
}
if op - seqHead < 4 {
return 0;
}
*entropyWritten = 1;
op
}
#[allow(clippy::too_many_arguments)]
pub fn ZSTD_compressSubBlock(
entropy: &ZSTD_entropyCTables_t,
entropyMetadata: &ZSTD_entropyCTablesMetadata_t,
sequences: &[SeqDef],
nbSeq: usize,
literals: &[u8],
llCode: &[u8],
mlCode: &[u8],
ofCode: &[u8],
cctxParams: &ZSTD_CCtx_params,
dst: &mut [u8],
bmi2: i32,
writeLitEntropy: bool,
writeSeqEntropy: bool,
litEntropyWritten: &mut i32,
seqEntropyWritten: &mut i32,
lastBlock: u32,
) -> usize {
use crate::common::mem::MEM_writeLE24;
use crate::decompress::zstd_decompress_block::{blockType_e, ZSTD_blockHeaderSize};
if dst.len() < ZSTD_blockHeaderSize {
return 0;
}
let mut op = ZSTD_blockHeaderSize;
let cLitSize = ZSTD_compressSubBlock_literal(
&entropy.huf.CTable,
&entropyMetadata.hufMetadata,
literals,
&mut dst[op..],
bmi2,
writeLitEntropy,
litEntropyWritten,
);
if crate::common::error::ERR_isError(cLitSize) || cLitSize == 0 {
return cLitSize;
}
op += cLitSize;
let cSeqSize = ZSTD_compressSubBlock_sequences(
&entropy.fse,
&entropyMetadata.fseMetadata,
sequences,
nbSeq,
llCode,
mlCode,
ofCode,
cctxParams,
&mut dst[op..],
bmi2,
writeSeqEntropy,
seqEntropyWritten,
);
if crate::common::error::ERR_isError(cSeqSize) || cSeqSize == 0 {
return cSeqSize;
}
op += cSeqSize;
let cSize = op - ZSTD_blockHeaderSize;
let cBlockHeader24 = lastBlock
.wrapping_add((blockType_e::bt_compressed as u32) << 1)
.wrapping_add((cSize as u32) << 3);
MEM_writeLE24(&mut dst[..3], cBlockHeader24);
op
}
pub fn ZSTD_seqDecompressedSize(
seqStore: &SeqStore_t,
sequences: &[SeqDef],
nbSeqs: usize,
litSize: usize,
lastSubBlock: i32,
) -> usize {
let mut matchLengthSum = 0usize;
let mut litLengthSum = 0usize;
let base = seqStore.sequences.as_ptr();
let start = sequences.as_ptr();
let start_idx = unsafe { start.offset_from(base) };
debug_assert!(start_idx >= 0);
let start_idx = start_idx as usize;
debug_assert!(start_idx + nbSeqs <= seqStore.sequences.len());
for n in 0..nbSeqs {
let seqLen = ZSTD_getSequenceLength(seqStore, start_idx + n);
litLengthSum += seqLen.litLength as usize;
matchLengthSum += seqLen.matchLength as usize;
}
if lastSubBlock == 0 {
debug_assert_eq!(litLengthSum, litSize);
} else {
debug_assert!(litLengthSum <= litSize);
}
matchLengthSum + litSize
}
#[allow(clippy::too_many_arguments)]
pub fn ZSTD_compressSubBlock_multi(
seqStorePtr: &SeqStore_t,
prevEntropy: &ZSTD_entropyCTables_t,
nextEntropy: &mut ZSTD_entropyCTables_t,
prev_rep: &[u32; 3],
next_rep: &mut [u32; 3],
entropyMetadata: &ZSTD_entropyCTablesMetadata_t,
cctxParams: &ZSTD_CCtx_params,
dst: &mut [u8],
src: &[u8],
bmi2: i32,
lastBlock: u32,
literalCountWorkspace: &mut [u32],
seqCountWorkspace: &mut [u32],
workspace: &mut [u32],
) -> usize {
use crate::compress::seq_store::ZSTD_updateRep;
use crate::compress::zstd_compress::ZSTD_noCompressBlock;
let nbSeqs = seqStorePtr.sequences.len();
let nbLiterals = seqStorePtr.literals.len();
let mut sp = 0usize;
let mut lp = 0usize;
let mut ip = 0usize;
let mut op = 0usize;
let mut ll_code_ptr = 0usize;
let mut ml_code_ptr = 0usize;
let mut of_code_ptr = 0usize;
let minTarget = crate::compress::zstd_compress::ZSTD_TARGETCBLOCKSIZE_MIN as usize;
let targetCBlockSize = minTarget.max(cctxParams.targetCBlockSize);
let mut writeLitEntropy =
(entropyMetadata.hufMetadata.hType == SymbolEncodingType_e::set_compressed) as i32;
let mut writeSeqEntropy = 1i32;
if nbSeqs > 0 {
let ebs = ZSTD_estimateSubBlockSize(
&seqStorePtr.literals[lp..],
&seqStorePtr.ofCode[of_code_ptr..],
&seqStorePtr.llCode[ll_code_ptr..],
&seqStorePtr.mlCode[ml_code_ptr..],
nbSeqs,
nextEntropy,
entropyMetadata,
literalCountWorkspace,
seqCountWorkspace,
workspace,
writeLitEntropy != 0,
writeSeqEntropy != 0,
);
let avgLitCost = if nbLiterals != 0 {
(ebs.estLitSize * BYTESCALE) / nbLiterals
} else {
BYTESCALE
};
let avgSeqCost = ((ebs.estBlockSize - ebs.estLitSize) * BYTESCALE) / nbSeqs;
let nbSubBlocks = ((ebs.estBlockSize + (targetCBlockSize / 2)) / targetCBlockSize).max(1);
let avgBlockBudget = (ebs.estBlockSize * BYTESCALE) / nbSubBlocks;
let mut blockBudgetSupp = 0usize;
if ebs.estBlockSize > src.len() {
return 0;
}
for n in 0..(nbSubBlocks.saturating_sub(1)) {
let seqCount = sizeBlockSequences(
&seqStorePtr.sequences[sp..],
seqStorePtr.sequences.len() - sp,
avgBlockBudget + blockBudgetSupp,
avgLitCost,
avgSeqCost,
(n == 0) as i32,
);
if sp + seqCount == seqStorePtr.sequences.len() {
break;
}
if seqCount == 0 {
break;
}
let mut litEntropyWritten = 0;
let mut seqEntropyWritten = 0;
let litSize = countLiterals(seqStorePtr, &seqStorePtr.sequences[sp..], seqCount);
let decompressedSize = ZSTD_seqDecompressedSize(
seqStorePtr,
&seqStorePtr.sequences[sp..],
seqCount,
litSize,
0,
);
let cSize = ZSTD_compressSubBlock(
nextEntropy,
entropyMetadata,
&seqStorePtr.sequences[sp..sp + seqCount],
seqCount,
&seqStorePtr.literals[lp..lp + litSize],
&seqStorePtr.llCode[ll_code_ptr..ll_code_ptr + seqCount],
&seqStorePtr.mlCode[ml_code_ptr..ml_code_ptr + seqCount],
&seqStorePtr.ofCode[of_code_ptr..of_code_ptr + seqCount],
cctxParams,
&mut dst[op..],
bmi2,
writeLitEntropy != 0,
writeSeqEntropy != 0,
&mut litEntropyWritten,
&mut seqEntropyWritten,
0,
);
if crate::common::error::ERR_isError(cSize) {
return cSize;
}
if cSize > 0 && cSize < decompressedSize {
ip += decompressedSize;
lp += litSize;
op += cSize;
ll_code_ptr += seqCount;
ml_code_ptr += seqCount;
of_code_ptr += seqCount;
if litEntropyWritten != 0 {
writeLitEntropy = 0;
}
if seqEntropyWritten != 0 {
writeSeqEntropy = 0;
}
sp += seqCount;
blockBudgetSupp = 0;
}
}
}
let mut litEntropyWritten = 0;
let mut seqEntropyWritten = 0;
let litSize = seqStorePtr.literals.len() - lp;
let seqCount = seqStorePtr.sequences.len() - sp;
let decompressedSize = ZSTD_seqDecompressedSize(
seqStorePtr,
&seqStorePtr.sequences[sp..],
seqCount,
litSize,
1,
);
let cSize = ZSTD_compressSubBlock(
nextEntropy,
entropyMetadata,
&seqStorePtr.sequences[sp..],
seqCount,
&seqStorePtr.literals[lp..],
&seqStorePtr.llCode[ll_code_ptr..],
&seqStorePtr.mlCode[ml_code_ptr..],
&seqStorePtr.ofCode[of_code_ptr..],
cctxParams,
&mut dst[op..],
bmi2,
writeLitEntropy != 0,
writeSeqEntropy != 0,
&mut litEntropyWritten,
&mut seqEntropyWritten,
lastBlock,
);
if crate::common::error::ERR_isError(cSize) {
return cSize;
}
if cSize > 0 && cSize < decompressedSize {
ip += decompressedSize;
lp += litSize;
op += cSize;
ll_code_ptr += seqCount;
ml_code_ptr += seqCount;
of_code_ptr += seqCount;
if litEntropyWritten != 0 {
writeLitEntropy = 0;
}
if seqEntropyWritten != 0 {
writeSeqEntropy = 0;
}
sp += seqCount;
}
debug_assert!(lp <= seqStorePtr.literals.len());
debug_assert!(ll_code_ptr <= seqStorePtr.llCode.len());
debug_assert!(ml_code_ptr <= seqStorePtr.mlCode.len());
debug_assert!(of_code_ptr <= seqStorePtr.ofCode.len());
if writeLitEntropy != 0 {
nextEntropy.huf = prevEntropy.huf.clone();
}
if writeSeqEntropy != 0 && ZSTD_needSequenceEntropyTables(&entropyMetadata.fseMetadata) != 0 {
return 0;
}
if ip < src.len() {
let cSize = ZSTD_noCompressBlock(&mut dst[op..], &src[ip..], lastBlock);
if crate::common::error::ERR_isError(cSize) {
return cSize;
}
op += cSize;
if sp < seqStorePtr.sequences.len() {
let mut rep = *prev_rep;
for seq_idx in 0..sp {
let ll0 = (ZSTD_getSequenceLength(seqStorePtr, seq_idx).litLength == 0) as u32;
ZSTD_updateRep(&mut rep, seqStorePtr.sequences[seq_idx].offBase, ll0);
}
*next_rep = rep;
}
}
op
}
pub fn ZSTD_estimateSubBlockSize_literal(
literals: &[u8],
huf: &ZSTD_hufCTables_t,
hufMetadata: &ZSTD_hufCTablesMetadata_t,
count: &mut [u32],
workspace: &mut [u32],
writeEntropy: bool,
) -> usize {
use crate::compress::hist::HIST_count_wksp;
use crate::compress::huf_compress::{HUF_estimateCompressedSize, HUF_SYMBOLVALUE_MAX};
let litSize = literals.len();
let literalSectionHeaderSize = 3usize;
match hufMetadata.hType {
SymbolEncodingType_e::set_basic => litSize,
SymbolEncodingType_e::set_rle => 1,
SymbolEncodingType_e::set_compressed | SymbolEncodingType_e::set_repeat => {
let mut maxSymbolValue = HUF_SYMBOLVALUE_MAX;
let count_len = maxSymbolValue as usize + 1;
if count.len() < count_len {
return litSize;
}
let count = &mut count[..count_len];
count.fill(0);
let largest = HIST_count_wksp(count, &mut maxSymbolValue, literals, workspace);
if crate::common::error::ERR_isError(largest) {
return litSize;
}
let mut cLitSizeEstimate =
HUF_estimateCompressedSize(&huf.CTable, count, maxSymbolValue);
if writeEntropy {
cLitSizeEstimate += hufMetadata.hufDesSize;
}
cLitSizeEstimate + literalSectionHeaderSize
}
}
}
pub fn ZSTD_estimateSubBlockSize_symbolType(
encType: SymbolEncodingType_e,
codeTable: &[u8],
maxCode: u32,
nbSeq: usize,
fseCTable: &[crate::compress::fse_compress::FSE_CTable],
additionalBits: Option<&[u8]>,
defaultNorm: &[i16],
defaultNormLog: u32,
defaultMax: u32,
count: &mut [u32],
workspace: &mut [u32],
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
use crate::compress::hist::HIST_countFast_wksp;
use crate::compress::zstd_compress_sequences::{ZSTD_crossEntropyCost, ZSTD_fseBitCost};
let codeTable = &codeTable[..codeTable.len().min(nbSeq)];
let mut max = maxCode;
let count_len = maxCode as usize + 1;
if count.len() < count_len {
return nbSeq * 10;
}
let count = &mut count[..count_len];
count.fill(0);
let _ = HIST_countFast_wksp(count, &mut max, codeTable, workspace);
let mut cSymbolTypeSizeEstimateInBits = match encType {
SymbolEncodingType_e::set_basic => {
debug_assert!(max <= defaultMax);
if max <= defaultMax {
ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max)
} else {
ERROR(ErrorCode::Generic)
}
}
SymbolEncodingType_e::set_rle => 0,
SymbolEncodingType_e::set_compressed | SymbolEncodingType_e::set_repeat => {
ZSTD_fseBitCost(fseCTable, count, max)
}
};
if crate::common::error::ERR_isError(cSymbolTypeSizeEstimateInBits) {
return nbSeq * 10;
}
for &c in codeTable {
if let Some(bits) = additionalBits {
cSymbolTypeSizeEstimateInBits += bits[c as usize] as usize;
} else {
cSymbolTypeSizeEstimateInBits += c as usize;
}
}
cSymbolTypeSizeEstimateInBits >> 3
}
pub fn ZSTD_estimateSubBlockSize_sequences(
ofCodeTable: &[u8],
llCodeTable: &[u8],
mlCodeTable: &[u8],
nbSeq: usize,
fseTables: &crate::compress::zstd_compress::ZSTD_fseCTables_t,
fseMetadata: &ZSTD_fseCTablesMetadata_t,
countWorkspace: &mut [u32],
workspace: &mut [u32],
writeEntropy: bool,
) -> usize {
use crate::decompress::zstd_decompress_block::{
DefaultMaxOff, LL_bits, LL_defaultNorm, LL_defaultNormLog, ML_bits, ML_defaultNorm,
ML_defaultNormLog, MaxLL, MaxML, MaxOff, OF_defaultNorm, OF_defaultNormLog,
};
const SEQUENCES_SECTION_HEADER_SIZE: usize = 3;
if nbSeq == 0 {
return SEQUENCES_SECTION_HEADER_SIZE;
}
let ofCodeTable = &ofCodeTable[..ofCodeTable.len().min(nbSeq)];
let llCodeTable = &llCodeTable[..llCodeTable.len().min(nbSeq)];
let mlCodeTable = &mlCodeTable[..mlCodeTable.len().min(nbSeq)];
let mut cSeqSizeEstimate = 0usize;
cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(
fseMetadata.ofType,
ofCodeTable,
MaxOff,
nbSeq,
&fseTables.offcodeCTable,
None,
&OF_defaultNorm,
OF_defaultNormLog,
DefaultMaxOff,
countWorkspace,
workspace,
);
cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(
fseMetadata.llType,
llCodeTable,
MaxLL,
nbSeq,
&fseTables.litlengthCTable,
Some(&LL_bits),
&LL_defaultNorm,
LL_defaultNormLog,
MaxLL,
countWorkspace,
workspace,
);
cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(
fseMetadata.mlType,
mlCodeTable,
MaxML,
nbSeq,
&fseTables.matchlengthCTable,
Some(&ML_bits),
&ML_defaultNorm,
ML_defaultNormLog,
MaxML,
countWorkspace,
workspace,
);
if writeEntropy {
cSeqSizeEstimate += fseMetadata.fseTablesSize;
}
cSeqSizeEstimate + SEQUENCES_SECTION_HEADER_SIZE
}
pub fn ZSTD_estimateSubBlockSize(
literals: &[u8],
ofCodeTable: &[u8],
llCodeTable: &[u8],
mlCodeTable: &[u8],
nbSeq: usize,
entropy: &ZSTD_entropyCTables_t,
entropyMetadata: &ZSTD_entropyCTablesMetadata_t,
literalCountWorkspace: &mut [u32],
seqCountWorkspace: &mut [u32],
workspace: &mut [u32],
writeLitEntropy: bool,
writeSeqEntropy: bool,
) -> EstimatedBlockSize {
let estLitSize = ZSTD_estimateSubBlockSize_literal(
literals,
&entropy.huf,
&entropyMetadata.hufMetadata,
literalCountWorkspace,
workspace,
writeLitEntropy,
);
let mut estBlockSize = ZSTD_estimateSubBlockSize_sequences(
ofCodeTable,
llCodeTable,
mlCodeTable,
nbSeq,
&entropy.fse,
&entropyMetadata.fseMetadata,
seqCountWorkspace,
workspace,
writeSeqEntropy,
);
estBlockSize += estLitSize + crate::decompress::zstd_decompress_block::ZSTD_blockHeaderSize;
EstimatedBlockSize {
estLitSize,
estBlockSize,
}
}
#[inline]
pub fn ZSTD_needSequenceEntropyTables(fseMetadata: &ZSTD_fseCTablesMetadata_t) -> i32 {
if matches!(
fseMetadata.llType,
SymbolEncodingType_e::set_compressed | SymbolEncodingType_e::set_rle
) {
return 1;
}
if matches!(
fseMetadata.mlType,
SymbolEncodingType_e::set_compressed | SymbolEncodingType_e::set_rle
) {
return 1;
}
if matches!(
fseMetadata.ofType,
SymbolEncodingType_e::set_compressed | SymbolEncodingType_e::set_rle
) {
return 1;
}
0
}
pub fn countLiterals(seqStore: &SeqStore_t, sp: &[SeqDef], seqCount: usize) -> usize {
let mut total = 0usize;
debug_assert!(seqCount <= sp.len());
let base = seqStore.sequences.as_ptr();
let start = sp.as_ptr();
let start_idx = unsafe { start.offset_from(base) };
debug_assert!(start_idx >= 0);
let start_idx = start_idx as usize;
debug_assert!(start_idx + seqCount <= seqStore.sequences.len());
for n in 0..seqCount {
total += ZSTD_getSequenceLength(seqStore, start_idx + n).litLength as usize;
}
total
}
pub fn sizeBlockSequences(
sp: &[SeqDef],
nbSeqs: usize,
targetBudget: usize,
avgLitCost: usize,
avgSeqCost: usize,
firstSubBlock: i32,
) -> usize {
let mut budget = 0usize;
debug_assert!(firstSubBlock == 0 || firstSubBlock == 1);
debug_assert!(nbSeqs > 0);
debug_assert!(nbSeqs <= sp.len());
let headerSize = firstSubBlock as usize * 120 * BYTESCALE;
budget = budget.wrapping_add(headerSize);
budget = budget.wrapping_add(
(sp[0].litLength as usize)
.wrapping_mul(avgLitCost)
.wrapping_add(avgSeqCost),
);
if budget > targetBudget {
return 1;
}
let mut inSize = (sp[0].litLength as usize)
.wrapping_add(sp[0].mlBase as usize)
.wrapping_add(MINMATCH as usize);
let mut n = 1usize;
while n < nbSeqs {
let currentCost = (sp[n].litLength as usize)
.wrapping_mul(avgLitCost)
.wrapping_add(avgSeqCost);
budget = budget.wrapping_add(currentCost);
inSize = inSize
.wrapping_add(sp[n].litLength as usize)
.wrapping_add(sp[n].mlBase as usize)
.wrapping_add(MINMATCH as usize);
if budget > targetBudget && budget < inSize.wrapping_mul(BYTESCALE) {
break;
}
n += 1;
}
n
}
pub fn ZSTD_compressSuperBlock(
zc: &mut crate::compress::zstd_compress::ZSTD_CCtx,
dst: &mut [u8],
src: &[u8],
lastBlock: u32,
) -> usize {
if zc.seqStore.is_none() {
return 0;
}
let mut entropyMetadata = ZSTD_entropyCTablesMetadata_t::default();
let rc = crate::compress::zstd_compress::ZSTD_buildBlockEntropyStats(
zc.seqStore.as_mut().unwrap(),
&zc.prevEntropy,
&mut zc.nextEntropy,
&zc.appliedParams,
&mut entropyMetadata,
&mut zc.entropyScratch,
);
if crate::common::error::ERR_isError(rc) {
return rc;
}
let seqStore = zc.seqStore.as_ref().unwrap();
ZSTD_compressSubBlock_multi(
seqStore,
&zc.prevEntropy,
&mut zc.nextEntropy,
&zc.prev_rep,
&mut zc.next_rep,
&entropyMetadata,
&zc.appliedParams,
dst,
src,
zc.bmi2,
lastBlock,
&mut zc.entropyScratch.hufCount,
&mut zc.entropyScratch.seqCountWorkspace,
&mut zc.entropyScratch.blockWorkspaceU32,
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compress::seq_store::{SeqStore_t, ZSTD_longLengthType_e};
use crate::compress::zstd_compress::{
ZSTD_CCtx_init_compressStream2, ZSTD_EndDirective, ZSTD_createCCtx,
ZSTD_estimateBlockSize_literal, ZSTD_estimateBlockSize_sequences,
};
#[test]
fn compressSuperBlock_tiny_input_falls_back_to_raw_block() {
let mut cctx = ZSTD_createCCtx().unwrap();
let mut dst = [0u8; 64];
let src = b"superblock-test";
cctx.requestedParams.compressionLevel = crate::compress::zstd_compress::ZSTD_CLEVEL_DEFAULT;
assert_eq!(
ZSTD_CCtx_init_compressStream2(&mut cctx, ZSTD_EndDirective::ZSTD_e_end, src.len()),
0
);
let bss = crate::compress::zstd_compress::ZSTD_buildSeqStore(&mut cctx, src);
assert!(!crate::common::error::ERR_isError(bss));
let rc = ZSTD_compressSuperBlock(&mut cctx, &mut dst, src, 1);
let expected = crate::compress::zstd_compress::ZSTD_noCompressBlock(&mut [0u8; 64], src, 1);
assert_eq!(rc, expected);
}
#[test]
fn need_sequence_entropy_tables_tracks_rle_and_compressed_modes() {
let mut meta = ZSTD_fseCTablesMetadata_t::default();
assert_eq!(ZSTD_needSequenceEntropyTables(&meta), 0);
meta.llType = SymbolEncodingType_e::set_rle;
assert_eq!(ZSTD_needSequenceEntropyTables(&meta), 1);
meta.llType = SymbolEncodingType_e::set_basic;
meta.ofType = SymbolEncodingType_e::set_compressed;
assert_eq!(ZSTD_needSequenceEntropyTables(&meta), 1);
}
#[test]
fn count_literals_uses_decoded_sequence_lengths() {
let seq_store = SeqStore_t {
sequences: vec![
SeqDef {
offBase: 4,
litLength: 2,
mlBase: 3,
},
SeqDef {
offBase: 5,
litLength: 9,
mlBase: 4,
},
SeqDef {
offBase: 6,
litLength: 1,
mlBase: 5,
},
],
literals: Vec::new(),
llCode: Vec::new(),
mlCode: Vec::new(),
ofCode: Vec::new(),
maxNbSeq: 8,
maxNbLit: 0,
longLengthType: ZSTD_longLengthType_e::ZSTD_llt_literalLength,
longLengthPos: 1,
};
assert_eq!(
countLiterals(&seq_store, &seq_store.sequences, 3),
2 + (9 + 0x10000) + 1
);
}
#[test]
fn size_block_sequences_stops_when_budget_crosses_compressible_limit() {
let seqs = vec![
SeqDef {
offBase: 4,
litLength: 10,
mlBase: 7,
},
SeqDef {
offBase: 5,
litLength: 30,
mlBase: 9,
},
SeqDef {
offBase: 6,
litLength: 60,
mlBase: 11,
},
];
let got = sizeBlockSequences(&seqs, seqs.len(), 1000, 10, 50, 0);
assert_eq!(got, 2);
}
#[test]
fn size_block_sequences_first_subblock_header_can_force_minimum_one_seq() {
let seqs = vec![
SeqDef {
offBase: 4,
litLength: 1,
mlBase: 1,
},
SeqDef {
offBase: 5,
litLength: 1,
mlBase: 1,
},
];
let got = sizeBlockSequences(&seqs, seqs.len(), 100, 1, 1, 1);
assert_eq!(got, 1);
}
#[test]
fn size_block_sequences_uses_size_t_wrapping_arithmetic() {
let seqs = vec![
SeqDef {
offBase: 4,
litLength: u16::MAX,
mlBase: u16::MAX,
},
SeqDef {
offBase: 5,
litLength: u16::MAX,
mlBase: u16::MAX,
},
];
let got = sizeBlockSequences(&seqs, seqs.len(), usize::MAX, usize::MAX, usize::MAX, 0);
assert_eq!(got, seqs.len());
}
#[test]
fn estimate_subblock_literal_uses_fixed_three_byte_header() {
let literals = vec![b'a'; 300];
let huf = ZSTD_hufCTables_t::default();
let mut huf_meta = ZSTD_hufCTablesMetadata_t::default();
huf_meta.hType = SymbolEncodingType_e::set_basic;
let mut wksp = vec![0u32; crate::compress::hist::HIST_WKSP_SIZE_U32];
let mut huf_count = vec![0u32; 256];
let sub = ZSTD_estimateSubBlockSize_literal(
&literals,
&huf,
&huf_meta,
&mut huf_count,
&mut wksp,
false,
);
let block = ZSTD_estimateBlockSize_literal(
&literals,
&huf,
&huf_meta,
&mut huf_count,
&mut wksp,
false,
);
assert_eq!(sub, literals.len());
assert_eq!(block, literals.len());
}
#[test]
fn estimate_subblock_sequences_zero_seq_is_three_byte_header() {
let fse = crate::compress::zstd_compress::ZSTD_fseCTables_t::default();
let meta = ZSTD_fseCTablesMetadata_t::default();
let mut wksp = vec![0u32; crate::compress::hist::HIST_WKSP_SIZE_U32];
let mut count_wksp =
vec![0u32; crate::decompress::zstd_decompress_block::MaxML as usize + 1];
assert_eq!(
ZSTD_estimateSubBlockSize_sequences(
&[],
&[],
&[],
0,
&fse,
&meta,
&mut count_wksp,
&mut wksp,
false,
),
3
);
assert_eq!(
ZSTD_estimateBlockSize_sequences(
&[],
&[],
&[],
0,
&fse,
&meta,
&mut count_wksp,
&mut wksp,
false,
),
2
);
}
#[test]
fn estimate_subblock_sequences_uses_only_nbseq_codes() {
let fse = crate::compress::zstd_compress::ZSTD_fseCTables_t::default();
let meta = ZSTD_fseCTablesMetadata_t::default();
let of_codes = [1, 2, 28, 31];
let ll_codes = [0, 1, 2, 35];
let ml_codes = [0, 1, 2, 52];
let mut wksp = vec![0u32; crate::compress::hist::HIST_WKSP_SIZE_U32];
let mut count_wksp =
vec![0u32; crate::decompress::zstd_decompress_block::MaxML as usize + 1];
let with_tail = ZSTD_estimateSubBlockSize_sequences(
&of_codes,
&ll_codes,
&ml_codes,
3,
&fse,
&meta,
&mut count_wksp,
&mut wksp,
false,
);
let trimmed = ZSTD_estimateSubBlockSize_sequences(
&of_codes[..3],
&ll_codes[..3],
&ml_codes[..3],
3,
&fse,
&meta,
&mut count_wksp,
&mut wksp,
false,
);
assert_eq!(with_tail, trimmed);
}
#[test]
fn stream_accumulator_min_matches_upstream_word_size() {
let expected = if core::mem::size_of::<usize>() == 4 {
25
} else {
57
};
assert_eq!(STREAM_ACCUMULATOR_MIN(), expected);
}
#[test]
fn estimate_subblock_size_adds_subsections_and_block_header() {
let entropy = ZSTD_entropyCTables_t::default();
let meta = ZSTD_entropyCTablesMetadata_t::default();
let mut wksp = vec![0u32; crate::compress::hist::HIST_WKSP_SIZE_U32];
let mut huf_count = vec![0u32; 256];
let mut seq_count =
vec![0u32; crate::decompress::zstd_decompress_block::MaxML as usize + 1];
let literals = b"abcde";
let est = ZSTD_estimateSubBlockSize(
literals,
&[],
&[],
&[],
0,
&entropy,
&meta,
&mut huf_count,
&mut seq_count,
&mut wksp,
false,
false,
);
assert_eq!(est.estLitSize, literals.len());
assert_eq!(
est.estBlockSize,
literals.len() + 3 + crate::decompress::zstd_decompress_block::ZSTD_blockHeaderSize
);
}
#[test]
fn compress_subblock_literal_raw_matches_no_compress_path() {
let huf = ZSTD_hufCTables_t::default();
let meta = ZSTD_hufCTablesMetadata_t::default();
let literals = b"hello";
let mut got = [0u8; 32];
let mut expected = [0u8; 32];
let mut entropy_written = -1;
let n = ZSTD_compressSubBlock_literal(
&huf.CTable,
&meta,
literals,
&mut got,
0,
false,
&mut entropy_written,
);
let m = crate::compress::zstd_compress_literals::ZSTD_noCompressLiterals(
&mut expected,
literals,
);
assert_eq!(entropy_written, 0);
assert_eq!(n, m);
assert_eq!(&got[..n], &expected[..m]);
}
#[test]
fn compress_subblock_literal_compressed_tiny_dst_returns_zero() {
let huf = ZSTD_hufCTables_t::default();
let mut meta = ZSTD_hufCTablesMetadata_t::default();
meta.hType = SymbolEncodingType_e::set_repeat;
let mut dst = [0u8; 2];
let mut entropy_written = -1;
let n = ZSTD_compressSubBlock_literal(
&huf.CTable,
&meta,
b"hello",
&mut dst,
0,
false,
&mut entropy_written,
);
assert_eq!(n, 0);
assert_eq!(entropy_written, 0);
}
#[test]
fn compress_subblock_sequences_zero_seq_writes_count_only() {
let fse = crate::compress::zstd_compress::ZSTD_fseCTables_t::default();
let meta = ZSTD_fseCTablesMetadata_t::default();
let params = ZSTD_CCtx_params::default();
let mut dst = [0u8; 16];
let mut entropy_written = -1;
let n = ZSTD_compressSubBlock_sequences(
&fse,
&meta,
&[],
0,
&[],
&[],
&[],
¶ms,
&mut dst,
0,
false,
&mut entropy_written,
);
assert_eq!(n, 1);
assert_eq!(dst[0], 0);
assert_eq!(entropy_written, 0);
}
#[test]
fn compress_subblock_sequences_tiny_dst_rejects_entropy_table_copy() {
let fse = crate::compress::zstd_compress::ZSTD_fseCTables_t::default();
let mut meta = ZSTD_fseCTablesMetadata_t::default();
meta.fseTablesSize = 4;
let params = ZSTD_CCtx_params::default();
let seq = [SeqDef {
offBase: 4,
litLength: 1,
mlBase: 3,
}];
let codes = [0u8];
let mut dst = [0u8; 3 + 1];
let mut entropy_written = -1;
let n = ZSTD_compressSubBlock_sequences(
&fse,
&meta,
&seq,
1,
&codes,
&codes,
&codes,
¶ms,
&mut dst,
0,
true,
&mut entropy_written,
);
assert_eq!(n, 0);
assert_eq!(entropy_written, 0);
}
#[test]
fn compress_subblock_with_raw_literals_and_no_sequences_builds_compressed_block_header() {
let entropy = ZSTD_entropyCTables_t::default();
let meta = ZSTD_entropyCTablesMetadata_t::default();
let params = ZSTD_CCtx_params::default();
let mut dst = [0u8; 64];
let mut lit_entropy_written = -1;
let mut seq_entropy_written = -1;
let n = ZSTD_compressSubBlock(
&entropy,
&meta,
&[],
0,
b"abc",
&[],
&[],
&[],
¶ms,
&mut dst,
0,
false,
false,
&mut lit_entropy_written,
&mut seq_entropy_written,
1,
);
assert_eq!(n, 3 + 4 + 1);
assert_eq!(lit_entropy_written, 0);
assert_eq!(seq_entropy_written, 0);
assert_eq!(dst[0] & 1, 1);
}
#[test]
fn compress_subblock_tiny_dst_returns_zero() {
let entropy = ZSTD_entropyCTables_t::default();
let meta = ZSTD_entropyCTablesMetadata_t::default();
let params = ZSTD_CCtx_params::default();
let mut dst = [0u8; 2];
let mut lit_entropy_written = -1;
let mut seq_entropy_written = -1;
let n = ZSTD_compressSubBlock(
&entropy,
&meta,
&[],
0,
b"abc",
&[],
&[],
&[],
¶ms,
&mut dst,
0,
false,
false,
&mut lit_entropy_written,
&mut seq_entropy_written,
1,
);
assert_eq!(n, 0);
}
#[test]
fn seq_decompressed_size_adds_match_lengths_and_passed_literal_budget() {
let seq_store = SeqStore_t {
sequences: vec![
SeqDef {
offBase: 4,
litLength: 2,
mlBase: 3,
},
SeqDef {
offBase: 5,
litLength: 4,
mlBase: 7,
},
],
literals: Vec::new(),
llCode: Vec::new(),
mlCode: Vec::new(),
ofCode: Vec::new(),
maxNbSeq: 8,
maxNbLit: 0,
longLengthType: ZSTD_longLengthType_e::ZSTD_llt_none,
longLengthPos: 0,
};
let got = ZSTD_seqDecompressedSize(&seq_store, &seq_store.sequences, 2, 6, 0);
assert_eq!(got, 6 + (3 + 3) + (7 + 3));
}
#[test]
fn compress_subblock_multi_no_sequences_falls_back_to_single_raw_block() {
let seq_store = SeqStore_t {
sequences: Vec::new(),
literals: b"hello superblock".to_vec(),
llCode: Vec::new(),
mlCode: Vec::new(),
ofCode: Vec::new(),
maxNbSeq: 0,
maxNbLit: 0,
longLengthType: ZSTD_longLengthType_e::ZSTD_llt_none,
longLengthPos: 0,
};
let prev_entropy = ZSTD_entropyCTables_t::default();
let mut next_entropy = ZSTD_entropyCTables_t::default();
let meta = ZSTD_entropyCTablesMetadata_t::default();
let params = ZSTD_CCtx_params::default();
let mut dst = [0u8; 128];
let src = b"hello superblock";
let mut wksp = vec![0u32; crate::compress::hist::HIST_WKSP_SIZE_U32];
let mut huf_count = vec![0u32; 256];
let mut seq_count =
vec![0u32; crate::decompress::zstd_decompress_block::MaxML as usize + 1];
let prev_rep = [1, 4, 8];
let mut next_rep = [1, 4, 8];
let got = ZSTD_compressSubBlock_multi(
&seq_store,
&prev_entropy,
&mut next_entropy,
&prev_rep,
&mut next_rep,
&meta,
¶ms,
&mut dst,
src,
0,
1,
&mut huf_count,
&mut seq_count,
&mut wksp,
);
let expected =
crate::compress::zstd_compress::ZSTD_noCompressBlock(&mut [0u8; 128], src, 1);
assert_eq!(got, expected);
assert_eq!(next_rep, prev_rep);
}
#[test]
fn compress2_roundtrips_when_target_block_size_enabled() {
use crate::decompress::zstd_decompress::ZSTD_decompress;
let src = b"aaaaabbbbbcccccdddddeeeeeaaaaabbbbbcccccdddddeeeee";
let mut cctx = ZSTD_createCCtx().unwrap();
cctx.requestedParams.compressionLevel = 5;
cctx.requestedParams.targetCBlockSize = 32;
let mut compressed =
vec![0u8; crate::compress::zstd_compress::ZSTD_compressBound(src.len())];
let csize = crate::compress::zstd_compress::ZSTD_compress2(&mut cctx, &mut compressed, src);
assert!(csize > 0);
let mut roundtrip = vec![0u8; src.len()];
let dsize = ZSTD_decompress(&mut roundtrip, &compressed[..csize]);
assert_eq!(dsize, src.len());
assert_eq!(roundtrip, src);
}
}