use crate::common::error::{ErrorCode, ERROR};
use crate::common::mem::{MEM_readLE16, MEM_readLE32, MEM_readLE64};
pub const ZSTD_MAGICNUMBER: u32 = 0xFD2FB528;
pub const ZSTD_MAGIC_DICTIONARY: u32 = 0xEC30A437;
pub const ZSTD_MAGIC_SKIPPABLE_START: u32 = 0x184D2A50;
pub const ZSTD_MAGIC_SKIPPABLE_MASK: u32 = 0xFFFFFFF0;
pub const ZSTD_FRAMEIDSIZE: usize = 4;
pub const ZSTD_SKIPPABLEHEADERSIZE: usize = 8;
pub const ZSTD_WINDOWLOG_ABSOLUTEMIN: u32 = 10;
pub const ZSTD_WINDOWLOG_MAX_64: u32 = 31;
pub const ZSTD_WINDOWLOG_MAX_32: u32 = 30;
pub const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX;
pub const ZSTD_CONTENTSIZE_ERROR: u64 = u64::MAX - 1;
pub const ZSTD_fcs_fieldSize: [usize; 4] = [0, 2, 4, 8];
pub const ZSTD_did_fieldSize: [usize; 4] = [0, 1, 2, 4];
const ZSTD_HISTORY_COMPACT_SLACK_BLOCKS: usize = 12;
#[inline]
const fn ZSTD_maxWindowSize_default() -> u64 {
(1u64 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) + 1
}
#[inline]
fn ZSTD_highbit64(v: u64) -> i32 {
if v == 0 {
0
} else {
(u64::BITS - 1 - v.leading_zeros()) as i32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ZSTD_format_e {
#[default]
ZSTD_f_zstd1,
ZSTD_f_zstd1_magicless,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ZSTD_FrameType_e {
#[default]
ZSTD_frame,
ZSTD_skippableFrame,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ZSTD_FrameHeader {
pub frameContentSize: u64,
pub windowSize: u64,
pub blockSizeMax: u32,
pub frameType: ZSTD_FrameType_e,
pub headerSize: u32,
pub dictID: u32,
pub checksumFlag: u32,
pub _reserved1: u32,
pub _reserved2: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ZSTD_dStreamStage {
#[default]
zdss_init = 0,
zdss_loadHeader = 1,
zdss_read = 2,
zdss_load = 3,
zdss_flush = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ZSTD_dictUses_e {
ZSTD_use_indefinitely = -1,
#[default]
ZSTD_dont_use = 0,
ZSTD_use_once = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ZSTD_dStage {
#[default]
ZSTDds_getFrameHeaderSize = 0,
ZSTDds_decodeFrameHeader = 1,
ZSTDds_decodeBlockHeader = 2,
ZSTDds_decompressBlock = 3,
ZSTDds_decompressLastBlock = 4,
ZSTDds_checkChecksum = 5,
ZSTDds_decodeSkippableHeader = 6,
ZSTDds_skipFrame = 7,
ZSTDds_frameComplete = 8,
}
#[inline]
pub const fn ZSTD_FRAMEHEADERSIZE_PREFIX(format: ZSTD_format_e) -> usize {
match format {
ZSTD_format_e::ZSTD_f_zstd1 => 5,
ZSTD_format_e::ZSTD_f_zstd1_magicless => 1,
}
}
#[inline]
pub const fn ZSTD_FRAMEHEADERSIZE_MIN(format: ZSTD_format_e) -> usize {
match format {
ZSTD_format_e::ZSTD_f_zstd1 => 6,
ZSTD_format_e::ZSTD_f_zstd1_magicless => 2,
}
}
#[inline]
pub fn ZSTD_startingInputLength(format: ZSTD_format_e) -> usize {
match format {
ZSTD_format_e::ZSTD_f_zstd1 => ZSTD_FRAMEIDSIZE + 1,
ZSTD_format_e::ZSTD_f_zstd1_magicless => 1,
}
}
pub fn ZSTD_frameHeaderSize_internal(src: &[u8], format: ZSTD_format_e) -> usize {
let minInputSize = ZSTD_startingInputLength(format);
if src.len() < minInputSize {
return ERROR(ErrorCode::SrcSizeWrong);
}
let fhd = src[minInputSize - 1];
let dictID = (fhd & 3) as usize;
let singleSegment = ((fhd >> 5) & 1) as usize;
let fcsId = (fhd >> 6) as usize;
minInputSize
+ (1 - singleSegment) + ZSTD_did_fieldSize[dictID]
+ ZSTD_fcs_fieldSize[fcsId]
+ (singleSegment & (fcsId == 0) as usize) }
pub fn ZSTD_frameHeaderSize(src: &[u8]) -> usize {
ZSTD_frameHeaderSize_internal(src, ZSTD_format_e::ZSTD_f_zstd1)
}
pub fn ZSTD_getFrameHeader_advanced(
zfh: &mut ZSTD_FrameHeader,
src: &[u8],
format: ZSTD_format_e,
) -> usize {
let srcSize = src.len();
let minInputSize = ZSTD_startingInputLength(format);
if srcSize < minInputSize {
if srcSize > 0 && format != ZSTD_format_e::ZSTD_f_zstd1_magicless {
let toCopy = 4.min(srcSize);
let mut hbuf = ZSTD_MAGICNUMBER.to_le_bytes();
hbuf[..toCopy].copy_from_slice(&src[..toCopy]);
if MEM_readLE32(&hbuf) != ZSTD_MAGICNUMBER {
let mut hbuf2 = ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes();
hbuf2[..toCopy].copy_from_slice(&src[..toCopy]);
if (MEM_readLE32(&hbuf2) & ZSTD_MAGIC_SKIPPABLE_MASK) != ZSTD_MAGIC_SKIPPABLE_START
{
return ERROR(ErrorCode::PrefixUnknown);
}
}
}
return minInputSize;
}
*zfh = ZSTD_FrameHeader::default();
if format != ZSTD_format_e::ZSTD_f_zstd1_magicless
&& MEM_readLE32(&src[..4]) != ZSTD_MAGICNUMBER
{
if (MEM_readLE32(&src[..4]) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START {
if srcSize < ZSTD_SKIPPABLEHEADERSIZE {
return ZSTD_SKIPPABLEHEADERSIZE;
}
zfh.frameType = ZSTD_FrameType_e::ZSTD_skippableFrame;
zfh.dictID = MEM_readLE32(&src[..4]) - ZSTD_MAGIC_SKIPPABLE_START;
zfh.headerSize = ZSTD_SKIPPABLEHEADERSIZE as u32;
zfh.frameContentSize =
MEM_readLE32(&src[ZSTD_FRAMEIDSIZE..ZSTD_FRAMEIDSIZE + 4]) as u64;
return 0;
}
return ERROR(ErrorCode::PrefixUnknown);
}
let fhsize = ZSTD_frameHeaderSize_internal(src, format);
if crate::common::error::ERR_isError(fhsize) {
return fhsize;
}
if srcSize < fhsize {
return fhsize;
}
zfh.headerSize = fhsize as u32;
let fhdByte = src[minInputSize - 1];
let mut pos = minInputSize;
let dictIDSizeCode = (fhdByte & 3) as u32;
let checksumFlag = ((fhdByte >> 2) & 1) as u32;
let singleSegment = ((fhdByte >> 5) & 1) as u32;
let fcsID = (fhdByte >> 6) as u32;
if (fhdByte & 0x08) != 0 {
return ERROR(ErrorCode::FrameParameterUnsupported);
}
let mut windowSize: u64 = 0;
let mut dictID: u32 = 0;
let mut frameContentSize: u64 = ZSTD_CONTENTSIZE_UNKNOWN;
if singleSegment == 0 {
let wlByte = src[pos];
pos += 1;
let windowLog = ((wlByte >> 3) as u32) + ZSTD_WINDOWLOG_ABSOLUTEMIN;
let winLogMax = if core::mem::size_of::<usize>() == 8 {
ZSTD_WINDOWLOG_MAX_64
} else {
ZSTD_WINDOWLOG_MAX_32
};
if windowLog > winLogMax {
return ERROR(ErrorCode::FrameParameterWindowTooLarge);
}
windowSize = 1u64 << windowLog;
windowSize += (windowSize >> 3) * (wlByte & 7) as u64;
}
match dictIDSizeCode {
0 => {}
1 => {
dictID = src[pos] as u32;
pos += 1;
}
2 => {
dictID = MEM_readLE16(&src[pos..pos + 2]) as u32;
pos += 2;
}
_ => {
dictID = MEM_readLE32(&src[pos..pos + 4]);
pos += 4;
}
}
match fcsID {
0 => {
if singleSegment != 0 {
frameContentSize = src[pos] as u64;
}
}
1 => frameContentSize = MEM_readLE16(&src[pos..pos + 2]) as u64 + 256,
2 => frameContentSize = MEM_readLE32(&src[pos..pos + 4]) as u64,
_ => frameContentSize = MEM_readLE64(&src[pos..pos + 8]),
}
if singleSegment != 0 {
windowSize = frameContentSize;
}
zfh.frameType = ZSTD_FrameType_e::ZSTD_frame;
zfh.frameContentSize = frameContentSize;
zfh.windowSize = windowSize;
zfh.blockSizeMax =
windowSize.min(crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX as u64) as u32;
zfh.dictID = dictID;
zfh.checksumFlag = checksumFlag;
0
}
pub fn ZSTD_getFrameHeader(zfh: &mut ZSTD_FrameHeader, src: &[u8]) -> usize {
ZSTD_getFrameHeader_advanced(zfh, src, ZSTD_format_e::ZSTD_f_zstd1)
}
#[cfg(test)]
#[allow(clippy::field_reassign_with_default)]
mod tests {
use super::*;
fn build_minimal_zstd_dict(dictID: u32, content: &[u8]) -> Vec<u8> {
use crate::common::mem::MEM_writeLE32;
use crate::compress::fse_compress::{
FSE_buildCTable_wksp, FSE_normalizeCount, FSE_writeNCount,
};
use crate::compress::huf_compress::{
HUF_buildCTable_wksp, HUF_writeCTable, HUF_CTABLE_WORKSPACE_SIZE_U32,
};
use crate::decompress::zstd_decompress_block::{
LL_defaultNorm, LL_defaultNormLog, ML_defaultNorm, ML_defaultNormLog, MaxLL, MaxML,
MaxOff, OF_defaultNorm, OF_defaultNormLog,
};
let mut out = Vec::new();
let mut magic_bytes = [0u8; 4];
MEM_writeLE32(&mut magic_bytes, ZSTD_MAGICNUMBER_DICTIONARY);
out.extend_from_slice(&magic_bytes);
let mut id_bytes = [0u8; 4];
MEM_writeLE32(&mut id_bytes, dictID);
out.extend_from_slice(&id_bytes);
let mut count = [0u32; 256];
for &b in content.iter() {
count[b as usize] += 1;
}
for (i, c) in count.iter_mut().enumerate().take(16) {
if *c == 0 {
*c = 1;
let _ = i;
}
}
let maxSymbolValue = count
.iter()
.enumerate()
.rposition(|(_, &c)| c > 0)
.unwrap_or(0) as u32;
let totalCount: usize = count.iter().sum::<u32>() as usize;
let tableLog =
crate::compress::huf_compress::HUF_optimalTableLog(11, totalCount, maxSymbolValue);
let mut ct = vec![crate::compress::huf_compress::HUF_CElt::default(); 257];
let mut wksp = vec![0u32; HUF_CTABLE_WORKSPACE_SIZE_U32];
FSE_buildCTable_wksp(
&mut [0u32; 512], &[0i16; 1],
0,
5,
&mut [0u8; 1024],
);
let _ = HUF_buildCTable_wksp(&mut ct, &count, maxSymbolValue, tableLog, &mut wksp);
let mut huf_hdr = vec![0u8; 512];
let w = HUF_writeCTable(&mut huf_hdr, &ct, maxSymbolValue, tableLog);
assert!(!crate::common::error::ERR_isError(w));
out.extend_from_slice(&huf_hdr[..w]);
let mut fse_buf = vec![0u8; 256];
let w = FSE_writeNCount(&mut fse_buf, &OF_defaultNorm, MaxOff, OF_defaultNormLog);
assert!(!crate::common::error::ERR_isError(w));
out.extend_from_slice(&fse_buf[..w]);
let w = FSE_writeNCount(&mut fse_buf, &ML_defaultNorm, MaxML, ML_defaultNormLog);
assert!(!crate::common::error::ERR_isError(w));
out.extend_from_slice(&fse_buf[..w]);
let w = FSE_writeNCount(&mut fse_buf, &LL_defaultNorm, MaxLL, LL_defaultNormLog);
assert!(!crate::common::error::ERR_isError(w));
out.extend_from_slice(&fse_buf[..w]);
let _ = FSE_normalizeCount;
assert!(content.len() > 1);
let safe = ((content.len() as u32) - 1).clamp(1, 8);
for r in [safe, safe.saturating_sub(1).max(1), 1u32] {
let mut rb = [0u8; 4];
MEM_writeLE32(&mut rb, r);
out.extend_from_slice(&rb);
}
out.extend_from_slice(content);
out
}
#[test]
fn insertDictionary_roundtrips_magic_prefix_dict() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let content = b"zstd-dict-content-bytes-for-compression-context";
let dict = build_minimal_zstd_dict(0x1234_5678, content);
let mut dctx = ZSTD_DCtx::new();
let rc = ZSTD_decompress_insertDictionary(&mut dctx, &dict);
assert!(
!crate::common::error::ERR_isError(rc),
"insertDictionary failed: {}",
crate::common::error::ERR_getErrorName(rc)
);
assert_eq!(dctx.dictID, 0x1234_5678);
assert_eq!(dctx.stream_dict, content);
assert_eq!(dctx.litEntropy, 1);
assert_eq!(dctx.fseEntropy, 1);
}
#[test]
fn insertDictionary_accepts_repcode_equal_to_content_size() {
use crate::common::mem::MEM_writeLE32;
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let content = b"12345678";
let mut dict = build_minimal_zstd_dict(0x1020_3040, content);
let rep_pos = dict.len() - content.len() - 12;
MEM_writeLE32(&mut dict[rep_pos..rep_pos + 4], content.len() as u32);
let mut dctx = ZSTD_DCtx::new();
let rc = ZSTD_decompress_insertDictionary(&mut dctx, &dict);
assert!(
!crate::common::error::ERR_isError(rc),
"repcode equal to content size must be accepted: {}",
crate::common::error::ERR_getErrorName(rc)
);
assert_eq!(dctx.ddict_rep[0], content.len() as u32);
assert_eq!(dctx.stream_dict, content);
}
#[test]
fn compress_insertDictionary_roundtrips_magic_prefix() {
use crate::compress::zstd_compress::{ZSTD_compress_insertDictionary, ZSTD_createCCtx};
use crate::compress::zstd_compress_literals::HUF_repeat;
use crate::decompress::zstd_ddict::ZSTD_dictContentType_e;
let content = b"zstd-dict-content-bytes-for-compression-context";
let dict = build_minimal_zstd_dict(0xABCD_1234, content);
let mut cctx = ZSTD_createCCtx().unwrap();
let params = cctx.requestedParams;
let rc = ZSTD_compress_insertDictionary(
&mut cctx,
¶ms,
&dict,
ZSTD_dictContentType_e::ZSTD_dct_auto,
);
assert!(
!crate::common::error::ERR_isError(rc),
"compress_insertDictionary failed: {}",
crate::common::error::ERR_getErrorName(rc)
);
assert_eq!(cctx.dictID, 0xABCD_1234);
assert_eq!(cctx.stream_dict, content);
assert_ne!(cctx.prevEntropy.huf.repeatMode, HUF_repeat::HUF_repeat_none);
}
#[test]
fn insertDictionary_raw_content_stashes_bytes() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let raw = b"this is not a zstd dict, just arbitrary bytes";
let mut dctx = ZSTD_DCtx::new();
let rc = ZSTD_decompress_insertDictionary(&mut dctx, raw);
assert!(!crate::common::error::ERR_isError(rc));
assert_eq!(dctx.dictID, 0);
assert_eq!(dctx.stream_dict, raw);
}
#[test]
fn zstd_sizeof_dctx_includes_owned_tables() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let dctx = ZSTD_DCtx::new();
let sz = ZSTD_sizeof_DCtx(&dctx);
assert!(sz > 4096, "sizeof_DCtx unexpectedly small: {sz}");
assert_eq!(ZSTD_sizeof_DStream(&dctx), sz);
}
#[test]
fn zstd_sizeof_dctx_grows_when_dict_loaded() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let mut dctx = ZSTD_DCtx::new();
let before = ZSTD_sizeof_DCtx(&dctx);
let dict = vec![0xABu8; 4096];
ZSTD_DCtx_loadDictionary(&mut dctx, &dict);
let after = ZSTD_sizeof_DCtx(&dctx);
assert!(
after >= before + dict.len(),
"sizeof_DCtx did not reflect loaded dict: before={before} after={after}"
);
}
#[test]
fn zstd_dParam_getBounds_windowLogMax_range() {
let b = ZSTD_dParam_getBounds(ZSTD_dParameter::ZSTD_d_windowLogMax);
assert_eq!(b.error, 0);
let expected_upper = if crate::common::mem::MEM_32bits() != 0 {
30
} else {
31
};
assert_eq!((b.lowerBound, b.upperBound), (10, expected_upper));
}
#[test]
fn fresh_DCtx_getParameter_windowLogMax_returns_LIMIT_DEFAULT() {
let dctx = ZSTD_DCtx::default();
let mut got = -999i32;
ZSTD_DCtx_getParameter(&dctx, ZSTD_dParameter::ZSTD_d_windowLogMax, &mut got);
assert_eq!(got, ZSTD_WINDOWLOG_LIMIT_DEFAULT as i32);
}
#[test]
fn DCtx_setParameter_windowLogMax_maps_zero_to_LIMIT_DEFAULT() {
let mut dctx = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_setParameter(&mut dctx, ZSTD_dParameter::ZSTD_d_windowLogMax, 0);
assert_eq!(rc, 0);
let mut got = -1i32;
ZSTD_DCtx_getParameter(&dctx, ZSTD_dParameter::ZSTD_d_windowLogMax, &mut got);
assert_eq!(got, 27);
}
#[test]
fn DCtx_setParameter_windowLogMax_rejects_out_of_range() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
let mut dctx = ZSTD_DCtx::default();
for oor in [-1, 9, 40, 100] {
let rc = ZSTD_DCtx_setParameter(&mut dctx, ZSTD_dParameter::ZSTD_d_windowLogMax, oor);
assert!(ERR_isError(rc), "expected error for value={oor}");
assert_eq!(ERR_getErrorCode(rc), ErrorCode::ParameterOutOfBound);
}
}
#[test]
fn dParam_bounds_accept_windowLog_at_upper_edge_via_setMaxWindowSize() {
let mut dctx = ZSTD_DCtx::default();
let min_size = 1usize << 10;
assert_eq!(ZSTD_DCtx_setMaxWindowSize(&mut dctx, min_size), 0);
let upper = if crate::common::mem::MEM_32bits() != 0 {
30
} else {
31
};
let max_size = 1usize << upper;
assert_eq!(ZSTD_DCtx_setMaxWindowSize(&mut dctx, max_size), 0);
}
#[test]
fn zstd_dstream_buffer_sizes_nonzero() {
assert!(ZSTD_DStreamInSize() > 0);
assert!(ZSTD_DStreamOutSize() > 0);
}
#[test]
fn zstd_dctx_setParameter_windowLogMax_roundtrips() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let mut dctx = ZSTD_DCtx::new();
let rc = ZSTD_DCtx_setParameter(&mut dctx, ZSTD_dParameter::ZSTD_d_windowLogMax, 20);
assert_eq!(rc, 0);
let mut v = 0i32;
ZSTD_DCtx_getParameter(&dctx, ZSTD_dParameter::ZSTD_d_windowLogMax, &mut v);
assert_eq!(v, 20);
}
#[test]
fn zstd_dctx_reset_clears_streaming_state() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let mut dctx = ZSTD_DCtx::new();
dctx.stream_in_buffer.extend_from_slice(b"pending");
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_session_only);
assert!(dctx.stream_in_buffer.is_empty());
}
#[test]
fn zstd_estimate_dctx_size_positive() {
let s = ZSTD_estimateDCtxSize();
assert!(s > 0);
assert!(ZSTD_estimateDStreamSize(1 << 17) > s);
}
#[test]
fn zstd_estimate_dstream_size_from_frame_reflects_window() {
let src: Vec<u8> = b"small src for small window".to_vec();
let mut dst = vec![0u8; 256];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut dst, &src, 1);
dst.truncate(n);
let from_frame = ZSTD_estimateDStreamSize_fromFrame(&dst);
let err = ZSTD_estimateDStreamSize_fromFrame(&[0xFF, 0xFF, 0xFF, 0xFF]);
assert!(from_frame > 0);
assert!(crate::common::error::ERR_isError(err));
}
#[test]
fn estimateDStreamSize_fromFrame_rejects_single_segment_window_above_max() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0xE0); src.extend_from_slice(&(1u64 << 40).to_le_bytes());
let rc = ZSTD_estimateDStreamSize_fromFrame(&src);
assert!(ERR_isError(rc));
assert_eq!(
ERR_getErrorCode(rc),
ErrorCode::FrameParameterWindowTooLarge
);
}
#[test]
fn zstd_decompressBlock_roundtrips_a_compressed_block_body() {
use crate::decompress::zstd_decompress_block::{
blockProperties_t, blockType_e, ZSTD_DCtx, ZSTD_blockHeaderSize, ZSTD_getcBlockSize,
};
let src: Vec<u8> = b"hello block api. "
.iter()
.cycle()
.take(200)
.copied()
.collect();
let mut frame = vec![0u8; 1024];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut frame, &src, 1);
frame.truncate(n);
let mut zfh = super::ZSTD_FrameHeader::default();
let rc = super::ZSTD_getFrameHeader(&mut zfh, &frame);
assert_eq!(rc, 0);
let body_start = zfh.headerSize as usize;
let mut bp = blockProperties_t {
blockType: blockType_e::bt_raw,
lastBlock: 0,
origSize: 0,
};
let body_size = ZSTD_getcBlockSize(&frame[body_start..], &mut bp);
if bp.blockType == blockType_e::bt_compressed {
let body = &frame
[body_start + ZSTD_blockHeaderSize..body_start + ZSTD_blockHeaderSize + body_size];
let mut dctx = ZSTD_DCtx::new();
dctx.isFrameDecompression = 1;
let mut out = vec![0u8; src.len() + 64];
let d = ZSTD_decompressBlock(&mut dctx, &mut out, body);
assert!(!crate::common::error::ERR_isError(d));
assert_eq!(&out[..d], &src[..]);
assert_eq!(dctx.isFrameDecompression, 0);
assert_eq!(dctx.prefixStart, Some(out.as_ptr() as usize));
assert_eq!(dctx.previousDstEnd, Some(out.as_ptr() as usize + d));
}
}
#[test]
fn decompressBlock_rotates_continuity_between_dst_buffers() {
use crate::decompress::zstd_decompress_block::{
blockProperties_t, blockType_e, ZSTD_DCtx, ZSTD_blockHeaderSize, ZSTD_getcBlockSize,
};
let src: Vec<u8> = b"standalone block continuity "
.iter()
.cycle()
.take(256)
.copied()
.collect();
let mut frame = vec![0u8; 1024];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut frame, &src, 1);
assert!(!crate::common::error::ERR_isError(n));
frame.truncate(n);
let mut zfh = super::ZSTD_FrameHeader::default();
assert_eq!(super::ZSTD_getFrameHeader(&mut zfh, &frame), 0);
let body_start = zfh.headerSize as usize;
let mut bp = blockProperties_t {
blockType: blockType_e::bt_raw,
lastBlock: 0,
origSize: 0,
};
let body_size = ZSTD_getcBlockSize(&frame[body_start..], &mut bp);
assert_eq!(bp.blockType, blockType_e::bt_compressed);
let body = &frame
[body_start + ZSTD_blockHeaderSize..body_start + ZSTD_blockHeaderSize + body_size];
let mut dctx = ZSTD_DCtx::new();
let mut first = vec![0u8; src.len() + 64];
let decoded = ZSTD_decompressBlock(&mut dctx, &mut first, body);
assert!(!crate::common::error::ERR_isError(decoded));
let first_end = first.as_ptr() as usize + decoded;
assert_eq!(dctx.previousDstEnd, Some(first_end));
let mut second = vec![0u8; src.len() + 64];
let decoded = ZSTD_decompressBlock(&mut dctx, &mut second, body);
assert!(!crate::common::error::ERR_isError(decoded));
assert_eq!(dctx.dictEnd, Some(first_end));
assert_eq!(dctx.prefixStart, Some(second.as_ptr() as usize));
assert_eq!(
dctx.previousDstEnd,
Some(second.as_ptr() as usize + decoded)
);
}
#[test]
fn decompressBlock_updates_previous_dst_end_after_zero_output() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let mut dctx = ZSTD_DCtx::new();
let previous = vec![0u8; 8];
dctx.prefixStart = Some(previous.as_ptr() as usize);
dctx.previousDstEnd = Some(previous.as_ptr() as usize + previous.len());
let mut dst = vec![0u8; 16];
let decoded = ZSTD_decompressBlock(&mut dctx, &mut dst, &[0, 0]);
assert!(
!crate::common::error::ERR_isError(decoded),
"decompressBlock failed: {}",
crate::common::error::ERR_getErrorName(decoded)
);
assert_eq!(decoded, 0);
assert_eq!(dctx.prefixStart, Some(dst.as_ptr() as usize));
assert_eq!(dctx.previousDstEnd, Some(dst.as_ptr() as usize));
}
#[test]
fn windowlog_and_contentsize_constants_match_upstream() {
assert_eq!(ZSTD_WINDOWLOG_ABSOLUTEMIN, 10);
assert_eq!(ZSTD_WINDOWLOG_MAX_64, 31);
assert_eq!(ZSTD_WINDOWLOG_MAX_32, 30);
assert_eq!(ZSTD_CONTENTSIZE_UNKNOWN, u64::MAX);
assert_eq!(ZSTD_CONTENTSIZE_ERROR, u64::MAX - 1);
assert_eq!(ZSTD_CONTENTSIZE_UNKNOWN - ZSTD_CONTENTSIZE_ERROR, 1);
}
#[test]
fn frame_format_magic_and_size_constants_match_spec() {
assert_eq!(ZSTD_MAGICNUMBER, 0xFD2FB528);
assert_eq!(ZSTD_MAGIC_DICTIONARY, 0xEC30A437);
assert_eq!(ZSTD_MAGIC_SKIPPABLE_START, 0x184D2A50);
assert_eq!(ZSTD_MAGIC_SKIPPABLE_MASK, 0xFFFFFFF0);
assert_eq!(ZSTD_FRAMEIDSIZE, 4);
assert_eq!(ZSTD_SKIPPABLEHEADERSIZE, 8);
for v in 0u32..=15 {
let magic = ZSTD_MAGIC_SKIPPABLE_START | v;
assert_eq!(
magic & ZSTD_MAGIC_SKIPPABLE_MASK,
ZSTD_MAGIC_SKIPPABLE_START
);
}
}
#[test]
fn zstd_isFrame_detects_regular_and_skippable() {
let regular = ZSTD_MAGICNUMBER.to_le_bytes();
assert_eq!(ZSTD_isFrame(®ular), 1);
let skippable = ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes();
assert_eq!(ZSTD_isFrame(&skippable), 1);
let skippable15 = (ZSTD_MAGIC_SKIPPABLE_START | 0x0F).to_le_bytes();
assert_eq!(ZSTD_isFrame(&skippable15), 1);
assert_eq!(ZSTD_isFrame(&[0xDE, 0xAD, 0xBE, 0xEF]), 0);
assert_eq!(ZSTD_isFrame(&[0xFD, 0x2F]), 0);
assert_eq!(ZSTD_isFrame(&[]), 0);
}
#[test]
fn zstd_isSkippableFrame_rejects_regular_frame() {
let regular = ZSTD_MAGICNUMBER.to_le_bytes();
assert_eq!(ZSTD_isSkippableFrame(®ular), 0);
let skippable = (ZSTD_MAGIC_SKIPPABLE_START | 0x05).to_le_bytes();
assert_eq!(ZSTD_isSkippableFrame(&skippable), 1);
}
#[test]
fn zstd_isFrame_rejects_dictionary_magic() {
let dict_magic = ZSTD_MAGIC_DICTIONARY.to_le_bytes();
assert_eq!(ZSTD_isFrame(&dict_magic), 0);
let mut padded = dict_magic.to_vec();
padded.extend_from_slice(&[0u8; 4]);
assert_eq!(ZSTD_isFrame(&padded), 0);
}
#[test]
fn zstd_isSkippableFrame_rejects_dictionary_magic() {
let dict_magic = ZSTD_MAGIC_DICTIONARY.to_le_bytes();
assert_eq!(ZSTD_isSkippableFrame(&dict_magic), 0);
}
#[test]
fn zstd_isSkippableFrame_accepts_all_16_variants() {
for v in 0u32..=15 {
let magic = (ZSTD_MAGIC_SKIPPABLE_START + v).to_le_bytes();
assert_eq!(
ZSTD_isSkippableFrame(&magic),
1,
"variant {v} didn't register as skippable"
);
}
let invalid = (ZSTD_MAGIC_SKIPPABLE_START + 16).to_le_bytes();
assert_eq!(ZSTD_isSkippableFrame(&invalid), 0);
}
#[test]
fn zstd_isSkippableFrame_rejects_short_src_safely() {
assert_eq!(ZSTD_isSkippableFrame(&[]), 0);
assert_eq!(ZSTD_isSkippableFrame(&[0x50]), 0);
assert_eq!(ZSTD_isSkippableFrame(&[0x50, 0x2A, 0x4D]), 0);
}
#[test]
fn zstd_getDictID_fromFrame_returns_zero_when_absent() {
let src: Vec<u8> = b"hello world".to_vec();
let mut dst = vec![0u8; 128];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut dst, &src, 1);
dst.truncate(n);
assert_eq!(ZSTD_getDictID_fromFrame(&dst), 0);
}
#[test]
fn copyRawBlock_and_setRleBlock_cover_happy_and_dst_too_small_paths() {
let src = b"hello-raw";
let mut dst = [0u8; 16];
let n = ZSTD_copyRawBlock(&mut dst, src);
assert_eq!(n, src.len());
assert_eq!(&dst[..n], src);
let mut tiny = [0u8; 4];
assert!(crate::common::error::ERR_isError(ZSTD_copyRawBlock(
&mut tiny, src
)));
let mut buf = vec![0u8; 32];
let rle_n = ZSTD_setRleBlock(&mut buf, 0xAB, 10);
assert_eq!(rle_n, 10);
assert!(buf[..10].iter().all(|&b| b == 0xAB));
assert!(buf[10..].iter().all(|&b| b == 0));
let mut short = [0u8; 4];
assert!(crate::common::error::ERR_isError(ZSTD_setRleBlock(
&mut short, 0xCD, 10
)));
}
#[test]
fn findFrameCompressedSize_matches_compressor_output_for_multi_block() {
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src: Vec<u8> = b"multi-block frame-size probe. "
.iter()
.cycle()
.take(200_000)
.copied()
.collect();
let bound = ZSTD_compressBound(src.len());
let mut dst = vec![0u8; bound];
let n = ZSTD_compress(&mut dst, &src, 1);
assert!(!crate::common::error::ERR_isError(n));
let mut with_trailer = dst[..n].to_vec();
with_trailer.extend_from_slice(&[0xFFu8; 64]);
let reported = ZSTD_findFrameCompressedSize(&with_trailer);
assert!(!crate::common::error::ERR_isError(reported));
assert_eq!(reported, n);
}
#[test]
fn findFrameSizeInfo_reports_skippable_frame_with_zero_decompressed_bound() {
let user_data_len: u32 = 10;
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&user_data_len.to_le_bytes());
src.extend(core::iter::repeat_n(0u8, user_data_len as usize));
let info = ZSTD_findFrameSizeInfo(&src, ZSTD_format_e::ZSTD_f_zstd1);
assert_eq!(info.nbBlocks, 0);
assert_eq!(info.compressedSize, 8 + user_data_len as usize);
assert_eq!(info.decompressedBound, 0);
}
#[test]
fn decompressBound_all_skippable_frames_returns_zero() {
let mut src = Vec::new();
for &user_data_len in &[5u32, 12] {
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&user_data_len.to_le_bytes());
src.extend(core::iter::repeat_n(0u8, user_data_len as usize));
}
assert_eq!(ZSTD_decompressBound(&src), 0);
}
#[test]
fn findDecompressedSize_all_skippable_frames_returns_zero() {
let mut src = Vec::new();
for &user_data_len in &[5u32, 12] {
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&user_data_len.to_le_bytes());
src.extend(core::iter::repeat_n(0u8, user_data_len as usize));
}
assert_eq!(ZSTD_findDecompressedSize(&src), 0);
}
#[test]
fn findDecompressedSize_returns_UNKNOWN_when_any_frame_lacks_fcs() {
let raw = make_raw_hello_frame();
let mut no_fcs = Vec::new();
no_fcs.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
no_fcs.push(0x00); no_fcs.push(0x20); no_fcs.push(((3u32 << 3) | 1) as u8);
no_fcs.push(0);
no_fcs.push(0);
no_fcs.extend_from_slice(b"abc");
let mut stream = raw;
stream.extend_from_slice(&no_fcs);
assert_eq!(ZSTD_findDecompressedSize(&stream), ZSTD_CONTENTSIZE_UNKNOWN);
}
#[test]
fn getFrameContentSize_reports_compressed_payload_size_across_fcs_encodings() {
let sizes: &[u64] = &[
1, 300, 100_000, 0x1_0000_0000, ];
for &sz in sizes {
use crate::compress::zstd_compress::{
ZSTD_FrameParameters, ZSTD_writeFrameHeader, ZSTD_FRAMEHEADERSIZE_MAX,
};
use crate::decompress::zstd_decompress::ZSTD_WINDOWLOG_ABSOLUTEMIN;
let fp = ZSTD_FrameParameters {
contentSizeFlag: 1,
checksumFlag: 0,
noDictIDFlag: 1,
};
let mut hdr = vec![0u8; ZSTD_FRAMEHEADERSIZE_MAX];
let n = ZSTD_writeFrameHeader(&mut hdr, &fp, ZSTD_WINDOWLOG_ABSOLUTEMIN + 10, sz, 0);
assert!(!crate::common::error::ERR_isError(n));
hdr.truncate(n);
assert_eq!(
ZSTD_getFrameContentSize(&hdr),
sz,
"FCS roundtrip failed for size={sz}",
);
}
}
#[test]
fn findDecompressedSize_sums_multiple_concatenated_frames() {
let parts: &[&[u8]] = &[
b"alpha".as_ref(),
b"longer-part-here".as_ref(),
b"c".as_ref(),
];
let expected_total: u64 = parts.iter().map(|p| p.len() as u64).sum();
let mut stream = Vec::new();
for part in parts {
let mut buf = vec![0u8; 256];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut buf, part, 3);
assert!(!crate::common::error::ERR_isError(n));
stream.extend_from_slice(&buf[..n]);
}
assert_eq!(ZSTD_findDecompressedSize(&stream), expected_total);
let mut stream_with_skip = Vec::new();
{
let mut buf = vec![0u8; 256];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut buf, parts[0], 3);
stream_with_skip.extend_from_slice(&buf[..n]);
}
stream_with_skip.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
stream_with_skip.extend_from_slice(&8u32.to_le_bytes());
stream_with_skip.extend_from_slice(b"SKIPDATA");
{
let mut buf = vec![0u8; 256];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut buf, parts[1], 3);
stream_with_skip.extend_from_slice(&buf[..n]);
}
assert_eq!(
ZSTD_findDecompressedSize(&stream_with_skip),
(parts[0].len() + parts[1].len()) as u64,
);
}
#[test]
fn findDecompressedSize_returns_ERROR_on_trailing_garbage_after_valid_frame() {
let mut src = make_raw_hello_frame();
src.extend_from_slice(&[0x28, 0xB5, 0x2F]);
let rc = ZSTD_findDecompressedSize(&src);
assert_eq!(rc, ZSTD_CONTENTSIZE_ERROR);
}
#[test]
fn zstd_getDictID_fromFrame_reads_dictID_when_present() {
use crate::compress::zstd_compress::{
ZSTD_FrameParameters, ZSTD_writeFrameHeader, ZSTD_FRAMEHEADERSIZE_MAX,
};
let dictID_in = 0xDEAD_BEEFu32;
let fParams = ZSTD_FrameParameters {
contentSizeFlag: 0,
checksumFlag: 0,
noDictIDFlag: 0,
};
let mut buf = [0u8; ZSTD_FRAMEHEADERSIZE_MAX];
let n = ZSTD_writeFrameHeader(&mut buf, &fParams, 17, 0, dictID_in);
assert!(!crate::common::error::ERR_isError(n));
let got = ZSTD_getDictID_fromFrame(&buf[..n]);
assert_eq!(got, dictID_in);
}
#[test]
fn frame_header_truncated_requests_more() {
let mut zfh = ZSTD_FrameHeader::default();
let src = ZSTD_MAGICNUMBER.to_le_bytes();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 5);
}
#[test]
fn createDCtx_advanced_and_createDStream_advanced_return_Some() {
use crate::compress::zstd_compress::ZSTD_customMem;
let _dctx = ZSTD_createDCtx_advanced(ZSTD_customMem::default()).unwrap();
let _dstream = ZSTD_createDStream_advanced(ZSTD_customMem::default()).unwrap();
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src: Vec<u8> = b"DCtx_advanced functional probe ".repeat(20);
let bound = ZSTD_compressBound(src.len());
let mut compressed = vec![0u8; bound];
let n = ZSTD_compress(&mut compressed, &src, 1);
let mut out = vec![0u8; src.len() + 64];
let d = ZSTD_decompress(&mut out, &compressed[..n]);
assert_eq!(&out[..d], &src[..]);
}
#[test]
fn decompresses_upstream_level10_large_real_text_frame() {
use crate::decompress::zstd_decompress_block::{
active_ll_table, active_ml_table, active_of_table, blockProperties_t, blockType_e,
streaming_operation, ZSTD_DCtx, ZSTD_blockHeaderSize, ZSTD_buildDefaultSeqTables,
ZSTD_decodeLiteralsBlock, ZSTD_decodeSeqHeaders, ZSTD_decoder_entropy_rep,
ZSTD_decompressSequences_body, ZSTD_getcBlockSize,
};
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, Stdio};
let which = Command::new("which")
.arg("zstd")
.output()
.expect("which zstd");
if !which.status.success() {
eprintln!("upstream zstd not on $PATH; skipping");
return;
}
let zstd = PathBuf::from(String::from_utf8(which.stdout).unwrap().trim());
let seed = fs::read("tests/fixtures/zstd_h.txt").expect("read zstd_h.txt");
let src: Vec<u8> = seed.repeat(20);
let mut child = Command::new(&zstd)
.args(["-q", "--no-check", "-10", "-c", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("spawn upstream zstd");
child.stdin.as_mut().unwrap().write_all(&src).unwrap();
let out = child.wait_with_output().expect("wait upstream zstd");
assert!(
out.status.success(),
"upstream compression failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut decoded = vec![0u8; src.len()];
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut zfh = ZSTD_FrameHeader::default();
let hdr = ZSTD_getFrameHeader_advanced(&mut zfh, &out.stdout, dctx.format);
assert_eq!(hdr, 0);
let mut ip = zfh.headerSize as usize;
let mut op = 0usize;
let mut block_idx = 0usize;
loop {
let mut bp = blockProperties_t {
blockType: blockType_e::bt_raw,
lastBlock: 0,
origSize: 0,
};
let cblock = ZSTD_getcBlockSize(&out.stdout[ip..], &mut bp);
assert!(
!crate::common::error::ERR_isError(cblock),
"block {block_idx}: cblock parse failed: {}",
crate::common::error::ERR_getErrorName(cblock)
);
ip += ZSTD_blockHeaderSize;
match bp.blockType {
blockType_e::bt_compressed => {
let block = &out.stdout[ip..ip + cblock];
let lit_rc = ZSTD_decodeLiteralsBlock(
&mut dctx,
block,
&mut decoded[op..],
streaming_operation::not_streaming,
);
assert!(
!crate::common::error::ERR_isError(lit_rc),
"block {block_idx}: literals failed: {}",
crate::common::error::ERR_getErrorName(lit_rc)
);
let mut nb_seq = 0i32;
let seq_header =
ZSTD_decodeSeqHeaders(&mut dctx, &mut nb_seq, &block[lit_rc..]);
assert!(
!crate::common::error::ERR_isError(seq_header),
"block {block_idx}: seq headers failed: {}",
crate::common::error::ERR_getErrorName(seq_header)
);
let lit_snapshot = dctx.litExtraBuffer[..dctx.litSize].to_vec();
let ll = active_ll_table(&dctx).to_vec();
let of = active_of_table(&dctx).to_vec();
let ml = active_ml_table(&dctx).to_vec();
let seq_rc = ZSTD_decompressSequences_body(
&mut decoded,
op,
&[],
&block[lit_rc + seq_header..],
nb_seq,
&lit_snapshot,
dctx.litSize,
&ll,
&of,
&ml,
&mut rep,
);
assert!(
!crate::common::error::ERR_isError(seq_rc),
"block {block_idx}: sequence body failed: {}",
crate::common::error::ERR_getErrorName(seq_rc)
);
op += seq_rc;
}
blockType_e::bt_raw => {
let rc = ZSTD_copyRawBlock(&mut decoded[op..], &out.stdout[ip..ip + cblock]);
assert!(
!crate::common::error::ERR_isError(rc),
"block {block_idx}: raw block failed: {}",
crate::common::error::ERR_getErrorName(rc)
);
op += rc;
}
blockType_e::bt_rle => {
let rc =
ZSTD_setRleBlock(&mut decoded[op..], out.stdout[ip], bp.origSize as usize);
assert!(
!crate::common::error::ERR_isError(rc),
"block {block_idx}: rle block failed: {}",
crate::common::error::ERR_getErrorName(rc)
);
op += rc;
}
blockType_e::bt_reserved => panic!("block {block_idx}: reserved block"),
}
ip += cblock;
block_idx += 1;
if bp.lastBlock != 0 {
break;
}
}
let d = ZSTD_decompress(&mut decoded, &out.stdout);
assert!(
!crate::common::error::ERR_isError(d),
"rust decompressor rejected upstream frame: {}",
crate::common::error::ERR_getErrorName(d)
);
assert_eq!(&decoded[..op], &src[..]);
assert_eq!(&decoded[..d], &src[..]);
}
#[test]
fn createDCtx_advanced_rejects_invalid_custommem_pairs() {
use crate::compress::zstd_compress::ZSTD_customMem;
fn dummy_alloc(_opaque: usize, _size: usize) -> *mut core::ffi::c_void {
core::ptr::null_mut()
}
let invalid = ZSTD_customMem {
customAlloc: Some(dummy_alloc),
customFree: None,
opaque: 1,
};
assert!(ZSTD_createDCtx_advanced(invalid).is_none());
assert!(ZSTD_createDStream_advanced(invalid).is_none());
}
#[test]
fn advanced_dctx_surfaces_invoke_custom_allocator_callbacks() {
use crate::compress::zstd_compress::ZSTD_customMem;
use core::sync::atomic::{AtomicUsize, Ordering};
static ALLOCS: AtomicUsize = AtomicUsize::new(0);
static FREES: AtomicUsize = AtomicUsize::new(0);
fn counting_alloc(_opaque: usize, size: usize) -> *mut core::ffi::c_void {
use std::alloc::{alloc, Layout};
const ALIGN: usize = 64;
const HEADER_WORDS: usize = 2;
let total = size.max(1) + ALIGN + HEADER_WORDS * core::mem::size_of::<usize>();
let layout = Layout::from_size_align(total, ALIGN).unwrap();
unsafe {
let base = alloc(layout);
if base.is_null() {
return core::ptr::null_mut();
}
let payload_addr =
(base as usize + HEADER_WORDS * core::mem::size_of::<usize>() + ALIGN - 1)
& !(ALIGN - 1);
let header = (payload_addr as *mut usize).sub(HEADER_WORDS);
header.write(base as usize);
header.add(1).write(total);
ALLOCS.fetch_add(1, Ordering::SeqCst);
payload_addr as *mut core::ffi::c_void
}
}
fn counting_free(_opaque: usize, address: *mut core::ffi::c_void) {
use std::alloc::{dealloc, Layout};
const ALIGN: usize = 64;
const HEADER_WORDS: usize = 2;
if address.is_null() {
return;
}
unsafe {
let header = (address as *mut usize).sub(HEADER_WORDS);
let base = header.read() as *mut u8;
let total = header.add(1).read();
let layout = Layout::from_size_align(total, ALIGN).unwrap();
dealloc(base, layout);
FREES.fetch_add(1, Ordering::SeqCst);
}
}
let custom = ZSTD_customMem {
customAlloc: Some(counting_alloc),
customFree: Some(counting_free),
opaque: 11,
};
let dctx = ZSTD_createDCtx_advanced(custom).unwrap();
assert_eq!(dctx.customMem, custom);
let dstream = ZSTD_createDStream_advanced(custom).unwrap();
assert_eq!(dstream.customMem, custom);
assert_eq!(ALLOCS.load(Ordering::SeqCst), 2);
assert_eq!(FREES.load(Ordering::SeqCst), 0);
assert_eq!(ZSTD_freeDCtx(dctx), 0);
assert_eq!(ZSTD_freeDStream(Some(dstream)), 0);
assert_eq!(FREES.load(Ordering::SeqCst), 2);
}
#[test]
fn ZSTD_DStream_is_alias_for_ZSTD_DCtx() {
assert_eq!(
core::mem::size_of::<ZSTD_DStream>(),
core::mem::size_of::<ZSTD_DCtx>()
);
let ds: Box<ZSTD_DStream> = ZSTD_createDStream().unwrap();
assert_eq!(ZSTD_sizeof_DCtx(&ds), ZSTD_sizeof_DStream(&ds));
}
#[test]
fn ZSTD_nextInputType_e_discriminants_match_upstream() {
assert_eq!(ZSTD_nextInputType_e::ZSTDnit_frameHeader as u32, 0);
assert_eq!(ZSTD_nextInputType_e::ZSTDnit_blockHeader as u32, 1);
assert_eq!(ZSTD_nextInputType_e::ZSTDnit_block as u32, 2);
assert_eq!(ZSTD_nextInputType_e::ZSTDnit_lastBlock as u32, 3);
assert_eq!(ZSTD_nextInputType_e::ZSTDnit_checksum as u32, 4);
assert_eq!(ZSTD_nextInputType_e::ZSTDnit_skippableFrame as u32, 5);
}
#[test]
fn decompressStream_simpleArgs_forwards_to_decompressStream() {
use crate::common::error::ERR_isError;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src = b"decompressStream_simpleArgs smoke test ".repeat(3);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
assert!(!ERR_isError(n));
framed.truncate(n);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream_simpleArgs(
&mut dctx,
&mut out,
&mut out_pos,
&framed,
&mut in_pos,
);
for _ in 0..8 {
if out_pos >= src.len() {
break;
}
let _ = ZSTD_decompressStream_simpleArgs(
&mut dctx,
&mut out,
&mut out_pos,
&[],
&mut 0usize,
);
}
assert_eq!(&out[..out_pos], &src[..]);
}
#[test]
fn decompressStream_rejects_invalid_cursors_without_panicking() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = [0u8; 4];
let mut out_pos = out.len() + 1;
let mut in_pos = 0usize;
let rc = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, b"abc", &mut in_pos);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::DstSizeTooSmall);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out_pos = 0usize;
let mut in_pos = 4usize;
let rc = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, b"abc", &mut in_pos);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn decompressStream_simpleArgs_rejects_invalid_cursors_without_panicking() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = [0u8; 1];
let mut out_pos = 0usize;
let mut in_pos = 2usize;
let rc =
ZSTD_decompressStream_simpleArgs(&mut dctx, &mut out, &mut out_pos, b"x", &mut in_pos);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn decompressStream_with_pending_output_and_full_dst_preserves_input_pos() {
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
dctx.stream_out_buffer = b"pending-output".to_vec();
dctx.stream_out_drained = 0;
let mut out = [];
let mut out_pos = 0usize;
let mut in_pos = 3usize;
let rc = ZSTD_decompressStream(
&mut dctx,
&mut out,
&mut out_pos,
b"abc-new-input",
&mut in_pos,
);
assert_eq!(rc, dctx.stream_out_buffer.len());
assert_eq!(out_pos, 0);
assert_eq!(in_pos, 3);
assert!(dctx.stream_in_buffer.is_empty());
}
#[test]
fn decompressStream_unknown_fcs_uses_caller_capacity_not_frame_bound() {
use crate::common::error::ERR_isError;
let payload = b"unknown-fcs";
let bh = ((payload.len() as u32) << 3) | 1; let frame = small_window_frame_with_block(bh, payload);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut tiny = [0u8; 4];
let mut out_pos = 0usize;
let mut in_pos = 0usize;
let rc = ZSTD_decompressStream(&mut dctx, &mut tiny, &mut out_pos, &frame, &mut in_pos);
assert!(
!ERR_isError(rc),
"err: {}",
crate::common::error::ERR_getErrorName(rc)
);
assert_eq!(in_pos, frame.len());
assert_eq!(out_pos, tiny.len());
assert_eq!(&tiny, &payload[..tiny.len()]);
let mut out = [0u8; 32];
let mut out_pos = 0usize;
let mut empty_pos = 0usize;
let rc = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &[], &mut empty_pos);
assert!(
!ERR_isError(rc),
"err: {}",
crate::common::error::ERR_getErrorName(rc)
);
assert_eq!(&out[..out_pos], &payload[tiny.len()..]);
}
#[test]
fn DCtx_reset_parameters_only_rejects_mid_stream_but_combined_variant_always_accepts() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src = b"dctx-reset-stage-semantics ".repeat(3);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
assert!(!ERR_isError(n));
framed.truncate(n);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
let rc = ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_parameters);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::StageWrong);
assert_eq!(
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_session_only),
0,
);
assert_eq!(
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_parameters),
0,
);
ZSTD_initDStream(&mut dctx);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
assert_eq!(
ZSTD_DCtx_reset(
&mut dctx,
ZSTD_DResetDirective::ZSTD_reset_session_and_parameters,
),
0,
);
}
#[test]
fn DCtx_session_reset_restores_legacy_decode_state() {
use crate::decompress::zstd_decompress_block::blockType_e;
let mut dctx = ZSTD_DCtx::new();
assert_eq!(
ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless),
0
);
dctx.expected = 99;
dctx.stage = ZSTD_dStage::ZSTDds_decompressBlock;
dctx.processedCSize = 123;
dctx.decodedSize = 456;
dctx.bType = blockType_e::bt_raw;
dctx.rleSize = 7;
dctx.historyBuffer.extend_from_slice(b"history");
dctx.validateChecksum = 1;
assert_eq!(
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_session_only),
0
);
assert_eq!(dctx.expected, ZSTD_startingInputLength(dctx.format));
assert_eq!(dctx.expected, 1);
assert_eq!(dctx.stage, ZSTD_dStage::ZSTDds_getFrameHeaderSize);
assert_eq!(dctx.processedCSize, 0);
assert_eq!(dctx.decodedSize, 0);
assert_eq!(dctx.bType, blockType_e::bt_reserved);
assert_eq!(dctx.rleSize, 0);
assert!(dctx.historyBuffer.is_empty());
assert_eq!(dctx.validateChecksum, 0);
dctx.expected = 77;
dctx.stage = ZSTD_dStage::ZSTDds_checkChecksum;
assert_eq!(ZSTD_resetDStream(&mut dctx), 1);
assert_eq!(dctx.expected, 1);
assert_eq!(dctx.stage, ZSTD_dStage::ZSTDds_getFrameHeaderSize);
dctx.expected = 55;
dctx.stage = ZSTD_dStage::ZSTDds_skipFrame;
assert_eq!(ZSTD_initDStream(&mut dctx), 1);
assert_eq!(dctx.expected, 1);
assert_eq!(dctx.stage, ZSTD_dStage::ZSTDds_getFrameHeaderSize);
}
#[test]
fn DCtx_param_setters_reject_mid_stream_with_StageWrong() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src = b"dctx-param-stage-gate ".repeat(4);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
assert!(!ERR_isError(n));
framed.truncate(n);
{
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
assert_eq!(
ZSTD_DCtx_setParameter(
&mut dctx,
ZSTD_dParameter::ZSTD_d_format,
ZSTD_format_e::ZSTD_f_zstd1 as i32,
),
0,
);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
let rc = ZSTD_DCtx_setParameter(
&mut dctx,
ZSTD_dParameter::ZSTD_d_format,
ZSTD_format_e::ZSTD_f_zstd1_magicless as i32,
);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::StageWrong);
}
{
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
assert_eq!(ZSTD_DCtx_setMaxWindowSize(&mut dctx, 1 << 20), 0);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
let rc = ZSTD_DCtx_setMaxWindowSize(&mut dctx, 1 << 20);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::StageWrong);
}
}
#[test]
fn DCtx_dict_family_setters_reject_mid_stream_with_StageWrong() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let dict = b"dctx-dict-family-stage-gate ".repeat(3);
let src = b"some payload bytes ".repeat(4);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
assert!(!ERR_isError(n));
framed.truncate(n);
{
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
assert_eq!(ZSTD_DCtx_loadDictionary(&mut dctx, &dict), 0);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
let rc = ZSTD_DCtx_loadDictionary(&mut dctx, &dict);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::StageWrong);
}
{
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, &dict), 0);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
let rc = ZSTD_DCtx_refPrefix(&mut dctx, &dict);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::StageWrong);
}
}
#[test]
fn DCtx_getParameter_defaults_on_fresh_dctx_match_upstream() {
let dctx = ZSTD_DCtx::new();
let mut v = 0i32;
assert_eq!(
ZSTD_DCtx_getParameter(&dctx, ZSTD_dParameter::ZSTD_d_windowLogMax, &mut v),
0,
);
assert_eq!(v, ZSTD_WINDOWLOG_LIMIT_DEFAULT as i32);
assert_eq!(
ZSTD_DCtx_getParameter(&dctx, ZSTD_dParameter::ZSTD_d_format, &mut v),
0,
);
assert_eq!(v, ZSTD_format_e::ZSTD_f_zstd1 as i32);
}
#[test]
fn DCtx_reset_parameters_clears_every_dict_slot_via_clearDict_helper() {
let mut dctx = ZSTD_DCtx::new();
dctx.stream_dict = b"reset-wipe-test".to_vec();
dctx.dictID = 0xCA_FE_BA_BE;
dctx.ddict_rep = [7, 8, 9];
dctx.dictUses = ZSTD_dictUses_e::ZSTD_use_indefinitely;
assert_eq!(
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_parameters),
0,
);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.dictID, 0);
assert_eq!(dctx.ddict_rep, [1, 4, 8]);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
}
#[test]
fn DCtx_loadDictionary_persists_across_decodes() {
use crate::common::error::ERR_isError;
use crate::common::xxhash::XXH64_state_t;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
use crate::decompress::zstd_decompress_block::{
ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let src = b"loadDictionary-persists-across-decodes-payload ".repeat(2);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
framed.truncate(n);
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let dict = b"persistent-dict-bytes".to_vec();
assert_eq!(ZSTD_DCtx_loadDictionary(&mut dctx, &dict), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
let mut out = vec![0u8; src.len() + 64];
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let d1 = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &framed);
assert!(!ERR_isError(d1));
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
assert_eq!(dctx.stream_dict, dict);
let d2 = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &framed);
assert!(!ERR_isError(d2));
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
assert_eq!(dctx.stream_dict, dict);
}
#[test]
fn decompress_usingDDict_maps_trailing_garbage_to_srcSizeWrong() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let src = b"prepared-ddict-trailing-garbage ".repeat(3);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
assert!(!ERR_isError(n));
framed.truncate(n);
framed.extend_from_slice(b"garbage");
let ddict = ZSTD_createDDict(b"raw-ddict").expect("ddict");
let mut dctx = ZSTD_DCtx::new();
let mut out = vec![0u8; src.len() + 32];
let rc = ZSTD_decompress_usingDDict(&mut dctx, &mut out, &framed, &ddict);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn DCtx_refDDict_empty_content_clears_dict_state() {
use crate::decompress::zstd_ddict::ZSTD_DDict;
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_loadDictionary(&mut dctx, b"pre-existing"), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
let empty_ddict = ZSTD_DDict::empty();
assert_eq!(ZSTD_DCtx_refDDict(&mut dctx, &empty_ddict), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.dictID, 0);
}
#[test]
fn DCtx_loadDictionary_empty_slice_clears_dict_state() {
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_loadDictionary(&mut dctx, b"real-dict"), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
assert_eq!(dctx.stream_dict, b"real-dict");
assert_eq!(ZSTD_DCtx_loadDictionary(&mut dctx, &[]), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.dictID, 0);
}
#[test]
fn DCtx_refPrefix_empty_slice_clears_dict_state() {
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, b"pre-existing"), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
assert!(!dctx.stream_dict.is_empty());
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, &[]), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.dictID, 0);
}
#[test]
fn decompressStream_refPrefix_auto_clears_after_one_frame() {
use crate::common::error::ERR_isError;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src = b"streaming-refPrefix-auto-clear-payload ".repeat(2);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
framed.truncate(n);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let prefix = b"streaming-one-shot-prefix".to_vec();
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, &prefix), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
for _ in 0..8 {
if out_pos >= src.len() {
break;
}
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &[], &mut 0usize);
}
let decoded_len = out_pos;
assert!(!ERR_isError(decoded_len));
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(
dctx.stream_dict.is_empty(),
"streaming refPrefix dict persisted after one-shot decode",
);
}
#[test]
fn decompressStream_prepared_dict_state_can_have_empty_raw_content() {
use crate::common::error::ERR_isError;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src = b"streaming-empty-content-prepared-dict-state ".repeat(2);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
assert!(!ERR_isError(n));
framed.truncate(n);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
dctx.stream_dict.clear();
dctx.dictID = 0xABCD_1234;
dctx.litEntropy = 1;
dctx.fseEntropy = 1;
dctx.ddict_rep = [1, 4, 8];
dctx.dictUses = ZSTD_dictUses_e::ZSTD_use_once;
let mut out = vec![0u8; src.len() + 32];
let mut out_pos = 0usize;
let mut in_pos = 0usize;
let hint = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &framed, &mut in_pos);
assert!(!ERR_isError(hint));
for _ in 0..8 {
if out_pos >= src.len() {
break;
}
let h = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &[], &mut 0usize);
assert!(!ERR_isError(h));
if h == 0 {
break;
}
}
assert_eq!(&out[..out_pos], src.as_slice());
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(dctx.stream_dict.is_empty());
}
#[test]
fn DCtx_refPrefix_auto_clears_after_one_decode() {
use crate::common::error::ERR_isError;
use crate::common::xxhash::XXH64_state_t;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
use crate::decompress::zstd_decompress_block::{
ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let src = b"refPrefix-auto-clear-payload ".repeat(3);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut framed, &src, 3);
framed.truncate(n);
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let prefix = b"one-shot-prefix-bytes".to_vec();
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, &prefix), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
assert_eq!(dctx.stream_dict, prefix);
let mut out = vec![0u8; src.len() + 64];
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let d = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &framed);
assert!(!ERR_isError(d));
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(
dctx.stream_dict.is_empty(),
"refPrefix dict persisted after one-shot decode",
);
}
#[test]
fn DCtx_dict_family_setters_populate_dictUses_lifecycle_tracker() {
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let dict_bytes = b"dictUses-lifecycle-tracker".to_vec();
let mut dctx = ZSTD_DCtx::new();
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert_eq!(ZSTD_DCtx_loadDictionary(&mut dctx, &dict_bytes), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
ZSTD_clearDict(&mut dctx);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, &dict_bytes), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
let mut dctx = ZSTD_DCtx::new();
let ddict = ZSTD_createDDict(&dict_bytes).expect("ddict");
assert_eq!(ZSTD_DCtx_refDDict(&mut dctx, &ddict), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_parameters);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
}
#[test]
fn initDStream_usingDDict_parses_magic_prefix_dict_entropy() {
use super::tests::build_minimal_zstd_dict;
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let content = b"initDStream_usingDDict-magic-dict-content ".repeat(2);
let dict = build_minimal_zstd_dict(0x12_34_56_78, &content);
let ddict = ZSTD_createDDict(&dict).expect("ddict create");
let mut dctx = ZSTD_DCtx::new();
let rc = ZSTD_initDStream_usingDDict(&mut dctx, &ddict);
assert!(!crate::common::error::ERR_isError(rc));
assert_eq!(dctx.dictID, 0x12_34_56_78);
assert_eq!(dctx.litEntropy, 1);
assert_eq!(dctx.fseEntropy, 1);
}
#[test]
fn initDStream_usingDict_parses_magic_prefix_dict_entropy() {
use super::tests::build_minimal_zstd_dict;
let content = b"initDStream_usingDict-magic-dict-content ".repeat(2);
let dict = build_minimal_zstd_dict(0xAB_CD_EF_00, &content);
let mut dctx = ZSTD_DCtx::new();
let rc = ZSTD_initDStream_usingDict(&mut dctx, &dict);
assert!(!crate::common::error::ERR_isError(rc));
assert_eq!(dctx.dictID, 0xAB_CD_EF_00);
assert_eq!(dctx.litEntropy, 1);
assert_eq!(dctx.fseEntropy, 1);
}
#[test]
fn decompressStream_preserves_loaded_full_dict_entropy_state() {
use crate::common::error::ERR_isError;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let content = b"stream-full-dict-entropy-content ".repeat(3);
let dict = build_minimal_zstd_dict(0x24_68_AC_E0, &content);
let src = b"ordinary stream frame while a full dict is attached ".repeat(3);
let mut frame = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut frame, &src, 3);
assert!(!ERR_isError(n));
let mut dctx = ZSTD_DCtx::new();
let rc = ZSTD_initDStream_usingDict(&mut dctx, &dict);
assert!(!ERR_isError(rc));
assert_eq!(dctx.dictID, 0x24_68_AC_E0);
assert_eq!(dctx.litEntropy, 1);
assert_eq!(dctx.fseEntropy, 1);
assert_eq!(dctx.ddict_rep, [8, 7, 1]);
let mut out = vec![0u8; src.len() + 32];
let mut out_pos = 0usize;
let mut in_pos = 0usize;
let hint =
ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &frame[..n], &mut in_pos);
assert!(!ERR_isError(hint));
while hint != 0 && out_pos < src.len() {
let h = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &[], &mut 0usize);
assert!(!ERR_isError(h));
if h == 0 {
break;
}
}
assert_eq!(&out[..out_pos], src.as_slice());
assert_eq!(dctx.dictID, 0x24_68_AC_E0);
assert_eq!(dctx.litEntropy, 1);
assert_eq!(dctx.fseEntropy, 1);
assert_eq!(dctx.ddict_rep, [8, 7, 1]);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
}
#[test]
fn DCtx_setMaxWindowSize_preserves_non_power_of_two_byte_cap() {
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_setMaxWindowSize(&mut dctx, 1 << 20), 0);
assert_eq!(dctx.d_windowLogMax, 20);
assert_eq!(dctx.d_maxWindowSize, 1 << 20);
assert!(dctx.d_maxWindowSizeSet);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_setMaxWindowSize(&mut dctx, 1152), 0);
assert_eq!(dctx.d_windowLogMax, 10);
assert_eq!(dctx.d_maxWindowSize, 1152);
assert!(dctx.d_maxWindowSizeSet);
let src = frame_with_1152_byte_window_and_empty_block();
let rc = ZSTD_decodeFrameHeader(&mut dctx, &src, src.len());
assert_eq!(rc, 0);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_setMaxWindowSize(&mut dctx, 1151), 0);
let rc = ZSTD_decodeFrameHeader(&mut dctx, &src, src.len());
assert_eq!(rc, 0);
}
#[test]
fn internal_decoder_stage_enums_match_upstream() {
assert_eq!(ZSTD_dStreamStage::zdss_init as i32, 0);
assert_eq!(ZSTD_dStreamStage::zdss_loadHeader as i32, 1);
assert_eq!(ZSTD_dStreamStage::zdss_read as i32, 2);
assert_eq!(ZSTD_dStreamStage::zdss_load as i32, 3);
assert_eq!(ZSTD_dStreamStage::zdss_flush as i32, 4);
assert_eq!(ZSTD_dStage::ZSTDds_getFrameHeaderSize as i32, 0);
assert_eq!(ZSTD_dStage::ZSTDds_decodeFrameHeader as i32, 1);
assert_eq!(ZSTD_dStage::ZSTDds_decodeBlockHeader as i32, 2);
assert_eq!(ZSTD_dStage::ZSTDds_decompressBlock as i32, 3);
assert_eq!(ZSTD_dStage::ZSTDds_decompressLastBlock as i32, 4);
assert_eq!(ZSTD_dStage::ZSTDds_checkChecksum as i32, 5);
assert_eq!(ZSTD_dStage::ZSTDds_decodeSkippableHeader as i32, 6);
assert_eq!(ZSTD_dStage::ZSTDds_skipFrame as i32, 7);
assert_eq!(ZSTD_dictUses_e::ZSTD_use_indefinitely as i32, -1);
assert_eq!(ZSTD_dictUses_e::ZSTD_dont_use as i32, 0);
assert_eq!(ZSTD_dictUses_e::ZSTD_use_once as i32, 1);
}
#[test]
fn nextSrcSizeWithInputSize_and_isSkipFrame_match_stage_rules() {
use crate::decompress::zstd_decompress_block::blockType_e;
let mut dctx = ZSTD_DCtx::new();
dctx.expected = 64;
dctx.stage = ZSTD_dStage::ZSTDds_decodeBlockHeader;
assert_eq!(ZSTD_nextSrcSizeToDecompressWithInputSize(&dctx, 7), 64);
assert_eq!(ZSTD_isSkipFrame(&dctx), 0);
dctx.stage = ZSTD_dStage::ZSTDds_decompressBlock;
dctx.bType = blockType_e::bt_compressed;
assert_eq!(ZSTD_nextSrcSizeToDecompressWithInputSize(&dctx, 7), 64);
dctx.bType = blockType_e::bt_raw;
assert_eq!(ZSTD_nextSrcSizeToDecompressWithInputSize(&dctx, 7), 7);
assert_eq!(ZSTD_nextSrcSizeToDecompressWithInputSize(&dctx, 128), 64);
dctx.stage = ZSTD_dStage::ZSTDds_skipFrame;
assert_eq!(ZSTD_isSkipFrame(&dctx), 1);
}
#[test]
fn format_e_and_frameType_e_discriminants_match_upstream() {
assert_eq!(ZSTD_format_e::ZSTD_f_zstd1 as i32, 0);
assert_eq!(ZSTD_format_e::ZSTD_f_zstd1_magicless as i32, 1);
assert_eq!(ZSTD_FrameType_e::ZSTD_frame as i32, 0);
assert_eq!(ZSTD_FrameType_e::ZSTD_skippableFrame as i32, 1);
}
#[test]
fn DResetDirective_discriminants_match_upstream() {
assert_eq!(ZSTD_DResetDirective::ZSTD_reset_session_only as i32, 1);
assert_eq!(ZSTD_DResetDirective::ZSTD_reset_parameters as i32, 2);
assert_eq!(
ZSTD_DResetDirective::ZSTD_reset_session_and_parameters as i32,
3,
);
}
#[test]
fn dParameter_discriminants_match_upstream_zstd_h() {
assert_eq!(ZSTD_dParameter::ZSTD_d_windowLogMax as i32, 100);
assert_eq!(ZSTD_dParameter::ZSTD_d_format as i32, 1000);
assert_eq!(ZSTD_dParameter::ZSTD_d_forceIgnoreChecksum as i32, 1002);
}
#[test]
fn DCtx_setParameter_d_format_round_trips_through_getParameter() {
use crate::common::error::{ERR_getErrorCode, ERR_isError, ErrorCode};
let mut dctx = ZSTD_DCtx::new();
let mut value = 0i32;
assert_eq!(
ZSTD_DCtx_getParameter(&dctx, ZSTD_dParameter::ZSTD_d_format, &mut value),
0,
);
assert_eq!(value, ZSTD_format_e::ZSTD_f_zstd1 as i32);
assert_eq!(
ZSTD_DCtx_setParameter(
&mut dctx,
ZSTD_dParameter::ZSTD_d_format,
ZSTD_format_e::ZSTD_f_zstd1_magicless as i32,
),
0,
);
assert_eq!(dctx.format, ZSTD_format_e::ZSTD_f_zstd1_magicless);
assert_eq!(
ZSTD_DCtx_getParameter(&dctx, ZSTD_dParameter::ZSTD_d_format, &mut value),
0,
);
assert_eq!(value, ZSTD_format_e::ZSTD_f_zstd1_magicless as i32);
let rc = ZSTD_DCtx_setParameter(&mut dctx, ZSTD_dParameter::ZSTD_d_format, 42);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::ParameterOutOfBound);
}
#[test]
fn DCtx_reset_parameters_clears_magicless_format() {
let mut dctx = ZSTD_DCtx::new();
ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless);
assert_eq!(dctx.format, ZSTD_format_e::ZSTD_f_zstd1_magicless);
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_session_only);
assert_eq!(dctx.format, ZSTD_format_e::ZSTD_f_zstd1_magicless);
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_parameters);
assert_eq!(dctx.format, ZSTD_format_e::ZSTD_f_zstd1);
ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless);
ZSTD_DCtx_reset(
&mut dctx,
ZSTD_DResetDirective::ZSTD_reset_session_and_parameters,
);
assert_eq!(dctx.format, ZSTD_format_e::ZSTD_f_zstd1);
}
#[test]
fn DCtx_setFormat_stashes_format_on_dctx() {
use crate::common::error::ERR_isError;
let mut dctx = ZSTD_DCtx::default();
assert_eq!(dctx.format, ZSTD_format_e::ZSTD_f_zstd1);
let rc = ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless);
assert!(!ERR_isError(rc));
assert_eq!(dctx.format, ZSTD_format_e::ZSTD_f_zstd1_magicless);
}
#[test]
fn initDStream_hint_honors_magicless_format() {
let mut dctx = ZSTD_DCtx::default();
assert_eq!(ZSTD_initDStream(&mut dctx), 5);
assert_eq!(ZSTD_resetDStream(&mut dctx), 5);
dctx.format = ZSTD_format_e::ZSTD_f_zstd1_magicless;
assert_eq!(ZSTD_initDStream(&mut dctx), 1);
assert_eq!(ZSTD_resetDStream(&mut dctx), 1);
}
#[test]
fn decompress_usingDict_honors_magicless_format_on_dctx() {
use crate::common::error::ERR_isError;
use crate::compress::zstd_compress::{
ZSTD_CCtx_setFormat, ZSTD_createCCtx, ZSTD_endStream, ZSTD_initCStream_usingDict,
};
let dict = b"usingDict-magicless-parity-dict-bytes ".repeat(4);
let src = b"payload-referencing-usingDict-magicless-parity-dict-bytes ".repeat(6);
let mut cctx = ZSTD_createCCtx().unwrap();
assert_eq!(
ZSTD_CCtx_setFormat(&mut cctx, ZSTD_format_e::ZSTD_f_zstd1_magicless),
ZSTD_format_e::ZSTD_f_zstd1_magicless as usize,
);
ZSTD_initCStream_usingDict(&mut cctx, &dict, 3);
let mut compressed = vec![0u8; 4096];
let mut cp = 0usize;
let mut sp = 0usize;
let _ = crate::compress::zstd_compress::ZSTD_compressStream(
&mut cctx,
&mut compressed,
&mut cp,
&src,
&mut sp,
);
loop {
let r = ZSTD_endStream(&mut cctx, &mut compressed, &mut cp);
assert!(!ERR_isError(r));
if r == 0 {
break;
}
}
compressed.truncate(cp);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(
ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless),
0,
);
let mut out = vec![0u8; src.len() + 128];
let d = ZSTD_decompress_usingDict(&mut dctx, &mut out, &compressed, &dict);
assert!(!ERR_isError(d), "decode err: {d:#x}");
assert_eq!(&out[..d], &src[..]);
}
#[test]
fn decompressStream_honors_magicless_format() {
use crate::common::error::ERR_isError;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src = b"streaming-magicless-decode-parity-payload ".repeat(10);
let mut framed = vec![0u8; ZSTD_compressBound(src.len())];
let c_sz = ZSTD_compress(&mut framed, &src, 3);
assert!(!ERR_isError(c_sz));
framed.truncate(c_sz);
let magicless = framed[4..].to_vec();
let mut dctx = ZSTD_DCtx::new();
let _ = ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless);
ZSTD_initDStream(&mut dctx);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let _hint =
ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &magicless, &mut in_pos);
for _ in 0..8 {
if out_pos >= src.len() {
break;
}
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &[], &mut 0usize);
}
assert_eq!(&out[..out_pos], &src[..]);
}
fn magicless_rle_frame(content_size: usize, value: u8) -> Vec<u8> {
use crate::common::mem::MEM_writeLE24;
use crate::compress::zstd_compress::{
ZSTD_FrameParameters, ZSTD_writeFrameHeader_advanced, ZSTD_FRAMEHEADERSIZE_MAX,
};
use crate::decompress::zstd_decompress_block::blockType_e;
let fparams = ZSTD_FrameParameters {
contentSizeFlag: 1,
checksumFlag: 0,
noDictIDFlag: 1,
};
let mut header = [0u8; ZSTD_FRAMEHEADERSIZE_MAX];
let hsize = ZSTD_writeFrameHeader_advanced(
&mut header,
&fparams,
17,
content_size as u64,
0,
ZSTD_format_e::ZSTD_f_zstd1_magicless,
);
assert!(!crate::common::error::ERR_isError(hsize));
let mut frame = Vec::with_capacity(hsize + 4);
frame.extend_from_slice(&header[..hsize]);
let block_header =
1u32 | ((blockType_e::bt_rle as u32) << 1) | ((content_size as u32) << 3);
let mut bh = [0u8; 3];
MEM_writeLE24(&mut bh, block_header);
frame.extend_from_slice(&bh);
frame.push(value);
frame
}
fn no_fcs_rle_frame(content_size: usize, value: u8) -> Vec<u8> {
use crate::common::mem::MEM_writeLE24;
use crate::decompress::zstd_decompress_block::blockType_e;
let mut frame = Vec::new();
frame.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
frame.push(0x00); frame.push(0x40); let block_header =
1u32 | ((blockType_e::bt_rle as u32) << 1) | ((content_size as u32) << 3);
let mut bh = [0u8; 3];
MEM_writeLE24(&mut bh, block_header);
frame.extend_from_slice(&bh);
frame.push(value);
frame
}
#[test]
fn decompressStream_magicless_declared_fcs_allocates_from_format_aware_header() {
let content_size = 2048usize;
let frame = magicless_rle_frame(content_size, b'M');
assert!(content_size > frame.len() * 32);
let mut dctx = ZSTD_DCtx::new();
let _ = ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless);
ZSTD_initDStream(&mut dctx);
let mut out = vec![0u8; content_size];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let hint = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &frame, &mut in_pos);
assert!(
!crate::common::error::ERR_isError(hint),
"stream decode failed: {}",
crate::common::error::ERR_getErrorName(hint)
);
assert_eq!(in_pos, frame.len());
assert_eq!(out_pos, content_size);
assert!(out.iter().all(|&b| b == b'M'));
}
#[test]
fn decompressStream_unknown_fcs_high_ratio_uses_frame_bound_not_32x() {
let content_size = 4096usize;
let frame = no_fcs_rle_frame(content_size, b'R');
assert_eq!(ZSTD_getFrameContentSize(&frame), ZSTD_CONTENTSIZE_UNKNOWN);
assert!(content_size > frame.len() * 32);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = vec![0u8; content_size];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let hint = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &frame, &mut in_pos);
assert!(
!crate::common::error::ERR_isError(hint),
"stream decode failed: {}",
crate::common::error::ERR_getErrorName(hint)
);
assert_eq!(in_pos, frame.len());
assert_eq!(out_pos, content_size);
assert!(out.iter().all(|&b| b == b'R'));
}
#[test]
fn decompressStream_bad_magic_is_error_not_input_hint() {
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = [0u8; 16];
let mut out_pos = 0usize;
let mut in_pos = 0usize;
let rc = ZSTD_decompressStream(
&mut dctx,
&mut out,
&mut out_pos,
&[0xFF, 0x00, 0x00, 0x00, 0x00],
&mut in_pos,
);
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(
crate::common::error::ERR_getErrorCode(rc),
ErrorCode::PrefixUnknown
);
}
#[test]
fn decompressStream_partial_header_returns_missing_prefix_hint() {
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = [0u8; 16];
let mut out_pos = 0usize;
let mut in_pos = 0usize;
let rc = ZSTD_decompressStream(
&mut dctx,
&mut out,
&mut out_pos,
&[0x28, 0xB5],
&mut in_pos,
);
assert_eq!(rc, ZSTD_startingInputLength(dctx.format) - 2);
assert_eq!(in_pos, 2);
assert_eq!(out_pos, 0);
}
#[test]
fn decompress_usingDict_magicless_declared_fcs_allocates_from_format_aware_header() {
let content_size = 2048usize;
let frame = magicless_rle_frame(content_size, b'D');
assert!(content_size > frame.len() * 32);
let mut dctx = ZSTD_DCtx::new();
let _ = ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless);
let mut out = vec![0u8; content_size];
let decoded = ZSTD_decompress_usingDict(&mut dctx, &mut out, &frame, &[]);
assert!(
!crate::common::error::ERR_isError(decoded),
"prepared-dict decode failed: {}",
crate::common::error::ERR_getErrorName(decoded)
);
assert_eq!(decoded, content_size);
assert!(out.iter().all(|&b| b == b'D'));
}
#[test]
fn decompressFrame_honors_magicless_format() {
use crate::common::xxhash::XXH64_state_t;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
use crate::decompress::zstd_decompress_block::{
ZSTD_DCtx, ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let src = b"Hello, magicless zstd world! 0123456789";
let mut zstd_framed = vec![0u8; ZSTD_compressBound(src.len())];
let c_sz = ZSTD_compress(&mut zstd_framed, src, 1);
assert!(!crate::common::error::ERR_isError(c_sz));
zstd_framed.truncate(c_sz);
let magicless = &zstd_framed[4..];
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let rc = ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless);
assert!(!crate::common::error::ERR_isError(rc));
let mut out = vec![0u8; src.len()];
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let mut consumed = 0usize;
let decoded = ZSTD_decompressFrame(
&mut dctx,
&mut rep,
&mut xxh,
&mut out,
magicless,
&mut consumed,
);
assert!(
!crate::common::error::ERR_isError(decoded),
"decompressFrame failed: {}",
crate::common::error::ERR_getErrorName(decoded)
);
assert_eq!(decoded, src.len());
assert_eq!(&out[..decoded], src);
assert_eq!(consumed, magicless.len());
}
#[test]
fn decompressContinue_rejects_wrong_chunk_size() {
let mut dctx = ZSTD_DCtx::default();
let mut dst = [0u8; 64];
let src = b"some-input";
let rc = ZSTD_decompressContinue(&mut dctx, &mut dst, src);
assert!(crate::common::error::ERR_isError(rc));
use crate::common::error::ERR_getErrorCode;
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn decompressContinue_wrong_chunk_size_preserves_continuity_state() {
let mut dctx = ZSTD_DCtx::default();
let prefix = vec![0u8; 8];
dctx.prefixStart = Some(prefix.as_ptr() as usize);
dctx.previousDstEnd = Some(prefix.as_ptr() as usize + prefix.len());
dctx.dictEnd = Some(prefix.as_ptr() as usize);
dctx.virtualStart = Some(prefix.as_ptr() as usize);
let before = (
dctx.prefixStart,
dctx.previousDstEnd,
dctx.dictEnd,
dctx.virtualStart,
);
let mut dst = [0u8; 64];
let rc = ZSTD_decompressContinue(&mut dctx, &mut dst, b"wrong-size");
assert!(crate::common::error::ERR_isError(rc));
use crate::common::error::ERR_getErrorCode;
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
assert_eq!(
(
dctx.prefixStart,
dctx.previousDstEnd,
dctx.dictEnd,
dctx.virtualStart,
),
before
);
}
fn raw_block_frame_for_continue(payload: &[u8], checksum: bool) -> Vec<u8> {
use crate::common::mem::{MEM_writeLE24, MEM_writeLE32};
use crate::common::xxhash::{XXH64_digest, XXH64_reset, XXH64_state_t, XXH64_update};
use crate::compress::zstd_compress::{
ZSTD_FrameParameters, ZSTD_writeFrameHeader, ZSTD_FRAMEHEADERSIZE_MAX,
};
let fparams = ZSTD_FrameParameters {
contentSizeFlag: 1,
checksumFlag: checksum as u32,
noDictIDFlag: 1,
};
let mut header = [0u8; ZSTD_FRAMEHEADERSIZE_MAX];
let hsize = ZSTD_writeFrameHeader(&mut header, &fparams, 17, payload.len() as u64, 0);
assert!(!crate::common::error::ERR_isError(hsize));
let mut frame =
Vec::with_capacity(hsize + 3 + payload.len() + if checksum { 4 } else { 0 });
frame.extend_from_slice(&header[..hsize]);
let block_header = 1u32 | ((payload.len() as u32) << 3);
let mut bh = [0u8; 3];
MEM_writeLE24(&mut bh, block_header);
frame.extend_from_slice(&bh);
frame.extend_from_slice(payload);
if checksum {
let mut xxh = XXH64_state_t::default();
XXH64_reset(&mut xxh, 0);
XXH64_update(&mut xxh, payload);
let mut check = [0u8; 4];
MEM_writeLE32(&mut check, XXH64_digest(&xxh) as u32);
frame.extend_from_slice(&check);
}
frame
}
#[test]
fn decompressContinue_updates_previous_dst_end_after_zero_output_block() {
let frame = no_fcs_rle_frame(0, 0x42);
let mut dctx = ZSTD_DCtx::default();
let mut out = vec![0u8; 16];
let mut empty = [];
let mut ip = 0usize;
while ip < frame.len() {
let chunk = ZSTD_nextSrcSizeToDecompress(&dctx);
assert!(chunk > 0);
let input_type = ZSTD_nextInputType(&dctx);
let produced = if matches!(
input_type,
ZSTD_nextInputType_e::ZSTDnit_block | ZSTD_nextInputType_e::ZSTDnit_lastBlock
) {
ZSTD_decompressContinue(&mut dctx, &mut out, &frame[ip..ip + chunk])
} else {
ZSTD_decompressContinue(&mut dctx, &mut empty, &frame[ip..ip + chunk])
};
assert!(
!crate::common::error::ERR_isError(produced),
"decompressContinue failed at ip={ip}: {}",
crate::common::error::ERR_getErrorName(produced)
);
ip += chunk;
}
assert_eq!(ZSTD_nextSrcSizeToDecompress(&dctx), 0);
assert_eq!(dctx.previousDstEnd, Some(out.as_ptr() as usize));
}
fn drive_continue_frame(frame: &[u8], out_capacity: usize) -> (ZSTD_DCtx, Vec<u8>, usize) {
let mut dctx = ZSTD_DCtx::default();
let mut out = vec![0u8; out_capacity];
let mut ip = 0usize;
let mut op = 0usize;
while ip < frame.len() {
let chunk = ZSTD_nextSrcSizeToDecompress(&dctx);
assert!(
chunk > 0,
"decoder reported completion before consuming frame"
);
let produced =
ZSTD_decompressContinue(&mut dctx, &mut out[op..], &frame[ip..ip + chunk]);
assert!(
!crate::common::error::ERR_isError(produced),
"decompressContinue failed at ip={ip}: {}",
crate::common::error::ERR_getErrorName(produced)
);
ip += chunk;
op += produced;
}
(dctx, out, op)
}
#[test]
fn decompressContinue_roundtrips_single_raw_block_frame() {
let payload = b"legacy continue raw block";
let frame = raw_block_frame_for_continue(payload, false);
let (dctx, out, op) = drive_continue_frame(&frame, payload.len());
assert_eq!(op, payload.len());
assert_eq!(&out[..op], payload);
assert_eq!(ZSTD_nextSrcSizeToDecompress(&dctx), 0);
assert_eq!(dctx.previousDstEnd, Some(out.as_ptr() as usize + op));
}
#[test]
fn decompressContinue_checksum_completion_reports_zero_next_src_size() {
let payload = b"legacy continue checksum raw block";
let frame = raw_block_frame_for_continue(payload, true);
let (dctx, out, op) = drive_continue_frame(&frame, payload.len());
assert_eq!(op, payload.len());
assert_eq!(&out[..op], payload);
assert_eq!(ZSTD_nextSrcSizeToDecompress(&dctx), 0);
assert_eq!(dctx.stage, ZSTD_dStage::ZSTDds_getFrameHeaderSize);
assert_eq!(dctx.previousDstEnd, Some(out.as_ptr() as usize + op));
}
#[test]
fn decompressContinue_accepts_partial_raw_block_input() {
let payload = b"legacy continue accepts partial raw block chunks";
let frame = raw_block_frame_for_continue(payload, false);
let mut dctx = ZSTD_DCtx::default();
let mut out = vec![0u8; payload.len()];
let mut ip = 0usize;
let mut op = 0usize;
while ZSTD_nextInputType(&dctx) != ZSTD_nextInputType_e::ZSTDnit_lastBlock {
let chunk = ZSTD_nextSrcSizeToDecompress(&dctx);
assert!(chunk > 0);
let produced =
ZSTD_decompressContinue(&mut dctx, &mut out[op..], &frame[ip..ip + chunk]);
assert!(
!crate::common::error::ERR_isError(produced),
"setup chunk failed: {}",
crate::common::error::ERR_getErrorName(produced)
);
ip += chunk;
op += produced;
}
let first = payload.len() / 3;
assert_eq!(
ZSTD_nextSrcSizeToDecompressWithInputSize(&dctx, first),
first
);
let produced = ZSTD_decompressContinue(&mut dctx, &mut out[op..], &frame[ip..ip + first]);
assert!(
!crate::common::error::ERR_isError(produced),
"partial raw chunk failed: {}",
crate::common::error::ERR_getErrorName(produced)
);
assert_eq!(produced, first);
assert_eq!(
dctx.previousDstEnd,
Some(out[op..].as_ptr() as usize + produced)
);
ip += first;
op += produced;
let remaining = payload.len() - first;
assert_eq!(
ZSTD_nextSrcSizeToDecompressWithInputSize(&dctx, remaining),
remaining
);
let produced =
ZSTD_decompressContinue(&mut dctx, &mut out[op..], &frame[ip..ip + remaining]);
assert!(
!crate::common::error::ERR_isError(produced),
"final raw chunk failed: {}",
crate::common::error::ERR_getErrorName(produced)
);
op += produced;
assert_eq!(&out[..op], payload);
assert_eq!(ZSTD_nextSrcSizeToDecompress(&dctx), 0);
assert_eq!(dctx.stage, ZSTD_dStage::ZSTDds_getFrameHeaderSize);
assert_eq!(dctx.previousDstEnd, Some(out.as_ptr() as usize + op));
}
#[test]
fn decompressContinue_skippable_completion_reports_zero_next_src_size() {
let payload = b"metadata payload";
let mut frame = Vec::new();
frame.extend_from_slice(&(ZSTD_MAGIC_SKIPPABLE_START | 3).to_le_bytes());
frame.extend_from_slice(&(payload.len() as u32).to_le_bytes());
frame.extend_from_slice(payload);
let (dctx, _out, op) = drive_continue_frame(&frame, 1);
assert_eq!(op, 0);
assert_eq!(ZSTD_nextSrcSizeToDecompress(&dctx), 0);
assert_eq!(dctx.stage, ZSTD_dStage::ZSTDds_getFrameHeaderSize);
}
#[test]
fn decompressContinue_multi_block_with_independent_dst_buffers() {
use crate::compress::zstd_compress::ZSTD_compress;
let chunk: Vec<u8> = (0..65_536u32)
.map(|i| ((i * 17 + 3) & 0xFF) as u8)
.collect();
let mut payload = chunk.clone();
for _ in 0..8 {
payload.extend_from_slice(&chunk);
}
let mut compressed = vec![0u8; payload.len() + 1024];
let n = ZSTD_compress(&mut compressed, &payload, 1);
assert!(
!crate::common::error::ERR_isError(n),
"compress failed: {}",
crate::common::error::ERR_getErrorName(n)
);
compressed.truncate(n);
let mut dctx = ZSTD_DCtx::default();
let mut ip = 0usize;
let mut decoded: Vec<u8> = Vec::with_capacity(payload.len());
while ip < compressed.len() {
let chunk = ZSTD_nextSrcSizeToDecompress(&dctx);
if chunk == 0 {
break;
}
let block_max = dctx
.fParams
.blockSizeMax
.max(crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX as u32)
as usize;
let mut block_dst = vec![0u8; block_max + 64];
let produced =
ZSTD_decompressContinue(&mut dctx, &mut block_dst, &compressed[ip..ip + chunk]);
assert!(
!crate::common::error::ERR_isError(produced),
"decompressContinue at ip={ip}, chunk={chunk}: {}",
crate::common::error::ERR_getErrorName(produced)
);
decoded.extend_from_slice(&block_dst[..produced]);
ip += chunk;
}
assert_eq!(decoded.len(), payload.len(), "decoded length mismatch");
assert_eq!(decoded, payload, "decoded bytes differ from payload");
}
#[test]
fn stream_workspace_helpers_track_overflow_and_continue_wrapper() {
use crate::common::mem::MEM_writeLE24;
use crate::compress::zstd_compress::{
ZSTD_FrameParameters, ZSTD_writeFrameHeader, ZSTD_FRAMEHEADERSIZE_MAX,
};
let mut dctx = ZSTD_DCtx::default();
dctx.stream_in_buffer.reserve(128);
dctx.stream_out_buffer.reserve(128);
assert_eq!(ZSTD_DCtx_isOverflow(&dctx, 16, 16), 1);
ZSTD_DCtx_updateOversizedDuration(&mut dctx, 16, 16);
assert_eq!(dctx.oversizedDuration, 1);
ZSTD_DCtx_updateOversizedDuration(&mut dctx, 1024, 1024);
assert_eq!(dctx.oversizedDuration, 0);
assert_eq!(ZSTD_checkOutBuffer(&dctx, &[], 0), 0);
let payload = b"continue-stream wrapper";
let fparams = ZSTD_FrameParameters {
contentSizeFlag: 1,
checksumFlag: 0,
noDictIDFlag: 1,
};
let mut header = [0u8; ZSTD_FRAMEHEADERSIZE_MAX];
let hsize = ZSTD_writeFrameHeader(&mut header, &fparams, 17, payload.len() as u64, 0);
assert!(!crate::common::error::ERR_isError(hsize));
let mut frame = Vec::new();
frame.extend_from_slice(&header[..hsize]);
let blockHeader = 1u32 | ((payload.len() as u32) << 3);
let mut bh = [0u8; 3];
MEM_writeLE24(&mut bh, blockHeader);
frame.extend_from_slice(&bh);
frame.extend_from_slice(payload);
let mut zds = ZSTD_DCtx::default();
let mut out = vec![0u8; payload.len()];
let mut ip = 0usize;
let mut op = 0usize;
while ip < frame.len() {
let chunk = ZSTD_nextSrcSizeToDecompress(&zds);
let rc =
ZSTD_decompressContinueStream(&mut zds, &mut out, &mut op, &frame[ip..ip + chunk]);
assert!(
!crate::common::error::ERR_isError(rc),
"continue stream failed at ip={ip}: {}",
crate::common::error::ERR_getErrorName(rc)
);
ip += chunk;
}
assert_eq!(&out[..op], payload);
}
#[test]
fn decodeFrameHeader_sets_checksum_and_rejects_wrong_dict() {
use crate::common::error::ERR_getErrorCode;
use crate::compress::zstd_compress::{
ZSTD_FrameParameters, ZSTD_writeFrameHeader, ZSTD_FRAMEHEADERSIZE_MAX,
};
let fparams = ZSTD_FrameParameters {
contentSizeFlag: 1,
checksumFlag: 1,
noDictIDFlag: 0,
};
let mut header = [0u8; ZSTD_FRAMEHEADERSIZE_MAX];
let hsize = ZSTD_writeFrameHeader(&mut header, &fparams, 17, 11, 0x1234);
assert!(!crate::common::error::ERR_isError(hsize));
let mut dctx = ZSTD_DCtx::default();
dctx.dictID = 0x1234;
let rc = ZSTD_decodeFrameHeader(&mut dctx, &header[..hsize], hsize);
assert_eq!(rc, 0);
assert_eq!(dctx.fParams.dictID, 0x1234);
assert_eq!(dctx.validateChecksum, 1);
assert_eq!(dctx.processedCSize, hsize as u64);
let mut wrong = ZSTD_DCtx::default();
wrong.dictID = 0x5678;
let rc = ZSTD_decodeFrameHeader(&mut wrong, &header[..hsize], hsize);
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::DictionaryWrong);
}
#[test]
fn getDDict_and_refDictContent_follow_upstream_lifecycle() {
let mut dctx = ZSTD_DCtx::default();
let dict = b"raw dictionary bytes";
assert_eq!(ZSTD_refDictContent(&mut dctx, dict), 0);
dctx.dictUses = ZSTD_dictUses_e::ZSTD_use_once;
assert_eq!(dctx.prefixStart, Some(dict.as_ptr() as usize));
assert_eq!(
dctx.previousDstEnd,
Some(dict.as_ptr() as usize + dict.len())
);
let used = ZSTD_getDDict(&mut dctx).expect("one-shot dict");
assert_eq!(used, dict);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(dctx.stream_dict.is_empty());
assert!(ZSTD_getDDict(&mut dctx).is_none());
}
#[test]
fn ddict_hashset_inserts_replaces_and_expands() {
use crate::compress::zstd_compress::ZSTD_customMem;
use crate::decompress::zstd_ddict::ZSTD_DDict;
fn make_ddict(dictID: u32, content: &[u8]) -> ZSTD_DDict {
let mut dict = vec![0u8; ZSTD_FRAMEIDSIZE + 4];
crate::common::mem::MEM_writeLE32(&mut dict[..4], ZSTD_MAGIC_DICTIONARY);
crate::common::mem::MEM_writeLE32(
&mut dict[ZSTD_FRAMEIDSIZE..ZSTD_FRAMEIDSIZE + 4],
dictID,
);
dict.extend_from_slice(content);
ZSTD_DDict::raw_with_dict_id(dictID, &dict)
}
let first = make_ddict(7, b"first");
let replacement = make_ddict(7, b"replacement");
let mut set = ZSTD_createDDictHashSet(ZSTD_customMem::default());
assert_eq!(
ZSTD_DDictHashSet_addDDict(&mut set, &first, ZSTD_customMem::default()),
0
);
assert_eq!(set.ddictPtrCount, 1);
assert!(core::ptr::eq(
ZSTD_DDictHashSet_getDDict(&set, 7).expect("first"),
&first
));
assert_eq!(
ZSTD_DDictHashSet_addDDict(&mut set, &replacement, ZSTD_customMem::default()),
0
);
assert_eq!(set.ddictPtrCount, 1);
assert!(core::ptr::eq(
ZSTD_DDictHashSet_getDDict(&set, 7).expect("replacement"),
&replacement
));
let many: Vec<Box<ZSTD_DDict>> = (100..121)
.map(|id| Box::new(make_ddict(id, &[id as u8; 3])))
.collect();
for ddict in &many {
assert_eq!(
ZSTD_DDictHashSet_addDDict(&mut set, ddict.as_ref(), ZSTD_customMem::default()),
0
);
}
assert!(set.ddictPtrTableSize > DDICT_HASHSET_TABLE_BASE_SIZE);
for ddict in &many {
let found = ZSTD_DDictHashSet_getDDict(
&set,
crate::decompress::zstd_ddict::ZSTD_getDictID_fromDDict(ddict),
)
.expect("present");
assert!(core::ptr::eq(found, ddict.as_ref()));
}
assert!(ZSTD_DDictHashSet_getDDict(&set, 0xFFFF).is_none());
let mut dctx = ZSTD_DCtx::default();
dctx.fParams.dictID = 117;
assert!(ZSTD_DCtx_selectFrameDDict(&mut dctx, &set).is_none());
assert_eq!(dctx.dictID, 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert_eq!(ZSTD_DCtx_refDDict(&mut dctx, &first), 0);
dctx.fParams.dictID = 117;
let selected = ZSTD_DCtx_selectFrameDDict(&mut dctx, &set).expect("selected");
assert_eq!(
crate::decompress::zstd_ddict::ZSTD_getDictID_fromDDict(selected),
117
);
assert_eq!(dctx.dictID, 117);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
assert_eq!(
dctx.stream_dict,
crate::decompress::zstd_ddict::ZSTD_DDict_dictContent(selected)
);
}
#[test]
fn decoder_rejects_corrupted_xxh64_trailer_with_checksumWrong() {
use crate::compress::match_state::ZSTD_compressionParameters;
use crate::compress::zstd_compress::{
ZSTD_FrameParameters, ZSTD_compressBound, ZSTD_compressFrame_fast, ZSTD_getCParams,
};
let src: Vec<u8> = b"payload with xxh64 trailer ".repeat(40);
let bound = ZSTD_compressBound(src.len());
let mut dst = vec![0u8; bound];
let cp: ZSTD_compressionParameters = ZSTD_getCParams(3, src.len() as u64, 0);
let fp = ZSTD_FrameParameters {
contentSizeFlag: 1,
checksumFlag: 1,
noDictIDFlag: 1,
};
let n = ZSTD_compressFrame_fast(&mut dst, &src, cp, fp);
assert!(!crate::common::error::ERR_isError(n));
dst.truncate(n);
let last = dst.len() - 1;
dst[last] ^= 0x01;
let mut out = vec![0u8; src.len() + 64];
let rc = ZSTD_decompress(&mut out, &dst);
assert!(
crate::common::error::ERR_isError(rc),
"decoder missed corrupted checksum (rc={rc})"
);
assert_eq!(
crate::common::error::ERR_getErrorCode(rc),
ErrorCode::ChecksumWrong,
"expected ChecksumWrong, got {:?}",
crate::common::error::ERR_getErrorCode(rc)
);
}
#[test]
fn resetDStream_clears_streaming_state_and_returns_next_hint() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let mut dctx = ZSTD_DCtx::new();
dctx.stream_in_buffer.extend_from_slice(b"pending-in");
dctx.stream_out_buffer.extend_from_slice(b"pending-out");
dctx.stream_out_drained = 4;
dctx.stream_dict = b"sticky-dict".to_vec();
let hint = ZSTD_resetDStream(&mut dctx);
assert_eq!(hint, ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1));
assert_eq!(hint, 5);
assert!(dctx.stream_in_buffer.is_empty());
assert!(dctx.stream_out_buffer.is_empty());
assert_eq!(dctx.stream_out_drained, 0);
assert_eq!(dctx.stream_dict, b"sticky-dict");
}
#[test]
fn multi_frame_roundtrip_across_3_frames_with_interleaved_skippables() {
use crate::compress::zstd_compress::{
ZSTD_compress, ZSTD_compressBound, ZSTD_writeSkippableFrame,
};
let payloads: [&[u8]; 3] = [
b"payload-alpha ",
b"payload-beta-is-a-bit-longer ",
b"payload-gamma! ",
];
let mut combined = Vec::new();
let mut expected = Vec::new();
let mut skip = vec![0u8; 32];
let n = ZSTD_writeSkippableFrame(&mut skip, b"leading", 0);
combined.extend_from_slice(&skip[..n]);
for (i, payload) in payloads.iter().enumerate() {
let bound = ZSTD_compressBound(payload.len());
let mut c = vec![0u8; bound];
let n = ZSTD_compress(&mut c, payload, 1);
assert!(!crate::common::error::ERR_isError(n));
combined.extend_from_slice(&c[..n]);
expected.extend_from_slice(payload);
if i == 0 {
let mut skip2 = vec![0u8; 24];
let n2 = ZSTD_writeSkippableFrame(&mut skip2, b"mid", 7);
combined.extend_from_slice(&skip2[..n2]);
}
}
let mut out = vec![0u8; expected.len() + 64];
let d = ZSTD_decompress(&mut out, &combined);
assert!(!crate::common::error::ERR_isError(d));
assert_eq!(d, expected.len());
assert_eq!(&out[..d], &expected[..]);
}
#[test]
fn zstd_decompress_loops_over_concatenated_frames() {
use crate::compress::zstd_compress::{
ZSTD_compress, ZSTD_compressBound, ZSTD_writeSkippableFrame,
};
let src = b"concat probe content ".to_vec();
let bound = ZSTD_compressBound(src.len());
let mut frame = vec![0u8; bound];
let n = ZSTD_compress(&mut frame, &src, 1);
frame.truncate(n);
let mut combined = frame.clone();
let mut skip = vec![0u8; 16];
let skip_n = ZSTD_writeSkippableFrame(&mut skip, b"meta", 1);
combined.extend_from_slice(&skip[..skip_n]);
combined.extend_from_slice(&frame);
let mut out = vec![0u8; src.len() * 2 + 64];
let d = ZSTD_decompress(&mut out, &combined);
assert!(!crate::common::error::ERR_isError(d));
assert_eq!(d, src.len() * 2);
assert_eq!(&out[..src.len()], &src[..]);
assert_eq!(&out[src.len()..src.len() * 2], &src[..]);
}
#[test]
fn decompressDCtx_resets_stale_dict_id_between_plain_frames() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::common::mem::MEM_writeLE32;
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::{
ZSTD_DCtx, ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let mut frame = Vec::new();
let mut magic = [0u8; 4];
MEM_writeLE32(&mut magic, ZSTD_MAGICNUMBER);
frame.extend_from_slice(&magic);
frame.push(0x23);
let mut dict_id = [0u8; 4];
MEM_writeLE32(&mut dict_id, 0xAABB_CCDD);
frame.extend_from_slice(&dict_id);
frame.push(0);
frame.extend_from_slice(&[1, 0, 0]);
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
dctx.dictID = 0xAABB_CCDD;
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let mut out = [0u8; 1];
let rc = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &frame);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::DictionaryWrong);
}
#[test]
fn decompress_rejects_truncated_frame_body_with_error() {
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src: Vec<u8> = b"truncation probe content ".repeat(30);
let bound = ZSTD_compressBound(src.len());
let mut compressed = vec![0u8; bound];
let n = ZSTD_compress(&mut compressed, &src, 3);
assert!(!crate::common::error::ERR_isError(n));
let truncated_len = n * 7 / 10;
assert!(truncated_len > 10 && truncated_len < n);
let truncated = &compressed[..truncated_len];
let mut out = vec![0u8; src.len() + 64];
let rc = ZSTD_decompress(&mut out, truncated);
assert!(
crate::common::error::ERR_isError(rc),
"decoder accepted truncated input (rc={rc}) — must reject",
);
}
#[test]
fn two_independent_dctxs_decode_same_frame_to_same_output() {
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src: Vec<u8> = b"DCtx-isolation test payload. ".repeat(30);
let bound = ZSTD_compressBound(src.len());
let mut compressed = vec![0u8; bound];
let n = ZSTD_compress(&mut compressed, &src, 3);
assert!(!crate::common::error::ERR_isError(n));
compressed.truncate(n);
let mut a = ZSTD_DCtx::new();
let mut b = ZSTD_DCtx::new();
ZSTD_DCtx_loadDictionary(&mut a, b"some-dict-A");
ZSTD_DCtx_loadDictionary(&mut b, b"other-dict-B");
let mut out_a = vec![0u8; src.len() + 64];
let d_a = ZSTD_decompress(&mut out_a, &compressed);
assert_eq!(&out_a[..d_a], &src[..]);
let mut out_b = vec![0u8; src.len() + 64];
let d_b = ZSTD_decompress(&mut out_b, &compressed);
assert_eq!(&out_b[..d_b], &src[..]);
assert_eq!(a.stream_dict, b"some-dict-A");
assert_eq!(b.stream_dict, b"other-dict-B");
}
#[test]
fn zstd_decompress_rejects_too_small_dst_buffer() {
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
let src: Vec<u8> = b"payload that won't fit in a tiny dst. ".repeat(20);
let bound = ZSTD_compressBound(src.len());
let mut compressed = vec![0u8; bound];
let n = ZSTD_compress(&mut compressed, &src, 1);
assert!(!crate::common::error::ERR_isError(n));
let mut tiny_dst = [0u8; 16];
let rc = ZSTD_decompress(&mut tiny_dst, &compressed[..n]);
assert!(crate::common::error::ERR_isError(rc));
}
#[test]
fn zstd_decompress_rejects_garbage_without_panicking() {
let mut dst = vec![0u8; 1024];
let test_inputs: Vec<Vec<u8>> = vec![
vec![0u8], vec![0u8; 3], vec![0xFFu8; 8], {
let mut v = ZSTD_MAGICNUMBER.to_le_bytes().to_vec();
v.push(0x20);
v
},
{
let mut v = ZSTD_MAGICNUMBER.to_le_bytes().to_vec();
v.extend_from_slice(&[0x28, 100]); v
},
(0..200u8).map(|i| i.wrapping_mul(17)).collect(), ];
let empty_rc = ZSTD_decompress(&mut dst, &[]);
assert!(!crate::common::error::ERR_isError(empty_rc));
assert_eq!(empty_rc, 0);
for (i, input) in test_inputs.iter().enumerate() {
let rc = ZSTD_decompress(&mut dst, input);
assert!(
crate::common::error::ERR_isError(rc),
"input #{i} (len={}) should have errored but returned {rc}",
input.len(),
);
}
}
#[test]
fn startingInputLength_differs_by_format() {
assert_eq!(ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1), 5);
assert_eq!(
ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1_magicless),
1
);
}
#[test]
fn getFrameHeader_advanced_magicless_skips_magic_check() {
let src = [0x20u8, 42];
let mut zfh = ZSTD_FrameHeader::default();
let rc =
ZSTD_getFrameHeader_advanced(&mut zfh, &src, ZSTD_format_e::ZSTD_f_zstd1_magicless);
assert_eq!(rc, 0);
assert_eq!(zfh.frameContentSize, 42);
assert_eq!(zfh.headerSize, 2);
}
#[test]
fn frameHeaderSize_rejects_too_short_input() {
assert!(crate::common::error::ERR_isError(ZSTD_frameHeaderSize(&[])));
assert!(crate::common::error::ERR_isError(ZSTD_frameHeaderSize(&[
0xFDu8
])));
assert!(crate::common::error::ERR_isError(ZSTD_frameHeaderSize(&[
0u8, 0, 0, 0
])));
}
#[test]
fn frameHeaderSize_returns_exact_size_for_each_layout() {
let magic = ZSTD_MAGICNUMBER.to_le_bytes();
let mut src = magic.to_vec();
src.push(0x20); src.push(42); assert_eq!(ZSTD_frameHeaderSize(&src), 6);
let mut src = magic.to_vec();
src.push(0x00);
src.push(0x20);
assert_eq!(ZSTD_frameHeaderSize(&src), 6);
let mut src = magic.to_vec();
src.push((1 << 6) | (1 << 5));
src.extend_from_slice(&744u16.to_le_bytes());
assert_eq!(ZSTD_frameHeaderSize(&src), 7);
let mut src = magic.to_vec();
src.push((3 << 6) | (1 << 5));
src.extend_from_slice(&0u64.to_le_bytes());
assert_eq!(ZSTD_frameHeaderSize(&src), 13);
}
#[test]
fn frame_header_rejects_oversized_windowLog() {
let mut src = ZSTD_MAGICNUMBER.to_le_bytes().to_vec();
src.push(0x00); src.push(22u8 << 3); let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(
crate::common::error::ERR_getErrorCode(rc),
ErrorCode::FrameParameterWindowTooLarge
);
}
#[test]
fn frame_header_accepts_max_windowLog() {
let mut src = ZSTD_MAGICNUMBER.to_le_bytes().to_vec();
src.push(0x00);
src.push(21u8 << 3);
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 0);
assert_eq!(zfh.windowSize, 1u64 << 31);
}
#[test]
fn decodeFrameHeader_does_not_enforce_dctx_window_limit() {
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let mut src = ZSTD_MAGICNUMBER.to_le_bytes().to_vec();
src.push(0x00); src.push(1);
let mut dctx = ZSTD_DCtx::default();
dctx.d_windowLogMax = 10; dctx.d_maxWindowSize = 1 << 10;
let rc = ZSTD_decodeFrameHeader(&mut dctx, &src, src.len());
assert_eq!(rc, 0);
}
#[test]
fn frame_header_bad_magic_errors() {
let mut zfh = ZSTD_FrameHeader::default();
let src = [0xFF, 0xFF, 0xFF, 0xFF, 0, 0];
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(
crate::common::error::ERR_getErrorCode(rc),
ErrorCode::PrefixUnknown
);
}
#[test]
fn frame_header_skippable_frame() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&42u32.to_le_bytes());
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 0);
assert_eq!(zfh.frameType, ZSTD_FrameType_e::ZSTD_skippableFrame);
assert_eq!(zfh.frameContentSize, 42);
assert_eq!(zfh.dictID, 0);
assert_eq!(zfh.headerSize, ZSTD_SKIPPABLEHEADERSIZE as u32);
}
#[test]
fn frame_header_single_segment_no_dict_no_fcs() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x20); src.push(100); let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 0);
assert_eq!(zfh.frameType, ZSTD_FrameType_e::ZSTD_frame);
assert_eq!(zfh.frameContentSize, 100);
assert_eq!(zfh.windowSize, 100); assert_eq!(zfh.checksumFlag, 0);
assert_eq!(zfh.dictID, 0);
assert_eq!(zfh.headerSize, 6);
}
#[test]
fn frame_header_fcs_size_variants() {
{
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push((1 << 6) | (1 << 5)); src.extend_from_slice(&744u16.to_le_bytes());
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 0);
assert_eq!(zfh.frameContentSize, 1000);
assert_eq!(zfh.windowSize, 1000);
assert_eq!(zfh.headerSize, 7);
}
{
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push((2 << 6) | (1 << 5)); src.extend_from_slice(&123_456u32.to_le_bytes());
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 0);
assert_eq!(zfh.frameContentSize, 123_456);
assert_eq!(zfh.windowSize, 123_456);
assert_eq!(zfh.headerSize, 9);
}
{
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push((3 << 6) | (1 << 5)); src.extend_from_slice(&9_999_999_999u64.to_le_bytes());
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 0);
assert_eq!(zfh.frameContentSize, 9_999_999_999);
assert_eq!(zfh.headerSize, 13);
}
}
#[test]
fn frame_header_window_descriptor() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x00); src.push(0x20); let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert_eq!(rc, 0);
assert_eq!(zfh.windowSize, 1u64 << 14);
assert_eq!(zfh.frameContentSize, ZSTD_CONTENTSIZE_UNKNOWN);
}
fn make_raw_hello_frame() -> Vec<u8> {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x20); src.push(5); let bh = (5u32 << 3) | (1); src.push((bh & 0xFF) as u8);
src.push(((bh >> 8) & 0xFF) as u8);
src.push(((bh >> 16) & 0xFF) as u8);
src.extend_from_slice(b"HELLO");
src
}
#[test]
fn get_frame_content_size_returns_declared_fcs() {
let src = make_raw_hello_frame();
let fcs = ZSTD_getFrameContentSize(&src);
assert_eq!(fcs, 5);
}
#[test]
fn get_frame_content_size_unknown_on_absent_fcs() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x00); src.push(0x20); let fcs = ZSTD_getFrameContentSize(&src);
assert_eq!(fcs, ZSTD_CONTENTSIZE_UNKNOWN);
}
#[test]
fn get_frame_content_size_error_on_bad_magic() {
let src = [0xFFu8; 16];
let fcs = ZSTD_getFrameContentSize(&src);
assert_eq!(fcs, ZSTD_CONTENTSIZE_ERROR);
}
#[test]
fn get_frame_content_size_zero_for_skippable_frame() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&12u32.to_le_bytes()); src.extend_from_slice(&[0u8; 12]);
assert_eq!(ZSTD_getFrameContentSize(&src), 0);
}
#[test]
fn find_frame_compressed_size_matches_hand_count() {
let src = make_raw_hello_frame();
let sz = ZSTD_findFrameCompressedSize(&src);
assert_eq!(sz, src.len());
}
#[test]
fn decompressionMargin_nonzero_for_valid_frame() {
let src = b"the fast brown fox ".repeat(32);
let mut dst = vec![0u8; 256];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut dst, &src, 3);
dst.truncate(n);
let margin = ZSTD_decompressionMargin(&dst);
assert!(!crate::common::error::ERR_isError(margin));
assert!(margin > 0);
assert!(margin < 200 * 1024);
}
#[test]
fn decompressionMargin_includes_checksum_bytes_when_flag_is_set() {
let src = b"fast brown fox ".repeat(32);
let mut cctx_a = crate::compress::zstd_compress::ZSTD_createCCtx().unwrap();
crate::compress::zstd_compress::ZSTD_CCtx_setParameter(
&mut cctx_a,
crate::compress::zstd_compress::ZSTD_cParameter::ZSTD_c_checksumFlag,
0,
);
let mut dst_no_chk = vec![0u8; 256];
let n_no =
crate::compress::zstd_compress::ZSTD_compress2(&mut cctx_a, &mut dst_no_chk, &src);
assert!(!crate::common::error::ERR_isError(n_no));
dst_no_chk.truncate(n_no);
let mut cctx_b = crate::compress::zstd_compress::ZSTD_createCCtx().unwrap();
crate::compress::zstd_compress::ZSTD_CCtx_setParameter(
&mut cctx_b,
crate::compress::zstd_compress::ZSTD_cParameter::ZSTD_c_checksumFlag,
1,
);
let mut dst_chk = vec![0u8; 256];
let n_chk = crate::compress::zstd_compress::ZSTD_compress2(&mut cctx_b, &mut dst_chk, &src);
assert!(!crate::common::error::ERR_isError(n_chk));
dst_chk.truncate(n_chk);
let m_no = ZSTD_decompressionMargin(&dst_no_chk);
let m_chk = ZSTD_decompressionMargin(&dst_chk);
assert!(!crate::common::error::ERR_isError(m_no));
assert!(!crate::common::error::ERR_isError(m_chk));
assert!(
m_chk >= m_no + 4,
"checksum margin must include ≥4 bytes over plain margin: chk={m_chk}, no={m_no}"
);
}
#[test]
fn decompressionMargin_rejects_garbage_input() {
let rc = ZSTD_decompressionMargin(&[0u8; 32]);
assert!(crate::common::error::ERR_isError(rc));
}
#[test]
fn decompressBound_empty_input_is_zero() {
assert_eq!(ZSTD_decompressBound(&[]), 0);
}
#[test]
fn decompressBound_corrupted_input_returns_error_sentinel() {
let bogus = [0u8, 1, 2, 3, 4, 5, 6, 7];
assert_eq!(ZSTD_decompressBound(&bogus), ZSTD_CONTENTSIZE_ERROR);
}
#[test]
fn decompressBound_upper_bounds_real_decompressed_size() {
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
for &size in &[0usize, 1, 33, 1024, 65_536, 131_073] {
let src: Vec<u8> = (0..size as u32).map(|i| (i ^ (i >> 5)) as u8).collect();
for &level in &[1i32, 3, 10] {
let bound = ZSTD_compressBound(src.len());
let mut dst = vec![0u8; bound];
let n = ZSTD_compress(&mut dst, &src, level);
assert!(!crate::common::error::ERR_isError(n));
let db = ZSTD_decompressBound(&dst[..n]);
assert!(
db != ZSTD_CONTENTSIZE_ERROR,
"decompressBound flagged error on valid frame size={size} level={level}"
);
assert!(
db >= size as u64,
"bound under-reports: size={size} level={level} bound={db}",
);
}
}
}
#[test]
fn decompressBound_sums_frames() {
let mut src = make_raw_hello_frame();
src.extend_from_slice(&make_raw_hello_frame());
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&4u32.to_le_bytes());
src.extend_from_slice(&[0u8; 4]);
let bound = ZSTD_decompressBound(&src);
assert_eq!(bound, 10);
}
#[test]
fn getDecompressedSize_zero_on_unknown() {
let raw = make_raw_hello_frame();
let got = ZSTD_getDecompressedSize(&raw);
assert_eq!(got, 5);
let bogus = [0u8, 1, 2, 3];
assert_eq!(ZSTD_getDecompressedSize(&bogus), 0);
let mut no_fcs = Vec::new();
no_fcs.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
no_fcs.push(0x00); no_fcs.push(0x20); assert_eq!(ZSTD_getDecompressedSize(&no_fcs), 0);
}
#[test]
fn decodingBufferSize_min_basic() {
let sz = ZSTD_decodingBufferSize_min(1024, 1024);
assert!(!crate::common::error::ERR_isError(sz));
assert!(sz >= 1024);
}
#[test]
fn decodingBufferSize_min_matches_upstream_formula() {
use crate::common::zstd_internal::WILDCOPY_OVERLENGTH;
use crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX;
let w = 1024u64;
let fcs = u64::MAX; let expected = w + 2 * w + (WILDCOPY_OVERLENGTH as u64) * 2;
assert_eq!(ZSTD_decodingBufferSize_min(w, fcs) as u64, expected);
let w2 = 1u64 << 20;
let expected2 = w2 + 2 * ZSTD_BLOCKSIZE_MAX as u64 + (WILDCOPY_OVERLENGTH as u64) * 2;
assert_eq!(ZSTD_decodingBufferSize_min(w2, fcs) as u64, expected2);
let tiny = 200u64;
assert_eq!(ZSTD_decodingBufferSize_min(w2, tiny) as u64, tiny);
}
#[test]
fn copyDCtx_deep_copies_all_state() {
let mut src = ZSTD_DCtx::default();
src.stream_dict = b"marker-dict".to_vec();
src.d_windowLogMax = 25;
src.d_maxWindowSize = 1u64 << 25;
src.d_maxWindowSizeSet = true;
let mut dst = ZSTD_DCtx::default();
ZSTD_copyDCtx(&mut dst, &src);
assert_eq!(dst.stream_dict, b"marker-dict");
assert_eq!(dst.d_windowLogMax, 25);
assert_eq!(dst.d_maxWindowSize, 1u64 << 25);
assert!(dst.d_maxWindowSizeSet);
}
#[test]
fn initDStream_family_returns_startingInputLength() {
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let expected = ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1);
let mut a = ZSTD_DCtx::default();
assert_eq!(ZSTD_initDStream(&mut a), expected);
let mut b = ZSTD_DCtx::default();
assert_eq!(ZSTD_initDStream_usingDict(&mut b, b"seed-dict"), expected);
let mut c = ZSTD_DCtx::default();
let ddict = ZSTD_createDDict(b"seed-ddict").expect("ddict");
assert_eq!(ZSTD_initDStream_usingDDict(&mut c, &ddict), expected);
}
#[test]
fn initDStream_usingDDict_copies_dict_content() {
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let dict = b"test-dict-content".to_vec();
let ddict = ZSTD_createDDict(&dict).expect("ddict");
let mut dctx = ZSTD_DCtx::default();
let rc = ZSTD_initDStream_usingDDict(&mut dctx, &ddict);
assert_eq!(rc, ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1));
assert_eq!(dctx.stream_dict, dict);
}
#[test]
fn nextSrcSizeToDecompress_starts_at_frame_header_prefix() {
let dctx = ZSTD_DCtx::default();
assert_eq!(
ZSTD_nextSrcSizeToDecompress(&dctx),
ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1)
);
}
#[test]
fn insertBlock_updates_history_end_and_contiguity_state() {
let mut dctx = ZSTD_DCtx::default();
let a = vec![1u8; 8];
let b = vec![2u8; 5];
let n = ZSTD_insertBlock(&mut dctx, &a);
assert_eq!(n, a.len());
assert_eq!(dctx.prefixStart, Some(a.as_ptr() as usize));
assert_eq!(dctx.previousDstEnd, Some(a.as_ptr() as usize + a.len()));
assert_eq!(dctx.dictEnd, None);
let n = ZSTD_insertBlock(&mut dctx, &b);
assert_eq!(n, b.len());
assert_eq!(dctx.dictEnd, Some(a.as_ptr() as usize + a.len()));
assert_eq!(dctx.prefixStart, Some(b.as_ptr() as usize));
assert_eq!(dctx.previousDstEnd, Some(b.as_ptr() as usize + b.len()));
}
#[test]
fn insertBlock_history_feeds_standalone_decompressBlock() {
use crate::compress::zstd_compress::{
ZSTD_compressBegin, ZSTD_compressBlock, ZSTD_createCCtx,
};
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let first: Vec<u8> = b"insert-block-standalone-history-"
.iter()
.cycle()
.take(4096)
.copied()
.collect();
let second = first.clone();
let mut cctx = ZSTD_createCCtx().expect("cctx");
let rc = ZSTD_compressBegin(&mut cctx, 1);
assert!(!crate::common::error::ERR_isError(rc));
let mut first_body = vec![0u8; first.len()];
let first_csize = ZSTD_compressBlock(&mut cctx, &mut first_body, &first);
assert!(
!crate::common::error::ERR_isError(first_csize),
"first compressBlock failed: {}",
crate::common::error::ERR_getErrorName(first_csize)
);
let mut second_body = vec![0u8; second.len()];
let second_csize = ZSTD_compressBlock(&mut cctx, &mut second_body, &second);
assert!(
!crate::common::error::ERR_isError(second_csize),
"second compressBlock failed: {}",
crate::common::error::ERR_getErrorName(second_csize)
);
assert!(
second_csize > 0,
"second block was not emitted as a compressed standalone body"
);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_insertBlock(&mut dctx, &first), first.len());
let inserted_end = first.as_ptr() as usize + first.len();
let mut out = vec![0u8; second.len() + 64];
let decoded = ZSTD_decompressBlock(&mut dctx, &mut out, &second_body[..second_csize]);
assert!(
!crate::common::error::ERR_isError(decoded),
"decompressBlock failed: {}",
crate::common::error::ERR_getErrorName(decoded)
);
assert_eq!(decoded, second.len());
assert_eq!(&out[..decoded], &second);
assert_eq!(dctx.dictEnd, Some(inserted_end));
assert_eq!(dctx.prefixStart, Some(out.as_ptr() as usize));
assert_eq!(dctx.previousDstEnd, Some(out.as_ptr() as usize + decoded));
}
#[test]
fn initDStream_clears_configured_dict() {
let mut dctx = ZSTD_DCtx::default();
dctx.stream_dict = b"sticky-dict".to_vec();
dctx.dictID = 0x1234;
dctx.litEntropy = 1;
dctx.fseEntropy = 1;
dctx.ddict_rep = [3, 2, 1];
dctx.dictUses = ZSTD_dictUses_e::ZSTD_use_indefinitely;
ZSTD_initDStream(&mut dctx);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.dictID, 0);
assert_eq!(dctx.litEntropy, 0);
assert_eq!(dctx.fseEntropy, 0);
assert_eq!(dctx.ddict_rep, [1, 4, 8]);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
}
#[test]
fn initDStream_usingDict_empty_slice_clears_stale_dict() {
let mut dctx = ZSTD_DCtx::default();
assert_eq!(ZSTD_initDStream_usingDict(&mut dctx, b"stale-dict"), 5);
assert_eq!(dctx.stream_dict, b"stale-dict");
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
assert_eq!(ZSTD_initDStream_usingDict(&mut dctx, &[]), 5);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.dictID, 0);
assert_eq!(dctx.litEntropy, 0);
assert_eq!(dctx.fseEntropy, 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
}
#[test]
fn dParam_all_variants_set_get_roundtrip() {
let mut dctx = ZSTD_DCtx::default();
let cases = [
(ZSTD_dParameter::ZSTD_d_windowLogMax, 20),
(
ZSTD_dParameter::ZSTD_d_forceIgnoreChecksum,
crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_ignoreChecksum
as i32,
),
];
for &(param, value) in &cases {
ZSTD_DCtx_setParameter(&mut dctx, param, value);
let mut got = -1i32;
ZSTD_DCtx_getParameter(&dctx, param, &mut got);
assert_eq!(got, value, "param {:?} didn't round-trip", param);
}
}
#[test]
fn dctx_reset_modes_differ_correctly() {
let mut dctx = ZSTD_DCtx::default();
dctx.stream_dict = b"prior-dict".to_vec();
dctx.d_windowLogMax = 25;
dctx.d_maxWindowSize = 1u64 << 25;
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_session_only);
assert_eq!(dctx.stream_dict, b"prior-dict");
assert_eq!(dctx.d_windowLogMax, 25);
assert_eq!(dctx.d_maxWindowSize, 1u64 << 25);
ZSTD_DCtx_reset(&mut dctx, ZSTD_DResetDirective::ZSTD_reset_parameters);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.d_windowLogMax, ZSTD_WINDOWLOG_LIMIT_DEFAULT);
assert_eq!(dctx.d_maxWindowSize, ZSTD_maxWindowSize_default());
assert!(!dctx.d_maxWindowSizeSet);
dctx.stream_dict = b"seed-again".to_vec();
dctx.d_windowLogMax = 20;
dctx.d_maxWindowSize = 1u64 << 20;
ZSTD_DCtx_reset(
&mut dctx,
ZSTD_DResetDirective::ZSTD_reset_session_and_parameters,
);
assert!(dctx.stream_dict.is_empty());
assert_eq!(dctx.d_windowLogMax, ZSTD_WINDOWLOG_LIMIT_DEFAULT);
assert_eq!(dctx.d_maxWindowSize, ZSTD_maxWindowSize_default());
assert!(!dctx.d_maxWindowSizeSet);
}
#[test]
fn decompress_side_free_functions_accept_none_without_panic() {
assert_eq!(ZSTD_freeDStream(None), 0);
assert_eq!(crate::decompress::zstd_ddict::ZSTD_freeDDict(None), 0);
}
#[test]
fn decompressBegin_variants_seed_dict_consistently() {
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let dict = b"begin-usingDict-seed".to_vec();
let mut a = ZSTD_DCtx::default();
assert_eq!(ZSTD_decompressBegin_usingDict(&mut a, &dict), 0);
assert_eq!(a.stream_dict, dict);
let ddict = ZSTD_createDDict(&dict).expect("ddict");
let mut b = ZSTD_DCtx::default();
assert_eq!(ZSTD_decompressBegin_usingDDict(&mut b, &ddict), 0);
assert_eq!(b.stream_dict, dict);
let mut c = ZSTD_DCtx::default();
c.stream_dict = b"preloaded".to_vec();
assert_eq!(ZSTD_decompressBegin(&mut c), 0);
assert_eq!(c.stream_dict, b"preloaded");
}
#[test]
fn DCtx_refDDict_seeds_stream_dict_and_roundtrips_via_stream_api() {
use crate::compress::zstd_compress::{ZSTD_compress_usingDict, ZSTD_createCCtx};
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let dict = b"refDDict-test-dict ".repeat(8);
let ddict = ZSTD_createDDict(&dict).expect("ddict");
let mut dctx = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_refDDict(&mut dctx, &ddict);
assert_eq!(rc, 0);
assert_eq!(dctx.stream_dict, dict);
let src: Vec<u8> = b"payload wearing the refDDict-test-dict ".repeat(18);
let mut cctx = ZSTD_createCCtx().unwrap();
let mut cbuf = vec![0u8; 4096];
let n = ZSTD_compress_usingDict(&mut cctx, &mut cbuf, &src, &dict, 3);
assert!(!crate::common::error::ERR_isError(n));
ZSTD_initDStream(&mut dctx);
let rc = ZSTD_DCtx_refDDict(&mut dctx, &ddict);
assert_eq!(rc, 0);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let drain =
ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &cbuf[..n], &mut in_pos);
assert!(!crate::common::error::ERR_isError(drain));
assert_eq!(&out[..out_pos], &src[..]);
}
#[test]
fn DCtx_refPrefix_roundtrips_with_compressor_using_dict() {
use crate::compress::zstd_compress::{ZSTD_compress_usingDict, ZSTD_createCCtx};
let dict = b"shared-dict-content-for-prefix-test ".repeat(8);
let src = b"payload that uses shared-dict-content-for-prefix-test ".repeat(16);
let mut cctx = ZSTD_createCCtx().unwrap();
let mut dst = vec![0u8; 4096];
let n = ZSTD_compress_usingDict(&mut cctx, &mut dst, &src, &dict, 3);
assert!(!crate::common::error::ERR_isError(n));
let mut dctx = ZSTD_DCtx::default();
ZSTD_initDStream(&mut dctx);
let rc = ZSTD_DCtx_refPrefix(&mut dctx, &dict);
assert_eq!(rc, 0);
let mut out_buf = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let drain = ZSTD_decompressStream(
&mut dctx,
&mut out_buf,
&mut out_pos,
&dst[..n],
&mut in_pos,
);
assert!(
!crate::common::error::ERR_isError(drain),
"drain err: {drain:#x}"
);
assert_eq!(&out_buf[..out_pos], &src[..]);
}
#[test]
fn DCtx_loadDictionary_variants_store_equivalent_state() {
use crate::decompress::zstd_ddict::{ZSTD_dictContentType_e, ZSTD_dictLoadMethod_e};
let dict = b"variant-equivalence-test-dict".to_vec();
let mut a = ZSTD_DCtx::default();
ZSTD_DCtx_loadDictionary(&mut a, &dict);
let mut b = ZSTD_DCtx::default();
ZSTD_DCtx_loadDictionary_byReference(&mut b, &dict);
let mut c = ZSTD_DCtx::default();
ZSTD_DCtx_loadDictionary_advanced(
&mut c,
&dict,
ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
ZSTD_dictContentType_e::ZSTD_dct_auto,
);
assert_eq!(a.stream_dict, dict);
assert_eq!(b.stream_dict, dict);
assert_eq!(c.stream_dict, dict);
}
#[test]
fn decompressStream_handles_skippable_then_regular_frame() {
let src = b"streaming-skip-then-real ".repeat(20);
let mut frame = vec![0u8; 4096];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut frame, &src, 3);
assert!(!crate::common::error::ERR_isError(n));
let mut skip = Vec::new();
skip.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
skip.extend_from_slice(&8u32.to_le_bytes());
skip.extend_from_slice(b"SKIPDATA");
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, b"one-shot-prefix"), 0);
let mut out = vec![0u8; src.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let hint = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &skip, &mut in_pos);
assert_eq!(hint, 0);
assert_eq!(in_pos, skip.len());
assert_eq!(out_pos, 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
in_pos = 0;
let _hint =
ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &frame[..n], &mut in_pos);
for _ in 0..8 {
if out_pos >= src.len() {
break;
}
let _ = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &[], &mut 0usize);
}
assert_eq!(&out[..out_pos], &src[..]);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
}
#[test]
fn decompressStream_decodes_buffered_concatenated_frames_in_one_call() {
let first = b"streaming-first-frame ".repeat(16);
let second = b"streaming-second-frame ".repeat(16);
let mut f1 = vec![0u8; 4096];
let mut f2 = vec![0u8; 4096];
let n1 = crate::compress::zstd_compress::ZSTD_compress(&mut f1, &first, 3);
let n2 = crate::compress::zstd_compress::ZSTD_compress(&mut f2, &second, 3);
assert!(!crate::common::error::ERR_isError(n1));
assert!(!crate::common::error::ERR_isError(n2));
let mut stream = Vec::new();
stream.extend_from_slice(&f1[..n1]);
stream.extend_from_slice(&f2[..n2]);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = vec![0u8; first.len() + second.len() + 64];
let mut in_pos = 0usize;
let mut out_pos = 0usize;
let hint = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &stream, &mut in_pos);
assert!(
!crate::common::error::ERR_isError(hint),
"stream decode failed: {}",
crate::common::error::ERR_getErrorName(hint)
);
assert_eq!(hint, 0);
assert_eq!(in_pos, stream.len());
assert_eq!(out_pos, first.len() + second.len());
assert_eq!(&out[..first.len()], first.as_slice());
assert_eq!(&out[first.len()..out_pos], second.as_slice());
}
#[test]
fn decompressStream_staging_is_bounded_by_block_not_frame() {
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
use crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX;
let source = vec![b'q'; 8 * 1024 * 1024 + 123];
let mut frame = vec![0; ZSTD_compressBound(source.len())];
let frame_len = ZSTD_compress(&mut frame, &source, 3);
assert!(!crate::common::error::ERR_isError(frame_len));
frame.truncate(frame_len);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut input_pos = 0usize;
let mut decoded = Vec::with_capacity(source.len());
loop {
let mut chunk = [0; 997];
let mut output_pos = 0usize;
let hint = ZSTD_decompressStream(
&mut dctx,
&mut chunk,
&mut output_pos,
&frame,
&mut input_pos,
);
assert!(!crate::common::error::ERR_isError(hint));
decoded.extend_from_slice(&chunk[..output_pos]);
assert!(dctx.stream_out_buffer.capacity() <= ZSTD_BLOCKSIZE_MAX);
assert!(dctx.stream_in_buffer.capacity() <= ZSTD_BLOCKSIZE_MAX);
if hint == 0 && input_pos == frame.len() {
break;
}
}
assert_eq!(decoded, source);
}
#[test]
fn decompressDCtx_truncated_frame_errors_out_cleanly() {
use crate::decompress::zstd_decompress_block::{
ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let src = b"full-frame-that-we-then-truncate ".repeat(20);
let mut frame = vec![0u8; 4096];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut frame, &src, 3);
assert!(!crate::common::error::ERR_isError(n));
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = crate::common::xxhash::XXH64_state_t::default();
let mut out = vec![0u8; src.len() + 64];
let decoded = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &frame[..n / 2]);
assert!(
crate::common::error::ERR_isError(decoded),
"truncated frame should error, got decoded={}",
decoded,
);
}
#[test]
fn decompressDCtx_advances_past_skippable_frames_mid_stream() {
use crate::decompress::zstd_decompress_block::{
ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let first = b"alpha-first-frame".as_ref();
let second = b"omega-second-frame".as_ref();
let mut stream = Vec::new();
{
let mut buf = vec![0u8; 256];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut buf, first, 3);
stream.extend_from_slice(&buf[..n]);
}
stream.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
stream.extend_from_slice(&8u32.to_le_bytes());
stream.extend_from_slice(b"SKIPDATA");
{
let mut buf = vec![0u8; 256];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut buf, second, 3);
stream.extend_from_slice(&buf[..n]);
}
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = crate::common::xxhash::XXH64_state_t::default();
let mut out = vec![0u8; first.len() + second.len() + 64];
let decoded = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &stream);
assert!(!crate::common::error::ERR_isError(decoded));
assert_eq!(decoded, first.len() + second.len());
assert_eq!(&out[..first.len()], first);
assert_eq!(&out[first.len()..decoded], second);
}
#[test]
fn decompressDCtx_applies_loaded_stream_dict() {
let dict = b"shared-dict-for-decompressDCtx-apply ".repeat(8);
let src: Vec<u8> = b"payload referring to shared-dict-for-decompressDCtx-apply ".repeat(15);
let mut cctx = crate::compress::zstd_compress::ZSTD_createCCtx().unwrap();
let mut frame = vec![0u8; 4096];
let n = crate::compress::zstd_compress::ZSTD_compress_usingDict(
&mut cctx, &mut frame, &src, &dict, 3,
);
assert!(!crate::common::error::ERR_isError(n));
let mut dctx = ZSTD_DCtx::new();
ZSTD_DCtx_loadDictionary(&mut dctx, &dict);
use crate::decompress::zstd_decompress_block::{
ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = crate::common::xxhash::XXH64_state_t::default();
let mut out = vec![0u8; src.len() + 64];
let decoded = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &frame[..n]);
assert!(!crate::common::error::ERR_isError(decoded));
assert_eq!(&out[..decoded], &src[..]);
}
#[test]
fn prepared_dict_frame_decode_presents_raw_history_once() {
let dict = b"prepared-history-once-dictionary ".repeat(12);
let src: Vec<u8> = b"prepared-history-once-dictionary payload ".repeat(24);
let mut cctx = crate::compress::zstd_compress::ZSTD_createCCtx().unwrap();
let mut frame = vec![0u8; 8192];
let n = crate::compress::zstd_compress::ZSTD_compress_usingDict(
&mut cctx, &mut frame, &src, &dict, 6,
);
assert!(!crate::common::error::ERR_isError(n));
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_loadDictionary(&mut dctx, &dict), 0);
let prepared = ZSTD_takePreparedDictForFrame(&mut dctx).expect("prepared dict");
assert_eq!(prepared.content, dict);
let mut out = vec![0u8; src.len()];
let decoded =
ZSTD_decompressFrame_usingPreparedDict(&mut dctx, &mut out, &frame[..n], &prepared);
assert!(
!crate::common::error::ERR_isError(decoded),
"prepared-dict decode failed: {}",
crate::common::error::ERR_getErrorName(decoded)
);
assert_eq!(decoded, src.len());
assert_eq!(out, src);
assert_eq!(dctx.stream_dict, dict);
}
#[test]
fn getFrameHeader_on_skippable_frame_returns_variant_and_size() {
let mut buf = Vec::new();
buf.extend_from_slice(&(ZSTD_MAGIC_SKIPPABLE_START + 7).to_le_bytes());
buf.extend_from_slice(&12u32.to_le_bytes());
buf.extend_from_slice(&[0xAB; 12]);
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &buf);
assert_eq!(rc, 0);
assert_eq!(zfh.frameType, ZSTD_FrameType_e::ZSTD_skippableFrame);
assert_eq!(zfh.dictID, 7);
assert_eq!(zfh.headerSize, ZSTD_SKIPPABLEHEADERSIZE as u32);
assert_eq!(zfh.frameContentSize, 12);
}
#[test]
fn DCtx_loadDictionary_empty_slice_clears_previous_dict() {
let mut dctx = ZSTD_DCtx::default();
ZSTD_DCtx_loadDictionary(&mut dctx, b"sticky-dict");
assert_eq!(dctx.stream_dict, b"sticky-dict");
ZSTD_DCtx_loadDictionary(&mut dctx, &[]);
assert!(dctx.stream_dict.is_empty());
}
#[test]
fn decompress_usingDict_with_empty_dict_matches_no_dict_path() {
let src = b"empty-dict == no-dict ".repeat(20);
let mut cbuf = vec![0u8; 4096];
let n = crate::compress::zstd_compress::ZSTD_compress(&mut cbuf, &src, 3);
assert!(!crate::common::error::ERR_isError(n));
let mut dctx = ZSTD_DCtx::new();
let mut out = vec![0u8; src.len() + 64];
let rc = ZSTD_decompress_usingDict(&mut dctx, &mut out, &cbuf[..n], &[]);
assert!(!crate::common::error::ERR_isError(rc));
assert_eq!(&out[..rc], &src[..]);
}
#[test]
fn decompress_usingDict_rejects_dictID_mismatch() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::common::mem::MEM_writeLE32;
let mut dict_a =
include_bytes!("../../tests/fixtures/upstream-zstd/dict-files/zero-weight-dict")
.to_vec();
MEM_writeLE32(&mut dict_a[4..8], 0x11111111);
let mut dict_b = dict_a.clone();
MEM_writeLE32(&mut dict_b[4..8], 0x22222222);
let src = b"dict-A-vs-dict-B dictID mismatch ".repeat(20);
let mut cctx = crate::compress::zstd_compress::ZSTD_createCCtx().unwrap();
let mut frame = vec![0u8; 4096];
let n = crate::compress::zstd_compress::ZSTD_compress_usingDict(
&mut cctx, &mut frame, &src, &dict_a, 3,
);
assert!(!ERR_isError(n));
let mut dctx = ZSTD_DCtx::new();
let mut out = vec![0u8; src.len() + 64];
let rc = ZSTD_decompress_usingDict(&mut dctx, &mut out, &frame[..n], &dict_b);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::DictionaryWrong);
}
#[test]
fn decompress_usingDict_rejects_magicless_frame_dictID_with_raw_dict() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
let mut frame = Vec::new();
frame.push(0x01); frame.push(0x00); frame.push(7); frame.extend_from_slice(&[0x01, 0x00, 0x00]);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(
ZSTD_DCtx_setFormat(&mut dctx, ZSTD_format_e::ZSTD_f_zstd1_magicless),
0
);
let mut out = [0u8; 8];
let rc = ZSTD_decompress_usingDict(&mut dctx, &mut out, &frame, b"raw-dict-with-id-zero");
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::DictionaryWrong);
}
#[test]
fn decompress_usingDDict_rejects_dictID_mismatch() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::common::mem::MEM_writeLE32;
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let mut dict_a =
include_bytes!("../../tests/fixtures/upstream-zstd/dict-files/zero-weight-dict")
.to_vec();
MEM_writeLE32(&mut dict_a[4..8], 0x11111111);
let mut dict_b = dict_a.clone();
MEM_writeLE32(&mut dict_b[4..8], 0x22222222);
let src = b"dict-A-vs-ddict-B dictID mismatch ".repeat(20);
let mut cctx = crate::compress::zstd_compress::ZSTD_createCCtx().unwrap();
let mut frame = vec![0u8; 4096];
let n = crate::compress::zstd_compress::ZSTD_compress_usingDict(
&mut cctx, &mut frame, &src, &dict_a, 3,
);
assert!(!ERR_isError(n));
let ddict_b = ZSTD_createDDict(&dict_b).expect("ddict");
let mut dctx = ZSTD_DCtx::new();
let mut out = vec![0u8; src.len() + 64];
let rc = ZSTD_decompress_usingDDict(&mut dctx, &mut out, &frame[..n], &ddict_b);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::DictionaryWrong);
}
#[test]
fn decompress_usingDict_decodes_concatenated_frames_and_rejects_trailing_input() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::compress::zstd_compress::{
ZSTD_compressBound, ZSTD_compress_usingDict, ZSTD_createCCtx,
};
let dict = b"concat-usingDict-shared-history ".repeat(8);
let src1 = b"first payload using concat-usingDict-shared-history ".repeat(10);
let src2 = b"second payload using concat-usingDict-shared-history ".repeat(9);
let mut cctx = ZSTD_createCCtx().unwrap();
let mut f1 = vec![0u8; ZSTD_compressBound(src1.len())];
let n1 = ZSTD_compress_usingDict(&mut cctx, &mut f1, &src1, &dict, 3);
assert!(!ERR_isError(n1));
let mut f2 = vec![0u8; ZSTD_compressBound(src2.len())];
let n2 = ZSTD_compress_usingDict(&mut cctx, &mut f2, &src2, &dict, 3);
assert!(!ERR_isError(n2));
let mut concatenated = Vec::new();
concatenated.extend_from_slice(&f1[..n1]);
concatenated.extend_from_slice(&f2[..n2]);
let mut dctx = ZSTD_DCtx::new();
let mut out = vec![0u8; src1.len() + src2.len() + 32];
let decoded = ZSTD_decompress_usingDict(&mut dctx, &mut out, &concatenated, &dict);
assert!(!ERR_isError(decoded));
assert_eq!(decoded, src1.len() + src2.len());
assert_eq!(&out[..src1.len()], src1.as_slice());
assert_eq!(&out[src1.len()..decoded], src2.as_slice());
concatenated.extend_from_slice(b"trailing");
let rc = ZSTD_decompress_usingDict(&mut dctx, &mut out, &concatenated, &dict);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn prepared_dict_decoders_short_garbage_is_src_size_wrong() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let dict = b"short-garbage-dict".repeat(4);
let short_garbage = b"x";
let mut dctx = ZSTD_DCtx::new();
let mut out = [0u8; 16];
let rc = ZSTD_decompress_usingDict(&mut dctx, &mut out, short_garbage, &dict);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
let ddict = ZSTD_createDDict(&dict).expect("ddict");
let rc = ZSTD_decompress_usingDDict(&mut dctx, &mut out, short_garbage, &ddict);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn decompressDCtx_maps_late_prefix_unknown_to_srcSizeWrong() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep;
let mut src = make_raw_hello_frame();
src.extend_from_slice(b"trailing");
let mut dctx = ZSTD_DCtx::new();
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let mut out = vec![0u8; 64];
let rc = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &src);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn decompress_usingDDict_decodes_concatenated_frames_and_rejects_trailing_input() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
use crate::compress::zstd_compress::{
ZSTD_compressBound, ZSTD_compress_usingDict, ZSTD_createCCtx,
};
use crate::decompress::zstd_ddict::ZSTD_createDDict;
let dict = b"concat-usingDDict-shared-history ".repeat(8);
let ddict = ZSTD_createDDict(&dict).expect("ddict");
let src1 = b"first payload using concat-usingDDict-shared-history ".repeat(10);
let src2 = b"second payload using concat-usingDDict-shared-history ".repeat(9);
let mut cctx = ZSTD_createCCtx().unwrap();
let mut f1 = vec![0u8; ZSTD_compressBound(src1.len())];
let n1 = ZSTD_compress_usingDict(&mut cctx, &mut f1, &src1, &dict, 3);
assert!(!ERR_isError(n1));
let mut f2 = vec![0u8; ZSTD_compressBound(src2.len())];
let n2 = ZSTD_compress_usingDict(&mut cctx, &mut f2, &src2, &dict, 3);
assert!(!ERR_isError(n2));
let mut concatenated = Vec::new();
concatenated.extend_from_slice(&f1[..n1]);
concatenated.extend_from_slice(&f2[..n2]);
let mut dctx = ZSTD_DCtx::new();
let mut out = vec![0u8; src1.len() + src2.len() + 32];
let decoded = ZSTD_decompress_usingDDict(&mut dctx, &mut out, &concatenated, &ddict);
assert!(!ERR_isError(decoded));
assert_eq!(decoded, src1.len() + src2.len());
assert_eq!(&out[..src1.len()], src1.as_slice());
assert_eq!(&out[src1.len()..decoded], src2.as_slice());
concatenated.extend_from_slice(b"trailing");
let rc = ZSTD_decompress_usingDDict(&mut dctx, &mut out, &concatenated, &ddict);
assert!(ERR_isError(rc));
assert_eq!(ERR_getErrorCode(rc), ErrorCode::SrcSizeWrong);
}
#[test]
fn decompress_usingDict_uses_caller_owned_dctx() {
use crate::common::error::ERR_isError;
use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
let src_bytes = b"use-caller-dctx-for-decompress-usingDict-parity ".repeat(4);
let mut cctx = crate::compress::zstd_compress::ZSTD_createCCtx().unwrap();
let mut frame = vec![0u8; 4096];
let n = crate::compress::zstd_compress::ZSTD_compress_usingDict(
&mut cctx,
&mut frame,
&src_bytes,
&[],
3,
);
assert!(!ERR_isError(n));
let mut dctx = ZSTD_DCtx::new();
dctx.dictID = 0xDEAD_BEEF;
let mut out = vec![0u8; src_bytes.len() + 64];
let rc = ZSTD_decompress_usingDict(&mut dctx, &mut out, &frame[..n], &[]);
assert!(!ERR_isError(rc));
assert_eq!(&out[..rc], src_bytes.as_slice());
assert_eq!(dctx.dictID, 0);
assert_eq!(dctx.isFrameDecompression, 1);
assert!(dctx.blockSizeMax > 0);
}
#[test]
fn DCtx_loadDictionary_advanced_honors_dictContentType() {
use crate::decompress::zstd_ddict::ZSTD_dictContentType_e;
use crate::decompress::zstd_ddict::ZSTD_dictLoadMethod_e;
let content = b"advanced-dctx-full-dict-content ".repeat(4);
let dict = build_minimal_zstd_dict(0x1020_3040, &content);
let mut raw = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_loadDictionary_advanced(
&mut raw,
&dict,
ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
ZSTD_dictContentType_e::ZSTD_dct_rawContent,
);
assert_eq!(rc, 0);
assert_eq!(raw.dictID, 0);
assert_eq!(raw.stream_dict, dict);
assert_eq!(raw.litEntropy, 0);
assert_eq!(raw.fseEntropy, 0);
let mut full = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_loadDictionary_advanced(
&mut full,
&dict,
ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
ZSTD_dictContentType_e::ZSTD_dct_fullDict,
);
assert_eq!(rc, 0);
assert_eq!(full.dictID, 0x1020_3040);
assert_eq!(full.stream_dict, dict);
assert_eq!(full.litEntropy, 1);
assert_eq!(full.fseEntropy, 1);
assert_eq!(full.ddict_rep, [8, 7, 1]);
let mut reject_raw_as_full = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_loadDictionary_advanced(
&mut reject_raw_as_full,
b"not-a-full-dict",
ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
ZSTD_dictContentType_e::ZSTD_dct_fullDict,
);
assert!(crate::common::error::ERR_isError(rc));
assert!(reject_raw_as_full.stream_dict.is_empty());
}
#[test]
fn decompressDCtx_preserves_loaded_full_dict_entropy() {
use crate::common::error::ERR_isError;
use crate::common::xxhash::XXH64_state_t;
use crate::compress::zstd_compress::{ZSTD_compress, ZSTD_compressBound};
use crate::decompress::zstd_ddict::{
ZSTD_createDDict_advanced, ZSTD_dictContentType_e, ZSTD_dictLoadMethod_e,
};
use crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep;
let content = b"full-dict-entropy-for-decompressDCtx ".repeat(4);
let dict = build_minimal_zstd_dict(0x5566_7788, &content);
let src = b"ordinary frame decoded while full dict is attached ".repeat(4);
let mut frame = vec![0u8; ZSTD_compressBound(src.len())];
let n = ZSTD_compress(&mut frame, &src, 3);
assert!(!ERR_isError(n));
let mut loaded = ZSTD_DCtx::new();
let rc = ZSTD_DCtx_loadDictionary_advanced(
&mut loaded,
&dict,
ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
ZSTD_dictContentType_e::ZSTD_dct_fullDict,
);
assert_eq!(rc, 0);
let mut out = vec![0u8; src.len() + 32];
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let decoded = ZSTD_decompressDCtx(&mut loaded, &mut rep, &mut xxh, &mut out, &frame[..n]);
assert!(!ERR_isError(decoded));
assert_eq!(&out[..decoded], src.as_slice());
assert_eq!(loaded.dictID, 0x5566_7788);
assert_eq!(loaded.litEntropy, 1);
assert_eq!(loaded.fseEntropy, 1);
assert_eq!(loaded.ddict_rep, [8, 7, 1]);
assert_eq!(loaded.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
let ddict = ZSTD_createDDict_advanced(
&dict,
ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
ZSTD_dictContentType_e::ZSTD_dct_fullDict,
)
.expect("full ddict");
let mut refd = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_refDDict(&mut refd, &ddict), 0);
let decoded = ZSTD_decompressDCtx(&mut refd, &mut rep, &mut xxh, &mut out, &frame[..n]);
assert!(!ERR_isError(decoded));
assert_eq!(&out[..decoded], src.as_slice());
assert_eq!(refd.dictID, 0x5566_7788);
assert_eq!(refd.litEntropy, 1);
assert_eq!(refd.fseEntropy, 1);
assert_eq!(refd.ddict_rep, [8, 7, 1]);
assert_eq!(refd.dictUses, ZSTD_dictUses_e::ZSTD_use_indefinitely);
}
#[test]
fn decompressDCtx_refPrefix_use_once_applies_to_one_concatenated_frame() {
use crate::common::error::ERR_isError;
use crate::common::xxhash::XXH64_state_t;
use crate::compress::zstd_compress::{
ZSTD_compress, ZSTD_compressBound, ZSTD_compress_usingDict, ZSTD_createCCtx,
};
use crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep;
let dict = b"concat-use-once-prefix ".repeat(8);
let src1 = b"first frame uses concat-use-once-prefix ".repeat(10);
let src2 = b"second frame has no prefix dependency ".repeat(8);
let mut cctx = ZSTD_createCCtx().unwrap();
let mut f1 = vec![0u8; ZSTD_compressBound(src1.len())];
let n1 = ZSTD_compress_usingDict(&mut cctx, &mut f1, &src1, &dict, 3);
assert!(!ERR_isError(n1));
let mut f2 = vec![0u8; ZSTD_compressBound(src2.len())];
let n2 = ZSTD_compress(&mut f2, &src2, 3);
assert!(!ERR_isError(n2));
let mut concatenated = Vec::new();
concatenated.extend_from_slice(&f1[..n1]);
concatenated.extend_from_slice(&f2[..n2]);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, &dict), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
let mut out = vec![0u8; src1.len() + src2.len() + 32];
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let decoded = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &concatenated);
assert!(!ERR_isError(decoded));
assert_eq!(decoded, src1.len() + src2.len());
assert_eq!(&out[..src1.len()], src1.as_slice());
assert_eq!(&out[src1.len()..decoded], src2.as_slice());
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(dctx.stream_dict.is_empty());
}
#[test]
fn decompressDCtx_refPrefix_use_once_spans_entire_multiframe_call() {
use crate::common::error::ERR_isError;
use crate::common::xxhash::XXH64_state_t;
use crate::compress::zstd_compress::{
ZSTD_compressBound, ZSTD_compress_usingDict, ZSTD_createCCtx,
};
use crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep;
let dict = b"concat-use-once-prefix-shared-across-call ".repeat(8);
let src1 = b"first frame uses concat-use-once-prefix-shared-across-call ".repeat(9);
let src2 = b"second frame also uses concat-use-once-prefix-shared-across-call ".repeat(9);
let mut cctx = ZSTD_createCCtx().unwrap();
let mut f1 = vec![0u8; ZSTD_compressBound(src1.len())];
let n1 = ZSTD_compress_usingDict(&mut cctx, &mut f1, &src1, &dict, 3);
assert!(!ERR_isError(n1));
let mut f2 = vec![0u8; ZSTD_compressBound(src2.len())];
let n2 = ZSTD_compress_usingDict(&mut cctx, &mut f2, &src2, &dict, 3);
assert!(!ERR_isError(n2));
let mut concatenated = Vec::new();
concatenated.extend_from_slice(&f1[..n1]);
concatenated.extend_from_slice(&f2[..n2]);
let mut dctx = ZSTD_DCtx::new();
assert_eq!(ZSTD_DCtx_refPrefix(&mut dctx, &dict), 0);
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
let mut out = vec![0u8; src1.len() + src2.len() + 32];
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let decoded = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut out, &concatenated);
assert!(
!ERR_isError(decoded),
"multiframe refPrefix decode failed: {}",
crate::common::error::ERR_getErrorName(decoded)
);
assert_eq!(decoded, src1.len() + src2.len());
assert_eq!(&out[..src1.len()], src1.as_slice());
assert_eq!(&out[src1.len()..decoded], src2.as_slice());
assert_eq!(dctx.dictUses, ZSTD_dictUses_e::ZSTD_dont_use);
assert!(dctx.stream_dict.is_empty());
}
#[test]
fn DCtx_refPrefix_advanced_matches_plain_across_every_contentType() {
use crate::decompress::zstd_ddict::ZSTD_dictContentType_e;
let prefix = b"advanced-refPrefix-roundtrip".to_vec();
let mut plain = ZSTD_DCtx::default();
ZSTD_DCtx_refPrefix(&mut plain, &prefix);
let mut raw = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_refPrefix_advanced(
&mut raw,
&prefix,
ZSTD_dictContentType_e::ZSTD_dct_rawContent,
);
assert_eq!(rc, 0);
assert_eq!(raw.stream_dict, plain.stream_dict);
assert_eq!(raw.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
let mut auto = ZSTD_DCtx::default();
let rc =
ZSTD_DCtx_refPrefix_advanced(&mut auto, &prefix, ZSTD_dictContentType_e::ZSTD_dct_auto);
assert_eq!(rc, 0);
assert_eq!(auto.stream_dict, plain.stream_dict);
assert_eq!(auto.dictUses, ZSTD_dictUses_e::ZSTD_use_once);
let mut full = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_refPrefix_advanced(
&mut full,
&prefix,
ZSTD_dictContentType_e::ZSTD_dct_fullDict,
);
assert!(crate::common::error::ERR_isError(rc));
assert!(full.stream_dict.is_empty());
}
#[test]
fn initStatic_decompression_variants_construct_ctx_and_ddict() {
let mut buf = vec![0u64; (1 << 20) / core::mem::size_of::<u64>()];
let bytes = unsafe {
core::slice::from_raw_parts_mut(
buf.as_mut_ptr() as *mut u8,
buf.len() * core::mem::size_of::<u64>(),
)
};
let dctx = ZSTD_initStaticDCtx(bytes).expect("static dctx");
assert_eq!(
ZSTD_nextSrcSizeToDecompress(dctx),
ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1)
);
let dstream = ZSTD_initStaticDStream(bytes).expect("static dstream");
assert_eq!(
ZSTD_nextSrcSizeToDecompress(dstream),
ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1)
);
let dict_bytes = b"dict";
let ddict = ZSTD_initStaticDDict(bytes, dict_bytes).expect("static ddict");
assert_eq!(
crate::decompress::zstd_ddict::ZSTD_DDict_dictContent(ddict),
dict_bytes
);
}
#[test]
fn initStaticDDict_auto_rejects_malformed_magic_prefix_dict() {
let mut buf = vec![0u64; 128];
let bytes = unsafe {
core::slice::from_raw_parts_mut(
buf.as_mut_ptr() as *mut u8,
buf.len() * core::mem::size_of::<u64>(),
)
};
let mut dict = Vec::new();
dict.extend_from_slice(&ZSTD_MAGIC_DICTIONARY.to_le_bytes());
dict.extend_from_slice(&0x1234ABCDu32.to_le_bytes());
assert!(ZSTD_initStaticDDict(bytes, &dict).is_none());
}
#[test]
fn initStaticDDict_full_dict_replays_entropy_from_referenced_bytes() {
let mut buf = vec![0u64; 1024];
let bytes = unsafe {
core::slice::from_raw_parts_mut(
buf.as_mut_ptr() as *mut u8,
buf.len() * core::mem::size_of::<u64>(),
)
};
let content = b"initDStream_usingDDict-magic-dict-content ".repeat(2);
let dict = build_minimal_zstd_dict(0x1357_2468, &content);
let ddict = ZSTD_initStaticDDict(bytes, &dict).expect("static full ddict");
assert_eq!(
crate::decompress::zstd_ddict::ZSTD_DDict_dictContent(ddict),
dict.as_slice()
);
assert_eq!(
crate::decompress::zstd_ddict::ZSTD_DDict_rawContent(ddict),
content.as_slice()
);
assert_eq!(
crate::decompress::zstd_ddict::ZSTD_getDictID_fromDDict(ddict),
0x1357_2468
);
let mut dctx = ZSTD_DCtx::new();
crate::decompress::zstd_ddict::ZSTD_copyDDictParameters(&mut dctx, ddict);
assert_eq!(dctx.stream_dict, dict);
assert_eq!(dctx.dictID, 0x1357_2468);
assert_eq!(dctx.litEntropy, 1);
assert_eq!(dctx.fseEntropy, 1);
assert_eq!(dctx.ddict_rep, [8, 7, 1]);
}
#[test]
fn DCtx_setMaxWindowSize_stores_log2() {
let mut dctx = ZSTD_DCtx::default();
let rc = ZSTD_DCtx_setMaxWindowSize(&mut dctx, 1 << 18);
assert_eq!(rc, 0);
assert_eq!(dctx.d_windowLogMax, 18u32);
assert_eq!(dctx.d_maxWindowSize, 1u64 << 18);
}
#[test]
fn DCtx_setMaxWindowSize_rejects_out_of_bounds() {
let mut dctx = ZSTD_DCtx::default();
assert!(crate::common::error::ERR_isError(
ZSTD_DCtx_setMaxWindowSize(&mut dctx, 100)
));
if crate::common::mem::MEM_32bits() == 0 {
assert!(crate::common::error::ERR_isError(
ZSTD_DCtx_setMaxWindowSize(&mut dctx, 1usize << 32)
));
}
}
#[test]
fn isFrame_and_isSkippable_are_exclusive() {
let mut real = 0xFD2FB528u32.to_le_bytes().to_vec();
real.resize(32, 0);
assert_eq!(ZSTD_isFrame(&real), 1);
assert_eq!(ZSTD_isSkippableFrame(&real), 0);
let mut skip = ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes().to_vec();
skip.extend_from_slice(&0u32.to_le_bytes());
assert_eq!(ZSTD_isFrame(&skip), 1); assert_eq!(ZSTD_isSkippableFrame(&skip), 1);
let rand = [0u8, 1, 2, 3, 4, 5, 6, 7];
assert_eq!(ZSTD_isFrame(&rand), 0);
assert_eq!(ZSTD_isSkippableFrame(&rand), 0);
}
#[test]
fn skippableFrame_all_16_variants_roundtrip() {
use crate::compress::zstd_compress::ZSTD_writeSkippableFrame;
for variant in 0..16u32 {
let payload = [variant as u8; 7];
let mut buf = vec![0u8; 32];
let w = ZSTD_writeSkippableFrame(&mut buf, &payload, variant);
assert!(!crate::common::error::ERR_isError(w));
let mut out = [0u8; 7];
let mut got_variant = 0u32;
let r = ZSTD_readSkippableFrame(&mut out, Some(&mut got_variant), &buf);
assert_eq!(r, 7);
assert_eq!(got_variant, variant);
assert_eq!(&out, &payload);
}
}
#[test]
fn readSkippableFrame_returns_payload_and_variant() {
let mut src = Vec::new();
src.extend_from_slice(&(ZSTD_MAGIC_SKIPPABLE_START + 3).to_le_bytes()); src.extend_from_slice(&5u32.to_le_bytes());
src.extend_from_slice(b"hello");
let mut dst = [0u8; 16];
let mut variant: u32 = 0;
let n = ZSTD_readSkippableFrame(&mut dst, Some(&mut variant), &src);
assert_eq!(n, 5);
assert_eq!(&dst[..5], b"hello");
assert_eq!(variant, 3);
}
#[test]
fn writeSkippableFrame_rejects_error_paths() {
use crate::compress::zstd_compress::ZSTD_writeSkippableFrame;
let payload = b"abcd".as_slice();
let mut tiny = [0u8; 4];
assert!(crate::common::error::ERR_isError(ZSTD_writeSkippableFrame(
&mut tiny, payload, 0
)));
let mut no_room = [0u8; 11];
assert!(crate::common::error::ERR_isError(ZSTD_writeSkippableFrame(
&mut no_room,
payload,
0
)));
let mut ok_buf = [0u8; 64];
assert!(crate::common::error::ERR_isError(ZSTD_writeSkippableFrame(
&mut ok_buf,
payload,
16
)));
let mut exact = [0u8; 8 + 4];
let n = ZSTD_writeSkippableFrame(&mut exact, payload, 0);
assert_eq!(n, 12);
}
#[test]
fn readSkippableFrame_rejects_non_skippable() {
let src = 0xFD2FB528u32.to_le_bytes();
let mut dst = [0u8; 16];
let rc = ZSTD_readSkippableFrame(&mut dst, None, &src);
assert!(crate::common::error::ERR_isError(rc));
}
#[test]
fn readSkippableFrame_rejects_short_src_and_truncated_frame() {
let mut dst = [0u8; 32];
let too_short = [0u8; 4];
assert!(crate::common::error::ERR_isError(ZSTD_readSkippableFrame(
&mut dst, None, &too_short
)));
let mut truncated = Vec::new();
truncated.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
truncated.extend_from_slice(&20u32.to_le_bytes());
truncated.extend_from_slice(&[0u8; 5]);
assert!(crate::common::error::ERR_isError(ZSTD_readSkippableFrame(
&mut dst, None, &truncated
)));
}
#[test]
fn readSkippableFrame_rejects_small_dst() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&10u32.to_le_bytes());
src.extend_from_slice(&[0u8; 10]);
let mut dst = [0u8; 4]; let rc = ZSTD_readSkippableFrame(&mut dst, None, &src);
assert!(crate::common::error::ERR_isError(rc));
}
#[test]
fn find_frame_compressed_size_skippable() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&7u32.to_le_bytes());
src.extend_from_slice(&[0u8; 7]);
let sz = ZSTD_findFrameCompressedSize(&src);
assert_eq!(sz, ZSTD_SKIPPABLEHEADERSIZE + 7);
}
#[test]
fn find_decompressed_size_sums_frames() {
let mut src = make_raw_hello_frame();
src.extend_from_slice(&ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes());
src.extend_from_slice(&3u32.to_le_bytes());
src.extend_from_slice(&[0u8; 3]);
src.extend_from_slice(&make_raw_hello_frame());
let total = ZSTD_findDecompressedSize(&src);
assert_eq!(total, 10);
}
#[test]
fn decompress_frame_raw_block_roundtrip() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x20);
src.push(5);
let bh = (5u32 << 3) | 1; src.push((bh & 0xFF) as u8);
src.push(((bh >> 8) & 0xFF) as u8);
src.push(((bh >> 16) & 0xFF) as u8);
src.extend_from_slice(b"HELLO");
let mut dst = vec![0u8; 32];
let out = ZSTD_decompress(&mut dst, &src);
assert!(
!crate::common::error::ERR_isError(out),
"err: {}",
crate::common::error::ERR_getErrorName(out)
);
assert_eq!(out, 5);
assert_eq!(&dst[..5], b"HELLO");
}
#[test]
fn decompress_frame_rle_block_roundtrip() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x20); src.push(10); let bh = (10u32 << 3) | (1 << 1) | 1;
src.push((bh & 0xFF) as u8);
src.push(((bh >> 8) & 0xFF) as u8);
src.push(((bh >> 16) & 0xFF) as u8);
src.push(b'Z');
let mut dst = vec![0u8; 32];
let out = ZSTD_decompress(&mut dst, &src);
assert!(!crate::common::error::ERR_isError(out));
assert_eq!(out, 10);
for b in dst.iter().take(10) {
assert_eq!(*b, b'Z');
}
}
fn small_window_frame_with_block(block_header: u32, payload: &[u8]) -> Vec<u8> {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x00); src.push(0x00); src.push((block_header & 0xFF) as u8);
src.push(((block_header >> 8) & 0xFF) as u8);
src.push(((block_header >> 16) & 0xFF) as u8);
src.extend_from_slice(payload);
src
}
fn frame_with_1152_byte_window_and_empty_block() -> Vec<u8> {
frame_with_window_descriptor_and_empty_block(0x01)
}
fn frame_with_window_descriptor_and_empty_block(window_descriptor: u8) -> Vec<u8> {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x00); src.push(window_descriptor);
src.push(0x01); src.push(0x00);
src.push(0x00);
src
}
fn frame_with_window_log_and_empty_block(window_log: u8) -> Vec<u8> {
frame_with_window_descriptor_and_empty_block((window_log - 10) << 3)
}
fn assert_one_shot_frame_decoders_reject_oversized_block(src: &[u8]) {
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::{
ZSTD_DCtx, ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let mut dst = vec![0u8; 2048];
let mut consumed = 0usize;
let rc = ZSTD_decompressFrame(&mut dctx, &mut rep, &mut xxh, &mut dst, src, &mut consumed);
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(
crate::common::error::ERR_getErrorCode(rc),
ErrorCode::CorruptionDetected
);
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let mut dst = vec![0u8; 2056];
let mut consumed = 0usize;
let rc = ZSTD_decompressFrame_withOpStart(
&mut dctx,
&mut rep,
&mut xxh,
&mut dst,
8,
src,
&mut consumed,
);
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(
crate::common::error::ERR_getErrorCode(rc),
ErrorCode::CorruptionDetected
);
}
#[test]
fn one_shot_frame_decoders_reject_raw_block_larger_than_blockSizeMax() {
let raw = vec![b'A'; 1025];
let bh = (1025u32 << 3) | 1; let src = small_window_frame_with_block(bh, &raw);
assert_one_shot_frame_decoders_reject_oversized_block(&src);
}
#[test]
fn one_shot_frame_decoders_do_not_enforce_explicit_dctx_window_limit() {
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::{
ZSTD_DCtx, ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let src = frame_with_1152_byte_window_and_empty_block();
let mut dctx = ZSTD_DCtx::new();
dctx.d_windowLogMax = 10;
dctx.d_maxWindowSize = 1 << 10;
dctx.d_maxWindowSizeSet = true;
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let mut dst = vec![0u8; 16];
let mut consumed = 0usize;
let rc = ZSTD_decompressFrame(&mut dctx, &mut rep, &mut xxh, &mut dst, &src, &mut consumed);
assert_eq!(rc, 0);
assert_eq!(consumed, src.len());
let mut dctx = ZSTD_DCtx::new();
dctx.d_windowLogMax = 10;
dctx.d_maxWindowSize = 1 << 10;
dctx.d_maxWindowSizeSet = true;
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
let mut dst = vec![0u8; 24];
let mut consumed = 0usize;
let rc = ZSTD_decompressFrame_withOpStart(
&mut dctx,
&mut rep,
&mut xxh,
&mut dst,
8,
&src,
&mut consumed,
);
assert_eq!(rc, 0);
assert_eq!(consumed, src.len());
}
#[test]
fn public_one_shot_decoders_accept_window_above_default_cap() {
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::{
ZSTD_DCtx, ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let src = frame_with_window_log_and_empty_block((ZSTD_WINDOWLOG_LIMIT_DEFAULT + 1) as u8);
let mut dst = [];
assert_eq!(ZSTD_decompress(&mut dst, &src), 0);
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
assert_eq!(
ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut dst, &src),
0
);
}
#[test]
fn decompressDCtx_reseeds_repcodes_for_each_concatenated_frame() {
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::{
ZSTD_DCtx, ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let frame = frame_with_1152_byte_window_and_empty_block();
let mut concatenated = frame.clone();
concatenated.extend_from_slice(&frame);
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep { rep: [33, 44, 55] };
let mut xxh = XXH64_state_t::default();
let mut dst = [];
let decoded = ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, &mut dst, &concatenated);
assert_eq!(decoded, 0);
assert_eq!(rep.rep, [1, 4, 8]);
assert_eq!(dctx.ddict_rep, [1, 4, 8]);
}
#[test]
fn streaming_decoder_still_rejects_window_above_default_cap() {
use crate::common::error::{ERR_getErrorCode, ERR_isError};
let src = frame_with_window_log_and_empty_block((ZSTD_WINDOWLOG_LIMIT_DEFAULT + 1) as u8);
let mut dctx = ZSTD_DCtx::new();
ZSTD_initDStream(&mut dctx);
let mut out = [0u8; 1];
let mut out_pos = 0usize;
let mut in_pos = 0usize;
let rc = ZSTD_decompressStream(&mut dctx, &mut out, &mut out_pos, &src, &mut in_pos);
assert!(ERR_isError(rc));
assert_eq!(
ERR_getErrorCode(rc),
ErrorCode::FrameParameterWindowTooLarge
);
}
#[test]
fn one_shot_frame_decoders_reject_rle_block_larger_than_blockSizeMax() {
let bh = (1025u32 << 3) | (1 << 1) | 1; let src = small_window_frame_with_block(bh, &[b'R']);
assert_one_shot_frame_decoders_reject_oversized_block(&src);
}
#[test]
fn frame_header_reserved_bit_errors() {
let mut src = Vec::new();
src.extend_from_slice(&ZSTD_MAGICNUMBER.to_le_bytes());
src.push(0x08); src.push(0);
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, &src);
assert!(crate::common::error::ERR_isError(rc));
}
}
pub use crate::decompress::zstd_decompress_block::ZSTD_DCtx;
pub fn ZSTD_createDCtx() -> Box<ZSTD_DCtx> {
let mut d = ZSTD_createDCtx_internal(crate::compress::zstd_compress::ZSTD_customMem::default())
.expect("default customMem allocation must succeed");
crate::decompress::zstd_decompress_block::ZSTD_buildDefaultSeqTables(&mut d);
d
}
pub fn ZSTD_freeDCtx(dctx: Box<ZSTD_DCtx>) -> usize {
let customMem = dctx.customMem;
unsafe {
crate::compress::zstd_compress::ZSTD_customFreeBox(dctx, customMem);
}
0
}
pub fn ZSTD_copyRawBlock(dst: &mut [u8], src: &[u8]) -> usize {
if src.len() > dst.len() {
return ERROR(ErrorCode::DstSizeTooSmall);
}
dst[..src.len()].copy_from_slice(src);
src.len()
}
pub fn ZSTD_setRleBlock(dst: &mut [u8], b: u8, regenSize: usize) -> usize {
if regenSize > dst.len() {
return ERROR(ErrorCode::DstSizeTooSmall);
}
for d in dst[..regenSize].iter_mut() {
*d = b;
}
regenSize
}
#[allow(clippy::too_many_arguments)]
pub fn ZSTD_decompressFrame_withOpStart(
dctx: &mut crate::decompress::zstd_decompress_block::ZSTD_DCtx,
entropy_rep: &mut crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep,
xxh: &mut crate::common::xxhash::XXH64_state_t,
dst: &mut [u8],
op_start: usize,
src: &[u8],
src_consumed: &mut usize,
) -> usize {
use crate::common::xxhash::{XXH64_digest, XXH64_reset, XXH64_update};
use crate::decompress::zstd_decompress_block::{
blockProperties_t, blockType_e, streaming_operation, ZSTD_blockHeaderSize,
ZSTD_decompressBlock_internal, ZSTD_getcBlockSize,
};
let frameheadersize_min = ZSTD_FRAMEHEADERSIZE_MIN(dctx.format);
let mut ip: usize = 0;
let mut op: usize = op_start;
let mut remaining = src.len();
if remaining < frameheadersize_min + ZSTD_blockHeaderSize {
return ERROR(ErrorCode::SrcSizeWrong);
}
let headerSize = ZSTD_frameHeaderSize_internal(src, dctx.format);
if crate::common::error::ERR_isError(headerSize) {
return headerSize;
}
if headerSize > src.len() {
return ERROR(ErrorCode::SrcSizeWrong);
}
let saved_window_cap = dctx.d_maxWindowSize;
if !dctx.d_maxWindowSizeSet {
dctx.d_maxWindowSize = u64::MAX;
}
let rc = ZSTD_decodeFrameHeader(dctx, src, headerSize);
if !dctx.d_maxWindowSizeSet {
dctx.d_maxWindowSize = saved_window_cap;
}
if crate::common::error::ERR_isError(rc) {
return rc;
}
let zfh = dctx.fParams;
if zfh.frameType != ZSTD_FrameType_e::ZSTD_frame {
return ERROR(ErrorCode::PrefixUnknown);
}
let validateChecksum = zfh.checksumFlag != 0
&& dctx.forceIgnoreChecksum
== crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_validateChecksum;
if validateChecksum {
XXH64_reset(xxh, 0);
}
if dctx.isFrameDecompression != 0 {
entropy_rep.rep = dctx.ddict_rep;
}
ip += zfh.headerSize as usize;
remaining -= zfh.headerSize as usize;
loop {
let mut bp = blockProperties_t {
blockType: blockType_e::bt_raw,
lastBlock: 0,
origSize: 0,
};
let cBlockSize = ZSTD_getcBlockSize(&src[ip..], &mut bp);
if crate::common::error::ERR_isError(cBlockSize) {
return cBlockSize;
}
ip += ZSTD_blockHeaderSize;
remaining -= ZSTD_blockHeaderSize;
if cBlockSize > remaining {
return ERROR(ErrorCode::SrcSizeWrong);
}
if bp.blockType == blockType_e::bt_raw && cBlockSize > zfh.blockSizeMax as usize {
return ERROR(ErrorCode::CorruptionDetected);
}
if bp.blockType == blockType_e::bt_rle && bp.origSize > zfh.blockSizeMax {
return ERROR(ErrorCode::CorruptionDetected);
}
let decodedSize = match bp.blockType {
blockType_e::bt_compressed => ZSTD_decompressBlock_internal(
dctx,
entropy_rep,
dst,
op,
&src[ip..ip + cBlockSize],
streaming_operation::not_streaming,
),
blockType_e::bt_raw => ZSTD_copyRawBlock(&mut dst[op..], &src[ip..ip + cBlockSize]),
blockType_e::bt_rle => ZSTD_setRleBlock(&mut dst[op..], src[ip], bp.origSize as usize),
blockType_e::bt_reserved => return ERROR(ErrorCode::CorruptionDetected),
};
if crate::common::error::ERR_isError(decodedSize) {
return decodedSize;
}
if validateChecksum && decodedSize > 0 {
XXH64_update(xxh, &dst[op..op + decodedSize]);
}
op += decodedSize;
ip += cBlockSize;
remaining -= cBlockSize;
if bp.lastBlock != 0 {
break;
}
}
let decoded = op - op_start;
if zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN && (decoded as u64) != zfh.frameContentSize
{
return ERROR(ErrorCode::CorruptionDetected);
}
if zfh.checksumFlag != 0 {
if remaining < 4 {
return ERROR(ErrorCode::ChecksumWrong);
}
if validateChecksum {
let calc = XXH64_digest(xxh) as u32;
let read = MEM_readLE32(&src[ip..ip + 4]);
if calc != read {
return ERROR(ErrorCode::ChecksumWrong);
}
}
ip += 4;
}
*src_consumed = ip;
decoded
}
pub fn ZSTD_decompressFrame(
dctx: &mut crate::decompress::zstd_decompress_block::ZSTD_DCtx,
entropy_rep: &mut crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep,
xxh: &mut crate::common::xxhash::XXH64_state_t,
dst: &mut [u8],
src: &[u8],
src_consumed: &mut usize,
) -> usize {
use crate::common::xxhash::{XXH64_digest, XXH64_reset, XXH64_update};
use crate::decompress::zstd_decompress_block::{
blockProperties_t, blockType_e, streaming_operation, ZSTD_blockHeaderSize,
ZSTD_decompressBlock_internal, ZSTD_getcBlockSize,
};
let mut ip: usize = 0;
let mut op: usize = 0;
let mut remaining = src.len();
let frameHeaderSizeMin = ZSTD_FRAMEHEADERSIZE_MIN(dctx.format);
if remaining < frameHeaderSizeMin + ZSTD_blockHeaderSize {
return ERROR(ErrorCode::SrcSizeWrong);
}
let headerSize = ZSTD_frameHeaderSize_internal(src, dctx.format);
if crate::common::error::ERR_isError(headerSize) {
return headerSize;
}
if headerSize > src.len() {
return ERROR(ErrorCode::SrcSizeWrong);
}
let saved_window_cap = dctx.d_maxWindowSize;
if !dctx.d_maxWindowSizeSet {
dctx.d_maxWindowSize = u64::MAX;
}
let rc = ZSTD_decodeFrameHeader(dctx, src, headerSize);
if !dctx.d_maxWindowSizeSet {
dctx.d_maxWindowSize = saved_window_cap;
}
if crate::common::error::ERR_isError(rc) {
return rc;
}
let zfh = dctx.fParams;
if zfh.frameType != ZSTD_FrameType_e::ZSTD_frame {
return ERROR(ErrorCode::PrefixUnknown);
}
let validateChecksum = zfh.checksumFlag != 0
&& dctx.forceIgnoreChecksum
== crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_validateChecksum;
if validateChecksum {
XXH64_reset(xxh, 0);
}
if dctx.isFrameDecompression != 0 {
entropy_rep.rep = dctx.ddict_rep;
}
ip += zfh.headerSize as usize;
remaining -= zfh.headerSize as usize;
loop {
let mut bp = blockProperties_t {
blockType: blockType_e::bt_raw,
lastBlock: 0,
origSize: 0,
};
let cBlockSize = ZSTD_getcBlockSize(&src[ip..], &mut bp);
if crate::common::error::ERR_isError(cBlockSize) {
return cBlockSize;
}
ip += ZSTD_blockHeaderSize;
remaining -= ZSTD_blockHeaderSize;
if cBlockSize > remaining {
return ERROR(ErrorCode::SrcSizeWrong);
}
if bp.blockType == blockType_e::bt_raw && cBlockSize > zfh.blockSizeMax as usize {
return ERROR(ErrorCode::CorruptionDetected);
}
if bp.blockType == blockType_e::bt_rle && bp.origSize > zfh.blockSizeMax {
return ERROR(ErrorCode::CorruptionDetected);
}
let decodedSize = match bp.blockType {
blockType_e::bt_compressed => ZSTD_decompressBlock_internal(
dctx,
entropy_rep,
dst,
op,
&src[ip..ip + cBlockSize],
streaming_operation::not_streaming,
),
blockType_e::bt_raw => ZSTD_copyRawBlock(&mut dst[op..], &src[ip..ip + cBlockSize]),
blockType_e::bt_rle => ZSTD_setRleBlock(&mut dst[op..], src[ip], bp.origSize as usize),
blockType_e::bt_reserved => {
return ERROR(ErrorCode::CorruptionDetected);
}
};
if crate::common::error::ERR_isError(decodedSize) {
return decodedSize;
}
if validateChecksum && decodedSize > 0 {
XXH64_update(xxh, &dst[op..op + decodedSize]);
}
op += decodedSize;
ip += cBlockSize;
remaining -= cBlockSize;
if bp.lastBlock != 0 {
break;
}
}
if zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN && (op as u64) != zfh.frameContentSize {
return ERROR(ErrorCode::CorruptionDetected);
}
if zfh.checksumFlag != 0 {
if remaining < 4 {
return ERROR(ErrorCode::ChecksumWrong);
}
if validateChecksum {
let calc = XXH64_digest(xxh) as u32;
let read = MEM_readLE32(&src[ip..ip + 4]);
if calc != read {
return ERROR(ErrorCode::ChecksumWrong);
}
}
ip += 4;
}
*src_consumed = ip;
op
}
pub fn ZSTD_decompressDCtx(
dctx: &mut crate::decompress::zstd_decompress_block::ZSTD_DCtx,
entropy_rep: &mut crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep,
xxh: &mut crate::common::xxhash::XXH64_state_t,
dst: &mut [u8],
src: &[u8],
) -> usize {
let clear_dict_after_call = dctx.dictUses == ZSTD_dictUses_e::ZSTD_use_once;
let prepared_dict = ZSTD_takePreparedDictForFrame(dctx);
let mut ip = 0usize;
let mut op = 0usize;
while ip < src.len() {
let rem = &src[ip..];
if dctx.format == ZSTD_format_e::ZSTD_f_zstd1
&& rem.len() >= ZSTD_SKIPPABLEHEADERSIZE
&& (MEM_readLE32(&rem[..4]) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START
{
let sz = readSkippableFrameSize(rem);
if crate::common::error::ERR_isError(sz) {
return sz;
}
ip += sz;
continue;
}
if let Some(dict) = prepared_dict.as_ref() {
let frame_sz = ZSTD_findFrameCompressedSize_advanced(rem, dctx.format);
if crate::common::error::ERR_isError(frame_sz) {
if clear_dict_after_call {
ZSTD_clearDict(dctx);
}
return ZSTD_mapPrefixUnknownAfterDecodedFrame(frame_sz, op);
}
let decoded = ZSTD_decompressFrame_usingPreparedDict(
dctx,
&mut dst[op..],
&rem[..frame_sz],
dict,
);
if crate::common::error::ERR_isError(decoded) {
if clear_dict_after_call {
ZSTD_clearDict(dctx);
}
return ZSTD_mapPrefixUnknownAfterDecodedFrame(decoded, op);
}
ip += frame_sz;
op += decoded;
continue;
}
let mut consumed = 0usize;
let rc = ZSTD_decompressBegin(dctx);
if crate::common::error::ERR_isError(rc) {
return rc;
}
let decoded =
ZSTD_decompressFrame(dctx, entropy_rep, xxh, &mut dst[op..], rem, &mut consumed);
if crate::common::error::ERR_isError(decoded) {
return ZSTD_mapPrefixUnknownAfterDecodedFrame(decoded, op);
}
ip += consumed;
op += decoded;
}
if clear_dict_after_call {
ZSTD_clearDict(dctx);
}
op
}
pub fn ZSTD_decompress(dst: &mut [u8], src: &[u8]) -> usize {
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::{
ZSTD_DCtx, ZSTD_buildDefaultSeqTables, ZSTD_decoder_entropy_rep,
};
let mut dctx = ZSTD_DCtx::new();
ZSTD_buildDefaultSeqTables(&mut dctx);
let mut rep = ZSTD_decoder_entropy_rep::default();
let mut xxh = XXH64_state_t::default();
ZSTD_decompressDCtx(&mut dctx, &mut rep, &mut xxh, dst, src)
}
pub fn ZSTD_decompress_usingDict(
dctx: &mut ZSTD_DCtx,
dst: &mut [u8],
src: &[u8],
dict: &[u8],
) -> usize {
let rc = ZSTD_DCtx_loadDictionary_advanced_internal(
dctx,
dict,
crate::decompress::zstd_ddict::ZSTD_dictContentType_e::ZSTD_dct_auto,
ZSTD_dictUses_e::ZSTD_use_once,
);
if crate::common::error::ERR_isError(rc) {
return rc;
}
let prepared = ZSTD_takePreparedDictForFrame(dctx).unwrap_or_else(|| ZSTD_preparedDict {
content: Vec::new(),
dictID: 0,
entropyPresent: false,
rep: [1, 4, 8],
entropy: None,
});
ZSTD_decompressMultiFrame_preparedDict(dctx, dst, src, &prepared)
}
#[derive(Clone)]
struct ZSTD_preparedDict {
content: Vec<u8>,
dictID: u32,
entropyPresent: bool,
rep: [u32; 3],
entropy: Option<ZSTD_preparedDictEntropy>,
}
#[derive(Clone)]
struct ZSTD_preparedDictEntropy {
hufTable: Vec<u32>,
LLTable: Vec<crate::decompress::zstd_decompress_block::ZSTD_seqSymbol>,
OFTable: Vec<crate::decompress::zstd_decompress_block::ZSTD_seqSymbol>,
MLTable: Vec<crate::decompress::zstd_decompress_block::ZSTD_seqSymbol>,
rep: [u32; 3],
}
fn ZSTD_preparedDictEntropy_parse(dict: &[u8]) -> Result<ZSTD_preparedDictEntropy, usize> {
let mut tmp = ZSTD_DCtx::new();
crate::decompress::zstd_decompress_block::ZSTD_buildDefaultSeqTables(&mut tmp);
let mut rep = [0u32; 3];
let rc = ZSTD_loadDEntropy(&mut tmp, &mut rep, dict);
if crate::common::error::ERR_isError(rc) {
return Err(ERROR(ErrorCode::DictionaryCorrupted));
}
Ok(ZSTD_preparedDictEntropy {
hufTable: tmp.hufTable,
LLTable: tmp.LLTable,
OFTable: tmp.OFTable,
MLTable: tmp.MLTable,
rep,
})
}
fn ZSTD_preparedDict_fromDDict(
ddict: &crate::decompress::zstd_ddict::ZSTD_DDict,
) -> ZSTD_preparedDict {
let entropyPresent = crate::decompress::zstd_ddict::ZSTD_DDict_entropyPresent(ddict) != 0;
let mut rep = [1, 4, 8];
let mut entropy = None;
if entropyPresent {
if let Ok(parsed) = ZSTD_preparedDictEntropy_parse(
crate::decompress::zstd_ddict::ZSTD_DDict_originalContent(ddict),
) {
rep = parsed.rep;
entropy = Some(parsed);
}
}
ZSTD_preparedDict {
content: crate::decompress::zstd_ddict::ZSTD_DDict_dictContent(ddict).to_vec(),
dictID: crate::decompress::zstd_ddict::ZSTD_getDictID_fromDDict(ddict),
entropyPresent,
rep,
entropy,
}
}
fn ZSTD_takePreparedDictForFrame(dctx: &mut ZSTD_DCtx) -> Option<ZSTD_preparedDict> {
let has_prepared = !dctx.stream_dict.is_empty()
|| dctx.dictID != 0
|| dctx.litEntropy != 0
|| dctx.fseEntropy != 0;
let prepared = if !has_prepared {
None
} else {
Some(ZSTD_preparedDict {
content: dctx.stream_dict.clone(),
dictID: dctx.dictID,
entropyPresent: dctx.litEntropy != 0 || dctx.fseEntropy != 0,
rep: dctx.ddict_rep,
entropy: if dctx.litEntropy != 0 || dctx.fseEntropy != 0 {
Some(ZSTD_preparedDictEntropy {
hufTable: dctx.hufTable.clone(),
LLTable: dctx.LLTable.clone(),
OFTable: dctx.OFTable.clone(),
MLTable: dctx.MLTable.clone(),
rep: dctx.ddict_rep,
})
} else {
None
},
})
};
match dctx.dictUses {
ZSTD_dictUses_e::ZSTD_dont_use => {
ZSTD_clearDict(dctx);
None
}
ZSTD_dictUses_e::ZSTD_use_indefinitely => prepared,
ZSTD_dictUses_e::ZSTD_use_once => {
ZSTD_clearDict(dctx);
prepared
}
}
}
fn ZSTD_decompressFrame_usingPreparedDict(
dctx: &mut ZSTD_DCtx,
dst: &mut [u8],
src: &[u8],
dict: &ZSTD_preparedDict,
) -> usize {
use crate::common::error::ERR_isError;
use crate::common::xxhash::XXH64_state_t;
use crate::decompress::zstd_decompress_block::ZSTD_decoder_entropy_rep;
let mut zfh = ZSTD_FrameHeader::default();
let header_rc = ZSTD_getFrameHeader_advanced(&mut zfh, src, dctx.format);
if ERR_isError(header_rc) {
return header_rc;
}
if header_rc != 0 {
return ERROR(ErrorCode::SrcSizeWrong);
}
if zfh.frameType != ZSTD_FrameType_e::ZSTD_frame {
return ERROR(ErrorCode::PrefixUnknown);
}
if zfh.dictID != 0 && zfh.dictID != dict.dictID {
return ERROR(ErrorCode::DictionaryWrong);
}
let declared = zfh.frameContentSize;
let out_size = if declared == ZSTD_CONTENTSIZE_UNKNOWN {
dst.len()
} else {
declared as usize
};
if out_size > dst.len() {
return ERROR(ErrorCode::DstSizeTooSmall);
}
let rc = ZSTD_decompressBegin(dctx);
if ERR_isError(rc) {
return rc;
}
dctx.stream_dict = dict.content.clone();
let dict_start = dctx.stream_dict.as_ptr() as usize;
let dict_end = dict_start.wrapping_add(dctx.stream_dict.len());
dctx.prefixStart = Some(dict_start);
dctx.virtualStart = Some(dict_start);
dctx.dictEnd = Some(dict_end);
dctx.previousDstEnd = Some(dict_end);
dctx.dictID = dict.dictID;
if let Some(entropy) = dict.entropy.as_ref() {
dctx.hufTable = entropy.hufTable.clone();
dctx.LLTable = entropy.LLTable.clone();
dctx.OFTable = entropy.OFTable.clone();
dctx.MLTable = entropy.MLTable.clone();
dctx.ddict_rep = entropy.rep;
dctx.litEntropy = 1;
dctx.fseEntropy = 1;
dctx.ll_default_active = false;
dctx.of_default_active = false;
dctx.ml_default_active = false;
} else if dict.entropyPresent {
dctx.ddict_rep = dict.rep;
dctx.litEntropy = 1;
dctx.fseEntropy = 1;
}
let mut rep = ZSTD_decoder_entropy_rep {
rep: dctx.ddict_rep,
};
let mut xxh = XXH64_state_t::default();
let mut consumed = 0usize;
let decoded = ZSTD_decompressFrame_withOpStart(
dctx,
&mut rep,
&mut xxh,
&mut dst[..out_size],
0,
src,
&mut consumed,
);
if ERR_isError(decoded) {
return decoded;
}
decoded
}
#[inline]
fn ZSTD_mapPrefixUnknownAfterDecodedFrame(err: usize, decoded_so_far: usize) -> usize {
if decoded_so_far > 0
&& crate::common::error::ERR_isError(err)
&& crate::common::error::ERR_getErrorCode(err) == ErrorCode::PrefixUnknown
{
ERROR(ErrorCode::SrcSizeWrong)
} else {
err
}
}
fn ZSTD_decompressMultiFrame_preparedDict(
dctx: &mut ZSTD_DCtx,
dst: &mut [u8],
src: &[u8],
dict: &ZSTD_preparedDict,
) -> usize {
let mut ip = 0usize;
let mut op = 0usize;
while src.len() - ip >= ZSTD_startingInputLength(dctx.format) {
let rem = &src[ip..];
if dctx.format == ZSTD_format_e::ZSTD_f_zstd1
&& rem.len() >= ZSTD_SKIPPABLEHEADERSIZE
&& (MEM_readLE32(&rem[..4]) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START
{
let sz = readSkippableFrameSize(rem);
if crate::common::error::ERR_isError(sz) {
return sz;
}
ip += sz;
continue;
}
let frame_sz = ZSTD_findFrameCompressedSize_advanced(rem, dctx.format);
if crate::common::error::ERR_isError(frame_sz) {
return ZSTD_mapPrefixUnknownAfterDecodedFrame(frame_sz, op);
}
let decoded =
ZSTD_decompressFrame_usingPreparedDict(dctx, &mut dst[op..], &rem[..frame_sz], dict);
if crate::common::error::ERR_isError(decoded) {
return ZSTD_mapPrefixUnknownAfterDecodedFrame(decoded, op);
}
ip += frame_sz;
op += decoded;
}
if ip != src.len() {
return ERROR(ErrorCode::SrcSizeWrong);
}
op
}
pub fn ZSTD_dParam_getBounds(
param: ZSTD_dParameter,
) -> crate::compress::zstd_compress::ZSTD_bounds {
match param {
ZSTD_dParameter::ZSTD_d_windowLogMax => {
let upper = if crate::common::mem::MEM_32bits() != 0 {
ZSTD_WINDOWLOG_MAX_32 as i32
} else {
ZSTD_WINDOWLOG_MAX_64 as i32
};
crate::compress::zstd_compress::ZSTD_bounds {
error: 0,
lowerBound: 10,
upperBound: upper,
}
}
ZSTD_dParameter::ZSTD_d_format => crate::compress::zstd_compress::ZSTD_bounds {
error: 0,
lowerBound: ZSTD_format_e::ZSTD_f_zstd1 as i32,
upperBound: ZSTD_format_e::ZSTD_f_zstd1_magicless as i32,
},
ZSTD_dParameter::ZSTD_d_forceIgnoreChecksum => {
crate::compress::zstd_compress::ZSTD_bounds {
error: 0,
lowerBound:
crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_validateChecksum
as i32,
upperBound:
crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_ignoreChecksum
as i32,
}
}
}
}
#[inline]
pub fn ZSTD_DCtx_trace_end(
_dctx: &ZSTD_DCtx,
_uncompressedSize: u64,
_compressedSize: u64,
_streaming: i32,
) {
}
pub fn ZSTD_clearDict(dctx: &mut ZSTD_DCtx) {
dctx.stream_dict.clear();
dctx.stream_dict_source.clear();
dctx.dictID = 0;
dctx.ddict_rep = crate::common::zstd_internal::repStartValue;
dctx.litEntropy = 0;
dctx.fseEntropy = 0;
dctx.dictUses = ZSTD_dictUses_e::ZSTD_dont_use;
}
pub const DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT: usize = 4;
pub const DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT: usize = 3;
pub const DDICT_HASHSET_TABLE_BASE_SIZE: usize = 64;
pub const DDICT_HASHSET_RESIZE_FACTOR: usize = 2;
pub struct ZSTD_DDictHashSet<'a> {
pub ddictPtrTable: Vec<Option<&'a crate::decompress::zstd_ddict::ZSTD_DDict>>,
pub ddictPtrTableSize: usize,
pub ddictPtrCount: usize,
}
pub fn ZSTD_DDictHashSet_getIndex(hashSet: &ZSTD_DDictHashSet<'_>, dictID: u32) -> usize {
let hash = crate::common::xxhash::XXH64(&dictID.to_le_bytes(), 0);
(hash as usize) & (hashSet.ddictPtrTableSize - 1)
}
pub fn ZSTD_DDictHashSet_emplaceDDict<'a>(
hashSet: &mut ZSTD_DDictHashSet<'a>,
ddict: &'a crate::decompress::zstd_ddict::ZSTD_DDict,
) -> usize {
use crate::decompress::zstd_ddict::ZSTD_getDictID_fromDDict;
let dictID = ZSTD_getDictID_fromDDict(ddict);
let mut idx = ZSTD_DDictHashSet_getIndex(hashSet, dictID);
let idxRangeMask = hashSet.ddictPtrTableSize - 1;
if hashSet.ddictPtrCount == hashSet.ddictPtrTableSize {
return ERROR(ErrorCode::Generic);
}
while let Some(existing) = hashSet.ddictPtrTable[idx] {
if ZSTD_getDictID_fromDDict(existing) == dictID {
hashSet.ddictPtrTable[idx] = Some(ddict);
return 0;
}
idx = (idx + 1) & idxRangeMask;
}
hashSet.ddictPtrTable[idx] = Some(ddict);
hashSet.ddictPtrCount += 1;
0
}
pub fn ZSTD_DDictHashSet_expand<'a>(
hashSet: &mut ZSTD_DDictHashSet<'a>,
_customMem: crate::compress::zstd_compress::ZSTD_customMem,
) -> usize {
let oldTable = core::mem::take(&mut hashSet.ddictPtrTable);
hashSet.ddictPtrTableSize *= DDICT_HASHSET_RESIZE_FACTOR;
hashSet.ddictPtrTable = vec![None; hashSet.ddictPtrTableSize];
hashSet.ddictPtrCount = 0;
for ddict in oldTable.into_iter().flatten() {
let rc = ZSTD_DDictHashSet_emplaceDDict(hashSet, ddict);
if crate::common::error::ERR_isError(rc) {
return rc;
}
}
0
}
pub fn ZSTD_DDictHashSet_getDDict<'a>(
hashSet: &ZSTD_DDictHashSet<'a>,
dictID: u32,
) -> Option<&'a crate::decompress::zstd_ddict::ZSTD_DDict> {
use crate::decompress::zstd_ddict::ZSTD_getDictID_fromDDict;
let mut idx = ZSTD_DDictHashSet_getIndex(hashSet, dictID);
let idxRangeMask = hashSet.ddictPtrTableSize - 1;
loop {
match hashSet.ddictPtrTable[idx] {
Some(ddict) if ZSTD_getDictID_fromDDict(ddict) == dictID => return Some(ddict),
Some(_) => idx = (idx + 1) & idxRangeMask,
None => return None,
}
}
}
pub fn ZSTD_createDDictHashSet<'a>(
_customMem: crate::compress::zstd_compress::ZSTD_customMem,
) -> ZSTD_DDictHashSet<'a> {
ZSTD_DDictHashSet {
ddictPtrTable: vec![None; DDICT_HASHSET_TABLE_BASE_SIZE],
ddictPtrTableSize: DDICT_HASHSET_TABLE_BASE_SIZE,
ddictPtrCount: 0,
}
}
pub fn ZSTD_DDictHashSet_addDDict<'a>(
hashSet: &mut ZSTD_DDictHashSet<'a>,
ddict: &'a crate::decompress::zstd_ddict::ZSTD_DDict,
customMem: crate::compress::zstd_compress::ZSTD_customMem,
) -> usize {
if hashSet.ddictPtrCount * DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT / hashSet.ddictPtrTableSize
* DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT
!= 0
{
let rc = ZSTD_DDictHashSet_expand(hashSet, customMem);
if crate::common::error::ERR_isError(rc) {
return rc;
}
}
ZSTD_DDictHashSet_emplaceDDict(hashSet, ddict)
}
pub fn ZSTD_DCtx_selectFrameDDict<'a>(
dctx: &mut ZSTD_DCtx,
hashSet: &ZSTD_DDictHashSet<'a>,
) -> Option<&'a crate::decompress::zstd_ddict::ZSTD_DDict> {
let has_active_ddict = dctx.dictUses != ZSTD_dictUses_e::ZSTD_dont_use
&& (!dctx.stream_dict.is_empty()
|| dctx.dictID != 0
|| dctx.litEntropy != 0
|| dctx.fseEntropy != 0);
if !has_active_ddict {
return None;
}
let frameDDict = ZSTD_DDictHashSet_getDDict(hashSet, dctx.fParams.dictID)?;
ZSTD_clearDict(dctx);
dctx.dictID = dctx.fParams.dictID;
crate::decompress::zstd_ddict::ZSTD_copyDDictParameters(dctx, frameDDict);
dctx.dictUses = ZSTD_dictUses_e::ZSTD_use_indefinitely;
Some(frameDDict)
}
pub fn ZSTD_getDDict(dctx: &mut ZSTD_DCtx) -> Option<Vec<u8>> {
match dctx.dictUses {
ZSTD_dictUses_e::ZSTD_dont_use => {
ZSTD_clearDict(dctx);
None
}
ZSTD_dictUses_e::ZSTD_use_indefinitely => {
if dctx.stream_dict.is_empty() {
None
} else {
Some(dctx.stream_dict.clone())
}
}
ZSTD_dictUses_e::ZSTD_use_once => {
let dict = if dctx.stream_dict.is_empty() {
None
} else {
Some(dctx.stream_dict.clone())
};
ZSTD_clearDict(dctx);
dict
}
}
}
pub fn ZSTD_createDCtx_internal(
customMem: crate::compress::zstd_compress::ZSTD_customMem,
) -> Option<Box<ZSTD_DCtx>> {
let mut dctx = unsafe {
crate::compress::zstd_compress::ZSTD_customAllocBox(ZSTD_DCtx::new(), customMem)?
};
dctx.customMem = customMem;
ZSTD_initDCtx_internal(&mut dctx);
Some(dctx)
}
pub fn ZSTD_initDCtx_internal(dctx: &mut ZSTD_DCtx) {
dctx.isFrameDecompression = 1;
ZSTD_clearDict(dctx);
ZSTD_DCtx_resetParameters(dctx);
}
pub fn ZSTD_DCtx_resetParameters(dctx: &mut ZSTD_DCtx) {
dctx.d_windowLogMax = ZSTD_WINDOWLOG_LIMIT_DEFAULT;
dctx.d_maxWindowSize = ZSTD_maxWindowSize_default();
dctx.d_maxWindowSizeSet = false;
dctx.format = ZSTD_format_e::ZSTD_f_zstd1;
dctx.dictUses = ZSTD_dictUses_e::ZSTD_dont_use;
dctx.forceIgnoreChecksum =
crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_validateChecksum;
}
pub fn ZSTD_dParam_withinBounds(dParam: ZSTD_dParameter, value: i32) -> i32 {
let bounds = ZSTD_dParam_getBounds(dParam);
if crate::common::error::ERR_isError(bounds.error) {
return 0;
}
if value < bounds.lowerBound || value > bounds.upperBound {
return 0;
}
1
}
pub fn ZSTD_DCtx_setMaxWindowSize(dctx: &mut ZSTD_DCtx, maxWindowSize: usize) -> usize {
use crate::common::error::{ErrorCode, ERROR};
if !dctx_is_in_init_stage(dctx) {
return ERROR(ErrorCode::StageWrong);
}
let bounds = ZSTD_dParam_getBounds(ZSTD_dParameter::ZSTD_d_windowLogMax);
let min = 1usize << bounds.lowerBound;
let max = 1usize << bounds.upperBound;
if maxWindowSize < min || maxWindowSize > max {
return ERROR(ErrorCode::ParameterOutOfBound);
}
dctx.d_maxWindowSize = maxWindowSize as u64;
dctx.d_windowLogMax = ((maxWindowSize.leading_zeros() as i32 ^ (usize::BITS as i32 - 1))
as u32)
.max(bounds.lowerBound as u32);
dctx.d_maxWindowSizeSet = true;
0
}
pub fn ZSTD_DCtx_setFormat(
dctx: &mut ZSTD_DCtx,
format: crate::decompress::zstd_decompress::ZSTD_format_e,
) -> usize {
ZSTD_DCtx_setParameter(dctx, ZSTD_dParameter::ZSTD_d_format, format as i32)
}
pub type ZSTD_DStream = ZSTD_DCtx;
pub fn ZSTD_createDStream() -> Option<Box<ZSTD_DStream>> {
Some(ZSTD_createDCtx())
}
pub fn ZSTD_freeDStream(zds: Option<Box<ZSTD_DStream>>) -> usize {
if let Some(zds) = zds {
return ZSTD_freeDCtx(zds);
}
0
}
#[inline]
fn dctx_is_in_init_stage(dctx: &ZSTD_DCtx) -> bool {
dctx.stream_in_buffer.is_empty()
&& dctx.stream_out_buffer.is_empty()
&& dctx.stream_out_drained == 0
&& !dctx.stream_active
}
fn ZSTD_DCtx_resetSession(dctx: &mut ZSTD_DCtx) {
dctx.stream_in_buffer.clear();
dctx.stream_out_buffer.clear();
dctx.stream_out_drained = 0;
dctx.stream_active = false;
dctx.stream_regular_frame_started = false;
dctx.oversizedDuration = 0;
dctx.isFrameDecompression = 1;
dctx.expected = ZSTD_startingInputLength(dctx.format);
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
dctx.processedCSize = 0;
dctx.decodedSize = 0;
dctx.previousDstEnd = None;
dctx.prefixStart = None;
dctx.virtualStart = None;
dctx.dictEnd = None;
dctx.bType = crate::decompress::zstd_decompress_block::blockType_e::bt_reserved;
dctx.rleSize = 0;
dctx.headerSize = 0;
dctx.validateChecksum = 0;
dctx.fParams = ZSTD_FrameHeader::default();
dctx.headerBuffer.fill(0);
dctx.historyBuffer.clear();
crate::common::xxhash::XXH64_reset(&mut dctx.xxhState, 0);
}
pub fn ZSTD_DCtx_loadDictionary(dctx: &mut ZSTD_DCtx, dict: &[u8]) -> usize {
ZSTD_DCtx_loadDictionary_advanced_internal(
dctx,
dict,
crate::decompress::zstd_ddict::ZSTD_dictContentType_e::ZSTD_dct_auto,
ZSTD_dictUses_e::ZSTD_use_indefinitely,
)
}
pub fn ZSTD_DCtx_refPrefix(dctx: &mut ZSTD_DCtx, prefix: &[u8]) -> usize {
if !dctx_is_in_init_stage(dctx) {
return ERROR(ErrorCode::StageWrong);
}
ZSTD_clearDict(dctx);
if !prefix.is_empty() {
dctx.stream_dict = prefix.to_vec();
dctx.stream_dict_source = prefix.to_vec();
dctx.dictUses = ZSTD_dictUses_e::ZSTD_use_once;
}
0
}
pub fn ZSTD_DCtx_refPrefix_advanced(
dctx: &mut ZSTD_DCtx,
prefix: &[u8],
dictContentType: crate::decompress::zstd_ddict::ZSTD_dictContentType_e,
) -> usize {
if dictContentType == crate::decompress::zstd_ddict::ZSTD_dictContentType_e::ZSTD_dct_rawContent
{
return ZSTD_DCtx_refPrefix(dctx, prefix);
}
ZSTD_DCtx_loadDictionary_advanced_internal(
dctx,
prefix,
dictContentType,
ZSTD_dictUses_e::ZSTD_use_once,
)
}
pub fn ZSTD_DCtx_loadDictionary_advanced(
dctx: &mut ZSTD_DCtx,
dict: &[u8],
_dictLoadMethod: crate::decompress::zstd_ddict::ZSTD_dictLoadMethod_e,
dictContentType: crate::decompress::zstd_ddict::ZSTD_dictContentType_e,
) -> usize {
ZSTD_DCtx_loadDictionary_advanced_internal(
dctx,
dict,
dictContentType,
ZSTD_dictUses_e::ZSTD_use_indefinitely,
)
}
fn ZSTD_DCtx_loadDictionary_advanced_internal(
dctx: &mut ZSTD_DCtx,
dict: &[u8],
dictContentType: crate::decompress::zstd_ddict::ZSTD_dictContentType_e,
dictUses: ZSTD_dictUses_e,
) -> usize {
if !dctx_is_in_init_stage(dctx) {
return ERROR(ErrorCode::StageWrong);
}
ZSTD_clearDict(dctx);
if dict.is_empty() {
return 0;
}
if dictContentType == crate::decompress::zstd_ddict::ZSTD_dictContentType_e::ZSTD_dct_rawContent
{
let rc = ZSTD_refDictContent(dctx, dict);
if crate::common::error::ERR_isError(rc) {
return rc;
}
dctx.dictID = 0;
dctx.dictUses = dictUses;
return 0;
}
let ddict = match crate::decompress::zstd_ddict::ZSTD_createDDict_advanced(
dict,
crate::decompress::zstd_ddict::ZSTD_dictLoadMethod_e::ZSTD_dlm_byCopy,
dictContentType,
) {
Some(ddict) => ddict,
None => return ERROR(ErrorCode::DictionaryCorrupted),
};
crate::decompress::zstd_ddict::ZSTD_copyDDictParameters(dctx, &ddict);
dctx.dictUses = dictUses;
0
}
#[inline]
pub fn ZSTD_DCtx_loadDictionary_byReference(dctx: &mut ZSTD_DCtx, dict: &[u8]) -> usize {
ZSTD_DCtx_loadDictionary(dctx, dict)
}
pub fn ZSTD_DCtx_refDDict(
dctx: &mut ZSTD_DCtx,
ddict: &crate::decompress::zstd_ddict::ZSTD_DDict,
) -> usize {
if !dctx_is_in_init_stage(dctx) {
return ERROR(ErrorCode::StageWrong);
}
ZSTD_clearDict(dctx);
if crate::decompress::zstd_ddict::ZSTD_DDict_rawContent(ddict).is_empty()
&& crate::decompress::zstd_ddict::ZSTD_DDict_entropyPresent(ddict) == 0
{
return 0;
}
crate::decompress::zstd_ddict::ZSTD_copyDDictParameters(dctx, ddict);
dctx.dictUses = ZSTD_dictUses_e::ZSTD_use_indefinitely;
0
}
pub fn ZSTD_DStreamInSize() -> usize {
use crate::decompress::zstd_decompress_block::{ZSTD_blockHeaderSize, ZSTD_BLOCKSIZE_MAX};
ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize
}
pub fn ZSTD_DStreamOutSize() -> usize {
crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum ZSTD_dParameter {
ZSTD_d_windowLogMax = 100,
ZSTD_d_format = 1000,
ZSTD_d_forceIgnoreChecksum = 1002,
}
pub fn ZSTD_DCtx_setParameter(dctx: &mut ZSTD_DCtx, param: ZSTD_dParameter, value: i32) -> usize {
use crate::common::error::{ErrorCode, ERROR};
if !dctx_is_in_init_stage(dctx) {
return ERROR(ErrorCode::StageWrong);
}
match param {
ZSTD_dParameter::ZSTD_d_windowLogMax => {
let effective = if value == 0 {
ZSTD_WINDOWLOG_LIMIT_DEFAULT as i32
} else {
value
};
let bounds = ZSTD_dParam_getBounds(ZSTD_dParameter::ZSTD_d_windowLogMax);
if effective < bounds.lowerBound || effective > bounds.upperBound {
return ERROR(ErrorCode::ParameterOutOfBound);
}
dctx.d_windowLogMax = effective as u32;
dctx.d_maxWindowSize = 1u64 << (effective as u32);
dctx.d_maxWindowSizeSet = true;
0
}
ZSTD_dParameter::ZSTD_d_format => {
let bounds = ZSTD_dParam_getBounds(ZSTD_dParameter::ZSTD_d_format);
if value < bounds.lowerBound || value > bounds.upperBound {
return ERROR(ErrorCode::ParameterOutOfBound);
}
dctx.format = match value {
v if v == ZSTD_format_e::ZSTD_f_zstd1_magicless as i32 => {
ZSTD_format_e::ZSTD_f_zstd1_magicless
}
_ => ZSTD_format_e::ZSTD_f_zstd1,
};
dctx.expected = ZSTD_startingInputLength(dctx.format);
0
}
ZSTD_dParameter::ZSTD_d_forceIgnoreChecksum => {
let bounds = ZSTD_dParam_getBounds(ZSTD_dParameter::ZSTD_d_forceIgnoreChecksum);
if value < bounds.lowerBound || value > bounds.upperBound {
return ERROR(ErrorCode::ParameterOutOfBound);
}
dctx.forceIgnoreChecksum = if value
== crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_ignoreChecksum
as i32
{
crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_ignoreChecksum
} else {
crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_validateChecksum
};
0
}
}
}
pub const ZSTD_WINDOWLOG_LIMIT_DEFAULT: u32 = 27;
pub fn ZSTD_DCtx_getParameter(dctx: &ZSTD_DCtx, param: ZSTD_dParameter, value: &mut i32) -> usize {
*value = match param {
ZSTD_dParameter::ZSTD_d_windowLogMax => ZSTD_highbit64(dctx.d_maxWindowSize),
ZSTD_dParameter::ZSTD_d_format => dctx.format as i32,
ZSTD_dParameter::ZSTD_d_forceIgnoreChecksum => dctx.forceIgnoreChecksum as i32,
};
0
}
pub fn ZSTD_DCtx_reset(dctx: &mut ZSTD_DCtx, reset: ZSTD_DResetDirective) -> usize {
let clear_session = matches!(
reset,
ZSTD_DResetDirective::ZSTD_reset_session_only
| ZSTD_DResetDirective::ZSTD_reset_session_and_parameters,
);
let clear_params = matches!(
reset,
ZSTD_DResetDirective::ZSTD_reset_parameters
| ZSTD_DResetDirective::ZSTD_reset_session_and_parameters,
);
if clear_params && !clear_session && !dctx_is_in_init_stage(dctx) {
return ERROR(ErrorCode::StageWrong);
}
if clear_session {
ZSTD_DCtx_resetSession(dctx);
}
if clear_params {
ZSTD_clearDict(dctx);
ZSTD_DCtx_resetParameters(dctx);
}
0
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum ZSTD_DResetDirective {
ZSTD_reset_session_only = 1,
ZSTD_reset_parameters = 2,
ZSTD_reset_session_and_parameters = 3,
}
pub fn ZSTD_sizeof_DCtx(dctx: &ZSTD_DCtx) -> usize {
core::mem::size_of::<ZSTD_DCtx>()
+ dctx.hufTable.capacity() * core::mem::size_of::<u32>()
+ dctx.workspace.capacity() * core::mem::size_of::<u32>()
+ dctx.litExtraBuffer.capacity()
+ dctx.LLTable.capacity()
* core::mem::size_of::<crate::decompress::zstd_decompress_block::ZSTD_seqSymbol>()
+ dctx.OFTable.capacity()
* core::mem::size_of::<crate::decompress::zstd_decompress_block::ZSTD_seqSymbol>()
+ dctx.MLTable.capacity()
* core::mem::size_of::<crate::decompress::zstd_decompress_block::ZSTD_seqSymbol>()
+ dctx.stream_in_buffer.capacity()
+ dctx.stream_out_buffer.capacity()
+ dctx.stream_dict.capacity()
}
pub fn ZSTD_sizeof_DStream(zds: &ZSTD_DStream) -> usize {
ZSTD_sizeof_DCtx(zds)
}
pub fn ZSTD_estimateDCtxSize() -> usize {
core::mem::size_of::<ZSTD_DCtx>()
}
pub fn ZSTD_estimateDStreamSize(windowSize: usize) -> usize {
use crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX;
let blockSize = windowSize.min(ZSTD_BLOCKSIZE_MAX);
let inBuffSize = blockSize;
let outBuffSize = ZSTD_decodingBufferSize_min(windowSize as u64, ZSTD_CONTENTSIZE_UNKNOWN);
ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize
}
pub fn ZSTD_estimateDStreamSize_fromFrame(src: &[u8]) -> usize {
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, src);
if crate::common::error::ERR_isError(rc) {
return rc;
}
if rc != 0 {
return ERROR(ErrorCode::SrcSizeWrong);
}
let windowSizeMax = if crate::common::mem::MEM_32bits() != 0 {
1u64 << ZSTD_WINDOWLOG_MAX_32
} else {
1u64 << ZSTD_WINDOWLOG_MAX_64
};
if zfh.windowSize > windowSizeMax {
return ERROR(ErrorCode::FrameParameterWindowTooLarge);
}
ZSTD_estimateDStreamSize(zfh.windowSize as usize)
}
pub fn ZSTD_insertBlock(dctx: &mut ZSTD_DCtx, block: &[u8]) -> usize {
crate::decompress::zstd_decompress_block::ZSTD_checkContinuity(dctx, block, block.len());
dctx.previousDstEnd = Some(block.as_ptr() as usize + block.len());
block.len()
}
pub fn ZSTD_decompressBlock(dctx: &mut ZSTD_DCtx, dst: &mut [u8], src: &[u8]) -> usize {
use crate::decompress::zstd_decompress_block::{
streaming_operation, ZSTD_checkContinuity, ZSTD_decoder_entropy_rep,
ZSTD_decompressBlock_internal,
};
dctx.isFrameDecompression = 0;
if dctx.ddict_rep == [0; 3] {
dctx.ddict_rep = [1, 4, 8];
}
ZSTD_checkContinuity(dctx, dst, dst.len());
let mut entropy_rep = ZSTD_decoder_entropy_rep {
rep: dctx.ddict_rep,
};
let decoded = ZSTD_decompressBlock_internal(
dctx,
&mut entropy_rep,
dst,
0,
src,
streaming_operation::not_streaming,
);
if !crate::common::error::ERR_isError(decoded) {
dctx.ddict_rep = entropy_rep.rep;
dctx.previousDstEnd = Some(dst.as_ptr() as usize + decoded);
}
decoded
}
pub fn ZSTD_decompressBlock_deprecated(dctx: &mut ZSTD_DCtx, dst: &mut [u8], src: &[u8]) -> usize {
ZSTD_decompressBlock(dctx, dst, src)
}
pub fn ZSTD_getBlockSize(_dctx: &ZSTD_DCtx) -> usize {
crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX
}
pub fn ZSTD_isFrame(src: &[u8]) -> u32 {
if src.len() < ZSTD_FRAMEIDSIZE {
return 0;
}
let magic = MEM_readLE32(&src[..4]);
if magic == ZSTD_MAGICNUMBER {
return 1;
}
if (magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START {
return 1;
}
0
}
pub fn ZSTD_isSkippableFrame(src: &[u8]) -> u32 {
if src.len() < ZSTD_FRAMEIDSIZE {
return 0;
}
let magic = MEM_readLE32(&src[..4]);
if (magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START {
return 1;
}
0
}
pub fn ZSTD_decodeFrameHeader(dctx: &mut ZSTD_DCtx, src: &[u8], headerSize: usize) -> usize {
use crate::common::xxhash::XXH64_reset;
if src.len() < headerSize {
return ERROR(ErrorCode::SrcSizeWrong);
}
let result = ZSTD_getFrameHeader_advanced(&mut dctx.fParams, &src[..headerSize], dctx.format);
if crate::common::error::ERR_isError(result) {
return result;
}
if result != 0 {
return ERROR(ErrorCode::SrcSizeWrong);
}
if dctx.fParams.dictID != 0 && dctx.dictID != dctx.fParams.dictID {
return ERROR(ErrorCode::DictionaryWrong);
}
dctx.isFrameDecompression = 1;
dctx.blockSizeMax = dctx.fParams.blockSizeMax as usize;
dctx.validateChecksum = if dctx.fParams.checksumFlag != 0
&& dctx.forceIgnoreChecksum
== crate::compress::zstd_compress::ZSTD_forceIgnoreChecksum_e::ZSTD_d_validateChecksum
{
1
} else {
0
};
if dctx.validateChecksum != 0 {
XXH64_reset(&mut dctx.xxhState, 0);
}
dctx.processedCSize = dctx.processedCSize.wrapping_add(headerSize as u64);
0
}
pub fn ZSTD_readSkippableFrame(
dst: &mut [u8],
magicVariant: Option<&mut u32>,
src: &[u8],
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
if src.len() < ZSTD_SKIPPABLEHEADERSIZE {
return ERROR(ErrorCode::SrcSizeWrong);
}
let magicNumber = MEM_readLE32(&src[..4]);
let skippableFrameSize = readSkippableFrameSize(src);
if ZSTD_isSkippableFrame(src) == 0 {
return ERROR(ErrorCode::FrameParameterUnsupported);
}
if skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > src.len() {
return ERROR(ErrorCode::SrcSizeWrong);
}
let skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE;
if skippableContentSize > dst.len() {
return ERROR(ErrorCode::DstSizeTooSmall);
}
if skippableContentSize > 0 {
dst[..skippableContentSize].copy_from_slice(
&src[ZSTD_SKIPPABLEHEADERSIZE..ZSTD_SKIPPABLEHEADERSIZE + skippableContentSize],
);
}
if let Some(v) = magicVariant {
*v = magicNumber - ZSTD_MAGIC_SKIPPABLE_START;
}
skippableContentSize
}
pub fn ZSTD_getDictID_fromFrame(src: &[u8]) -> u32 {
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, src);
if crate::common::error::ERR_isError(rc) || rc != 0 {
return 0;
}
zfh.dictID
}
pub fn ZSTD_decompress_usingDDict(
dctx: &mut ZSTD_DCtx,
dst: &mut [u8],
src: &[u8],
ddict: &crate::decompress::zstd_ddict::ZSTD_DDict,
) -> usize {
let prepared = ZSTD_preparedDict_fromDDict(ddict);
ZSTD_decompressMultiFrame_preparedDict(dctx, dst, src, &prepared)
}
pub fn ZSTD_decompress_insertDictionary(dctx: &mut ZSTD_DCtx, dict: &[u8]) -> usize {
use crate::common::error::{ERR_isError, ErrorCode, ERROR};
use crate::common::mem::MEM_readLE32;
dctx.stream_dict_source = dict.to_vec();
if dict.len() < 8 {
dctx.dictID = 0;
return ZSTD_refDictContent(dctx, dict);
}
let magic = MEM_readLE32(&dict[..4]);
if magic != ZSTD_MAGICNUMBER_DICTIONARY {
dctx.dictID = 0;
return ZSTD_refDictContent(dctx, dict);
}
dctx.dictID = MEM_readLE32(&dict[4..8]);
let mut rep = [0u32; 3];
let eSize = ZSTD_loadDEntropy(dctx, &mut rep, dict);
if ERR_isError(eSize) {
return ERROR(ErrorCode::DictionaryCorrupted);
}
dctx.ddict_rep = rep;
dctx.litEntropy = 1;
dctx.fseEntropy = 1;
ZSTD_refDictContent(dctx, &dict[eSize..])
}
pub fn ZSTD_refDictContent(dctx: &mut ZSTD_DCtx, dict: &[u8]) -> usize {
let dict_start = dict.as_ptr() as usize;
let dict_end = dict_start + dict.len();
let previous_dst_end = dctx.previousDstEnd.unwrap_or(0);
let prefix_start = dctx.prefixStart.unwrap_or(previous_dst_end);
dctx.dictEnd = dctx.previousDstEnd;
dctx.virtualStart = Some(dict_start.wrapping_sub(previous_dst_end.wrapping_sub(prefix_start)));
dctx.prefixStart = Some(dict_start);
dctx.previousDstEnd = Some(dict_end);
dctx.stream_dict = dict.to_vec();
0
}
pub const ZSTD_MAGICNUMBER_DICTIONARY: u32 = 0xEC30A437;
pub fn ZSTD_loadDEntropy(dctx: &mut ZSTD_DCtx, repOut: &mut [u32; 3], dict: &[u8]) -> usize {
use crate::common::error::{ERR_isError, ErrorCode, ERROR};
use crate::common::mem::MEM_readLE32;
use crate::decompress::huf_decompress::HUF_readDTableX2;
use crate::decompress::zstd_decompress_block::{
LLFSELog, LL_base, LL_bits, MLFSELog, ML_base, ML_bits, MaxLL, MaxML, MaxOff, OF_base,
OF_bits, OffFSELog, ZSTD_buildFSETable,
};
if dict.len() <= 8 {
return ERROR(ErrorCode::DictionaryCorrupted);
}
let mut pos = 8usize;
let mut hufWorkspace = vec![0u32; 1024];
let hSize = HUF_readDTableX2(&mut dctx.hufTable, &dict[pos..], &mut hufWorkspace, 0);
if ERR_isError(hSize) {
return ERROR(ErrorCode::DictionaryCorrupted);
}
pos += hSize;
let mut ofcNCount = [0i16; (MaxOff + 1) as usize];
let mut ofcMaxValue: u32 = MaxOff;
let mut ofcLog: u32 = 0;
let ofcSize = crate::common::entropy_common::FSE_readNCount(
&mut ofcNCount,
&mut ofcMaxValue,
&mut ofcLog,
&dict[pos..],
);
if ERR_isError(ofcSize) || ofcMaxValue > MaxOff || ofcLog > OffFSELog {
return ERROR(ErrorCode::DictionaryCorrupted);
}
ZSTD_buildFSETable(
&mut dctx.OFTable,
&ofcNCount,
ofcMaxValue,
&OF_base,
&OF_bits,
ofcLog,
);
pos += ofcSize;
let mut mlNCount = [0i16; (MaxML + 1) as usize];
let mut mlMaxValue: u32 = MaxML;
let mut mlLog: u32 = 0;
let mlSize = crate::common::entropy_common::FSE_readNCount(
&mut mlNCount,
&mut mlMaxValue,
&mut mlLog,
&dict[pos..],
);
if ERR_isError(mlSize) || mlMaxValue > MaxML || mlLog > MLFSELog {
return ERROR(ErrorCode::DictionaryCorrupted);
}
ZSTD_buildFSETable(
&mut dctx.MLTable,
&mlNCount,
mlMaxValue,
&ML_base,
&ML_bits,
mlLog,
);
pos += mlSize;
let mut llNCount = [0i16; (MaxLL + 1) as usize];
let mut llMaxValue: u32 = MaxLL;
let mut llLog: u32 = 0;
let llSize = crate::common::entropy_common::FSE_readNCount(
&mut llNCount,
&mut llMaxValue,
&mut llLog,
&dict[pos..],
);
if ERR_isError(llSize) || llMaxValue > MaxLL || llLog > LLFSELog {
return ERROR(ErrorCode::DictionaryCorrupted);
}
ZSTD_buildFSETable(
&mut dctx.LLTable,
&llNCount,
llMaxValue,
&LL_base,
&LL_bits,
llLog,
);
pos += llSize;
dctx.ll_default_active = false;
dctx.of_default_active = false;
dctx.ml_default_active = false;
if pos + 12 > dict.len() {
return ERROR(ErrorCode::DictionaryCorrupted);
}
let dictContentSize = dict.len() - (pos + 12);
for slot in repOut.iter_mut() {
let r = MEM_readLE32(&dict[pos..pos + 4]);
if r == 0 || (r as usize) > dictContentSize {
return ERROR(ErrorCode::DictionaryCorrupted);
}
*slot = r;
pos += 4;
}
pos
}
pub fn ZSTD_decompressMultiFrame(
dctx: &mut ZSTD_DCtx,
dst: &mut [u8],
src: &[u8],
dict: &[u8],
ddict: Option<&crate::decompress::zstd_ddict::ZSTD_DDict>,
) -> usize {
debug_assert!(
dict.is_empty() || ddict.is_none(),
"dict xor ddict, not both"
);
if let Some(dd) = ddict {
ZSTD_decompress_usingDDict(dctx, dst, src, dd)
} else {
ZSTD_decompress_usingDict(dctx, dst, src, dict)
}
}
pub fn ZSTD_getFrameContentSize(src: &[u8]) -> u64 {
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, src);
if rc != 0 {
return ZSTD_CONTENTSIZE_ERROR;
}
if zfh.frameType == ZSTD_FrameType_e::ZSTD_skippableFrame {
return 0;
}
zfh.frameContentSize
}
fn readSkippableFrameSize(src: &[u8]) -> usize {
if src.len() < ZSTD_SKIPPABLEHEADERSIZE {
return ERROR(ErrorCode::SrcSizeWrong);
}
let sizeU32 = MEM_readLE32(&src[ZSTD_FRAMEIDSIZE..ZSTD_FRAMEIDSIZE + 4]);
let total = ZSTD_SKIPPABLEHEADERSIZE.wrapping_add(sizeU32 as usize);
if (total as u32) < sizeU32 {
return ERROR(ErrorCode::FrameParameterUnsupported);
}
if total > src.len() {
return ERROR(ErrorCode::SrcSizeWrong);
}
total
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ZSTD_frameSizeInfo {
pub nbBlocks: usize,
pub compressedSize: usize,
pub decompressedBound: u64,
}
fn frameSizeInfo_error(err_code: usize) -> ZSTD_frameSizeInfo {
ZSTD_frameSizeInfo {
nbBlocks: 0,
compressedSize: err_code,
decompressedBound: ZSTD_CONTENTSIZE_ERROR,
}
}
pub fn ZSTD_findFrameSizeInfo(src: &[u8], format: ZSTD_format_e) -> ZSTD_frameSizeInfo {
use crate::decompress::zstd_decompress_block::{
blockProperties_t, blockType_e, ZSTD_blockHeaderSize, ZSTD_getcBlockSize,
};
let srcSize = src.len();
if format == ZSTD_format_e::ZSTD_f_zstd1
&& srcSize >= ZSTD_SKIPPABLEHEADERSIZE
&& (MEM_readLE32(&src[..4]) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START
{
return ZSTD_frameSizeInfo {
nbBlocks: 0,
compressedSize: readSkippableFrameSize(src),
decompressedBound: 0,
};
}
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader_advanced(&mut zfh, src, format);
if crate::common::error::ERR_isError(rc) {
return frameSizeInfo_error(rc);
}
if rc > 0 {
return frameSizeInfo_error(ERROR(ErrorCode::SrcSizeWrong));
}
let mut ip = zfh.headerSize as usize;
let mut remaining = srcSize - ip;
let mut nbBlocks: usize = 0;
loop {
let mut bp = blockProperties_t {
blockType: blockType_e::bt_raw,
lastBlock: 0,
origSize: 0,
};
let cBlockSize = ZSTD_getcBlockSize(&src[ip..], &mut bp);
if crate::common::error::ERR_isError(cBlockSize) {
return frameSizeInfo_error(cBlockSize);
}
if ZSTD_blockHeaderSize + cBlockSize > remaining {
return frameSizeInfo_error(ERROR(ErrorCode::SrcSizeWrong));
}
ip += ZSTD_blockHeaderSize + cBlockSize;
remaining -= ZSTD_blockHeaderSize + cBlockSize;
nbBlocks += 1;
if bp.lastBlock != 0 {
break;
}
}
if zfh.checksumFlag != 0 {
if remaining < 4 {
return frameSizeInfo_error(ERROR(ErrorCode::SrcSizeWrong));
}
ip += 4;
}
let compressedSize = ip;
let decompressedBound = if zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN {
zfh.frameContentSize
} else {
nbBlocks as u64 * zfh.blockSizeMax as u64
};
ZSTD_frameSizeInfo {
nbBlocks,
compressedSize,
decompressedBound,
}
}
pub fn ZSTD_findFrameCompressedSize_advanced(src: &[u8], format: ZSTD_format_e) -> usize {
ZSTD_findFrameSizeInfo(src, format).compressedSize
}
#[inline]
pub fn ZSTD_findFrameCompressedSize(src: &[u8]) -> usize {
ZSTD_findFrameCompressedSize_advanced(src, ZSTD_format_e::ZSTD_f_zstd1)
}
pub fn ZSTD_findDecompressedSize(src: &[u8]) -> u64 {
let mut src = src;
let mut total: u64 = 0;
while src.len() >= ZSTD_startingInputLength(ZSTD_format_e::ZSTD_f_zstd1) {
let magic = MEM_readLE32(&src[..4]);
if (magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START {
let sz = readSkippableFrameSize(src);
if crate::common::error::ERR_isError(sz) {
return ZSTD_CONTENTSIZE_ERROR;
}
src = &src[sz..];
continue;
}
let fcs = ZSTD_getFrameContentSize(src);
if fcs >= ZSTD_CONTENTSIZE_ERROR {
return fcs;
}
if total.checked_add(fcs).is_none() {
return ZSTD_CONTENTSIZE_ERROR;
}
total += fcs;
let sz = ZSTD_findFrameCompressedSize(src);
if crate::common::error::ERR_isError(sz) {
return ZSTD_CONTENTSIZE_ERROR;
}
src = &src[sz..];
}
if !src.is_empty() {
return ZSTD_CONTENTSIZE_ERROR;
}
total
}
pub fn ZSTD_initDStream(zds: &mut ZSTD_DCtx) -> usize {
ZSTD_DCtx_resetSession(zds);
ZSTD_clearDict(zds);
ZSTD_startingInputLength(zds.format)
}
pub fn ZSTD_initDStream_usingDict(zds: &mut ZSTD_DCtx, dict: &[u8]) -> usize {
let rc = ZSTD_initDStream(zds);
if crate::common::error::ERR_isError(rc) {
return rc;
}
let rc = ZSTD_DCtx_loadDictionary(zds, dict);
if crate::common::error::ERR_isError(rc) {
return rc;
}
ZSTD_startingInputLength(zds.format)
}
#[inline]
pub const fn ZSTD_DECOMPRESSION_MARGIN(originalSize: usize, blockSize: usize) -> usize {
use crate::compress::zstd_compress::ZSTD_FRAMEHEADERSIZE_MAX;
let blocks = if originalSize == 0 {
0
} else {
3 * originalSize.div_ceil(blockSize)
};
ZSTD_FRAMEHEADERSIZE_MAX + 4 + blocks + blockSize
}
pub fn ZSTD_decompressionMargin(src: &[u8]) -> usize {
use crate::common::error::{ERR_isError, ErrorCode, ERROR};
let mut margin: usize = 0;
let mut maxBlockSize: u32 = 0;
let mut cursor = src;
while !cursor.is_empty() {
let info = ZSTD_findFrameSizeInfo(cursor, ZSTD_format_e::ZSTD_f_zstd1);
let mut zfh = ZSTD_FrameHeader::default();
let rc = ZSTD_getFrameHeader(&mut zfh, cursor);
if ERR_isError(rc) {
return rc;
}
if ERR_isError(info.compressedSize) || info.decompressedBound == ZSTD_CONTENTSIZE_ERROR {
return ERROR(ErrorCode::CorruptionDetected);
}
if zfh.frameType == ZSTD_FrameType_e::ZSTD_frame {
margin += zfh.headerSize as usize;
margin += if zfh.checksumFlag != 0 { 4 } else { 0 };
margin += 3 * info.nbBlocks;
if zfh.blockSizeMax > maxBlockSize {
maxBlockSize = zfh.blockSizeMax;
}
} else {
margin += info.compressedSize;
}
cursor = &cursor[info.compressedSize..];
}
margin + maxBlockSize as usize
}
pub fn ZSTD_decompressBound(src: &[u8]) -> u64 {
use crate::common::error::ERR_isError;
let mut bound: u64 = 0;
let mut cursor = src;
while !cursor.is_empty() {
let info = ZSTD_findFrameSizeInfo(cursor, ZSTD_format_e::ZSTD_f_zstd1);
if ERR_isError(info.compressedSize) || info.decompressedBound == ZSTD_CONTENTSIZE_ERROR {
return ZSTD_CONTENTSIZE_ERROR;
}
cursor = &cursor[info.compressedSize..];
bound = bound.saturating_add(info.decompressedBound);
}
bound
}
pub fn ZSTD_getDecompressedSize(src: &[u8]) -> u64 {
let ret = ZSTD_getFrameContentSize(src);
if ret >= ZSTD_CONTENTSIZE_ERROR {
0
} else {
ret
}
}
pub fn ZSTD_decodingBufferSize_internal(
windowSize: u64,
frameContentSize: u64,
blockSizeMax: usize,
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
use crate::common::zstd_internal::WILDCOPY_OVERLENGTH;
use crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX;
let blockSize = windowSize
.min(ZSTD_BLOCKSIZE_MAX as u64)
.min(blockSizeMax as u64) as usize;
let neededRBSize: u64 = windowSize + (blockSize as u64) * 2 + (WILDCOPY_OVERLENGTH as u64) * 2;
let neededSize = frameContentSize.min(neededRBSize);
let minRBSize = neededSize as usize;
if minRBSize as u64 != neededSize {
return ERROR(ErrorCode::FrameParameterWindowTooLarge);
}
minRBSize
}
pub fn ZSTD_decodingBufferSize_min(windowSize: u64, frameContentSize: u64) -> usize {
use crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX;
ZSTD_decodingBufferSize_internal(windowSize, frameContentSize, ZSTD_BLOCKSIZE_MAX)
}
pub fn ZSTD_createDCtx_advanced(
customMem: crate::compress::zstd_compress::ZSTD_customMem,
) -> Option<Box<ZSTD_DCtx>> {
if !crate::compress::zstd_compress::ZSTD_customMem_validate(customMem) {
return None;
}
ZSTD_createDCtx_internal(customMem)
}
pub fn ZSTD_createDStream_advanced(
customMem: crate::compress::zstd_compress::ZSTD_customMem,
) -> Option<Box<ZSTD_DStream>> {
ZSTD_createDCtx_advanced(customMem)
}
pub fn ZSTD_initStaticDCtx(workspace: &mut [u8]) -> Option<&mut ZSTD_DCtx> {
use core::mem::{align_of, size_of};
use core::ptr;
if (workspace.as_mut_ptr() as usize) & (align_of::<u64>() - 1) != 0 {
return None;
}
if workspace.len() < size_of::<ZSTD_DCtx>() {
return None;
}
let dctx = unsafe { &mut *(workspace.as_mut_ptr() as *mut ZSTD_DCtx) };
unsafe {
ptr::write(dctx, ZSTD_DCtx::default());
}
ZSTD_initDCtx_internal(dctx);
Some(dctx)
}
pub fn ZSTD_initStaticDStream(workspace: &mut [u8]) -> Option<&mut ZSTD_DStream> {
ZSTD_initStaticDCtx(workspace)
}
pub fn ZSTD_initStaticDDict<'a>(
workspace: &'a mut [u8],
dict: &'a [u8],
) -> Option<&'a mut crate::decompress::zstd_ddict::ZSTD_DDict> {
use crate::decompress::zstd_ddict::{ZSTD_DDict, ZSTD_dictContentType_e};
use core::mem::{align_of, size_of};
use core::ptr;
if (workspace.as_mut_ptr() as usize) & (align_of::<u64>() - 1) != 0 {
return None;
}
if workspace.len() < size_of::<ZSTD_DDict>() {
return None;
}
let ddict = unsafe { &mut *(workspace.as_mut_ptr() as *mut ZSTD_DDict) };
unsafe {
ptr::write(ddict, ZSTD_DDict::borrowed(dict));
}
let rc = crate::decompress::zstd_ddict::ZSTD_loadEntropy_intoDDict(
ddict,
ZSTD_dictContentType_e::ZSTD_dct_auto,
);
if crate::common::error::ERR_isError(rc) {
return None;
}
Some(ddict)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ZSTD_nextInputType_e {
#[default]
ZSTDnit_frameHeader = 0,
ZSTDnit_blockHeader = 1,
ZSTDnit_block = 2,
ZSTDnit_lastBlock = 3,
ZSTDnit_checksum = 4,
ZSTDnit_skippableFrame = 5,
}
#[inline]
pub fn ZSTD_nextInputType(dctx: &ZSTD_DCtx) -> ZSTD_nextInputType_e {
match dctx.stage {
ZSTD_dStage::ZSTDds_getFrameHeaderSize | ZSTD_dStage::ZSTDds_decodeFrameHeader => {
ZSTD_nextInputType_e::ZSTDnit_frameHeader
}
ZSTD_dStage::ZSTDds_decodeBlockHeader => ZSTD_nextInputType_e::ZSTDnit_blockHeader,
ZSTD_dStage::ZSTDds_decompressBlock => ZSTD_nextInputType_e::ZSTDnit_block,
ZSTD_dStage::ZSTDds_decompressLastBlock => ZSTD_nextInputType_e::ZSTDnit_lastBlock,
ZSTD_dStage::ZSTDds_checkChecksum => ZSTD_nextInputType_e::ZSTDnit_checksum,
ZSTD_dStage::ZSTDds_decodeSkippableHeader | ZSTD_dStage::ZSTDds_skipFrame => {
ZSTD_nextInputType_e::ZSTDnit_skippableFrame
}
ZSTD_dStage::ZSTDds_frameComplete => ZSTD_nextInputType_e::ZSTDnit_frameHeader,
}
}
#[inline]
pub fn ZSTD_nextSrcSizeToDecompressWithInputSize(dctx: &ZSTD_DCtx, inputSize: usize) -> usize {
let in_block = matches!(
dctx.stage,
ZSTD_dStage::ZSTDds_decompressBlock | ZSTD_dStage::ZSTDds_decompressLastBlock
);
if !in_block || dctx.bType != crate::decompress::zstd_decompress_block::blockType_e::bt_raw {
return dctx.expected;
}
if dctx.expected == 0 {
return 0;
}
inputSize.clamp(1, dctx.expected)
}
#[inline]
pub fn ZSTD_isSkipFrame(dctx: &ZSTD_DCtx) -> i32 {
(dctx.stage == ZSTD_dStage::ZSTDds_skipFrame) as i32
}
#[inline]
pub fn ZSTD_decompressBegin(dctx: &mut ZSTD_DCtx) -> usize {
use crate::common::xxhash::XXH64_reset;
dctx.litEntropy = 0;
dctx.fseEntropy = 0;
dctx.dictID = 0;
dctx.isFrameDecompression = 1;
dctx.previousDstEnd = None;
dctx.prefixStart = None;
dctx.virtualStart = None;
dctx.dictEnd = None;
dctx.ddict_rep = [1, 4, 8];
crate::decompress::zstd_decompress_block::ZSTD_buildDefaultSeqTables(dctx);
dctx.expected = ZSTD_startingInputLength(dctx.format);
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
dctx.processedCSize = 0;
dctx.decodedSize = 0;
dctx.headerSize = 0;
dctx.rleSize = 0;
dctx.bType = crate::decompress::zstd_decompress_block::blockType_e::bt_reserved;
dctx.validateChecksum = 0;
dctx.fParams = ZSTD_FrameHeader::default();
dctx.headerBuffer.fill(0);
dctx.historyBuffer.clear();
XXH64_reset(&mut dctx.xxhState, 0);
0
}
pub fn ZSTD_decompressBegin_usingDict(dctx: &mut ZSTD_DCtx, dict: &[u8]) -> usize {
use crate::common::error::{ERR_isError, ErrorCode, ERROR};
let rc = ZSTD_decompressBegin(dctx);
if ERR_isError(rc) {
return rc;
}
if dict.is_empty() {
return 0;
}
let rc = ZSTD_decompress_insertDictionary(dctx, dict);
if ERR_isError(rc) {
return ERROR(ErrorCode::DictionaryCorrupted);
}
0
}
pub fn ZSTD_decompressBegin_usingDDict(
dctx: &mut ZSTD_DCtx,
ddict: &crate::decompress::zstd_ddict::ZSTD_DDict,
) -> usize {
use crate::common::error::ERR_isError;
let rc = ZSTD_decompressBegin(dctx);
if ERR_isError(rc) {
return rc;
}
if crate::decompress::zstd_ddict::ZSTD_DDict_rawContent(ddict).is_empty()
&& crate::decompress::zstd_ddict::ZSTD_DDict_entropyPresent(ddict) == 0
{
return 0;
}
crate::decompress::zstd_ddict::ZSTD_copyDDictParameters(dctx, ddict);
0
}
pub fn ZSTD_decompressContinue(dctx: &mut ZSTD_DCtx, dst: &mut [u8], src: &[u8]) -> usize {
use crate::common::error::{ErrorCode, ERROR};
use crate::common::mem::MEM_readLE32;
use crate::common::xxhash::{XXH64_digest, XXH64_update};
use crate::decompress::zstd_decompress_block::{
blockProperties_t, blockType_e, streaming_operation, ZSTD_blockHeaderSize,
ZSTD_checkContinuity, ZSTD_decoder_entropy_rep, ZSTD_decompressBlock_internal,
ZSTD_getcBlockSize,
};
let expected_src_size = if matches!(
dctx.stage,
ZSTD_dStage::ZSTDds_decompressBlock | ZSTD_dStage::ZSTDds_decompressLastBlock
) && dctx.bType == blockType_e::bt_raw
{
src.len().clamp(1, dctx.expected)
} else {
dctx.expected
};
if src.len() != expected_src_size {
return ERROR(ErrorCode::SrcSizeWrong);
}
ZSTD_checkContinuity(dctx, dst, dst.len());
dctx.processedCSize = dctx.processedCSize.wrapping_add(src.len() as u64);
match dctx.stage {
ZSTD_dStage::ZSTDds_getFrameHeaderSize => {
if dctx.format == ZSTD_format_e::ZSTD_f_zstd1
&& src.len() >= ZSTD_FRAMEIDSIZE
&& (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START
{
dctx.headerBuffer[..src.len()].copy_from_slice(src);
dctx.expected = ZSTD_SKIPPABLEHEADERSIZE - src.len();
dctx.stage = ZSTD_dStage::ZSTDds_decodeSkippableHeader;
return 0;
}
let headerSize = ZSTD_frameHeaderSize_internal(src, dctx.format);
if crate::common::error::ERR_isError(headerSize) {
return headerSize;
}
dctx.headerSize = headerSize;
dctx.headerBuffer[..src.len()].copy_from_slice(src);
dctx.expected = headerSize - src.len();
dctx.stage = ZSTD_dStage::ZSTDds_decodeFrameHeader;
0
}
ZSTD_dStage::ZSTDds_decodeFrameHeader => {
let offset = dctx.headerSize - src.len();
dctx.headerBuffer[offset..offset + src.len()].copy_from_slice(src);
let header = dctx.headerBuffer[..dctx.headerSize].to_vec();
let rc = ZSTD_decodeFrameHeader(dctx, &header, header.len());
if crate::common::error::ERR_isError(rc) {
return rc;
}
if dctx.fParams.frameType == ZSTD_FrameType_e::ZSTD_frame
&& dctx.fParams.windowSize > dctx.d_maxWindowSize
{
return ERROR(ErrorCode::FrameParameterWindowTooLarge);
}
if dctx.fParams.frameType != ZSTD_FrameType_e::ZSTD_frame {
return ERROR(ErrorCode::PrefixUnknown);
}
dctx.stream_regular_frame_started = true;
dctx.expected = ZSTD_blockHeaderSize;
dctx.stage = ZSTD_dStage::ZSTDds_decodeBlockHeader;
0
}
ZSTD_dStage::ZSTDds_decodeBlockHeader => {
let mut bp = blockProperties_t {
blockType: blockType_e::bt_raw,
lastBlock: 0,
origSize: 0,
};
let cBlockSize = ZSTD_getcBlockSize(src, &mut bp);
if crate::common::error::ERR_isError(cBlockSize) {
return cBlockSize;
}
if cBlockSize > dctx.fParams.blockSizeMax as usize {
return ERROR(ErrorCode::CorruptionDetected);
}
dctx.expected = cBlockSize;
dctx.bType = bp.blockType;
dctx.rleSize = bp.origSize as usize;
if cBlockSize != 0 {
dctx.stage = if bp.lastBlock != 0 {
ZSTD_dStage::ZSTDds_decompressLastBlock
} else {
ZSTD_dStage::ZSTDds_decompressBlock
};
return 0;
}
if bp.lastBlock != 0 {
if dctx.fParams.checksumFlag != 0 {
dctx.expected = 4;
dctx.stage = ZSTD_dStage::ZSTDds_checkChecksum;
} else {
dctx.expected = 0;
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
}
} else {
dctx.expected = ZSTD_blockHeaderSize;
}
0
}
ZSTD_dStage::ZSTDds_decompressLastBlock | ZSTD_dStage::ZSTDds_decompressBlock => {
let mut entropy_rep = ZSTD_decoder_entropy_rep {
rep: dctx.ddict_rep,
};
let rSize = match dctx.bType {
blockType_e::bt_compressed => {
let r = ZSTD_decompressBlock_internal(
dctx,
&mut entropy_rep,
dst,
0,
src,
streaming_operation::is_streaming,
);
dctx.expected = 0;
r
}
blockType_e::bt_raw => {
let r = ZSTD_copyRawBlock(dst, src);
if crate::common::error::ERR_isError(r) {
return r;
}
dctx.expected -= r;
r
}
blockType_e::bt_rle => {
let r = ZSTD_setRleBlock(dst, src[0], dctx.rleSize);
dctx.expected = 0;
r
}
blockType_e::bt_reserved => return ERROR(ErrorCode::CorruptionDetected),
};
if crate::common::error::ERR_isError(rSize) {
return rSize;
}
if rSize > dctx.fParams.blockSizeMax as usize {
return ERROR(ErrorCode::CorruptionDetected);
}
dctx.previousDstEnd = Some(dst.as_ptr() as usize + rSize);
dctx.ddict_rep = entropy_rep.rep;
dctx.decodedSize = dctx.decodedSize.wrapping_add(rSize as u64);
if dctx.validateChecksum != 0 && rSize > 0 {
XXH64_update(&mut dctx.xxhState, &dst[..rSize]);
}
if rSize > 0 {
let cap = if dctx.fParams.windowSize > 0 {
dctx.fParams.windowSize as usize
} else {
crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX
};
dctx.historyBuffer.extend_from_slice(&dst[..rSize]);
let compact_at = cap.saturating_add(
crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX
* ZSTD_HISTORY_COMPACT_SLACK_BLOCKS,
);
if dctx.historyBuffer.len() > compact_at {
let drop = dctx.historyBuffer.len() - cap;
dctx.historyBuffer.drain(..drop);
}
}
if dctx.expected > 0 {
return rSize;
}
if dctx.stage == ZSTD_dStage::ZSTDds_decompressLastBlock {
if dctx.fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN
&& dctx.decodedSize != dctx.fParams.frameContentSize
{
return ERROR(ErrorCode::CorruptionDetected);
}
if dctx.fParams.checksumFlag != 0 {
dctx.expected = 4;
dctx.stage = ZSTD_dStage::ZSTDds_checkChecksum;
} else {
dctx.expected = 0;
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
}
dctx.historyBuffer.clear();
} else {
dctx.expected = ZSTD_blockHeaderSize;
dctx.stage = ZSTD_dStage::ZSTDds_decodeBlockHeader;
}
rSize
}
ZSTD_dStage::ZSTDds_checkChecksum => {
if dctx.validateChecksum != 0 {
let h32 = XXH64_digest(&dctx.xxhState) as u32;
let check32 = MEM_readLE32(src);
if check32 != h32 {
return ERROR(ErrorCode::ChecksumWrong);
}
}
dctx.expected = 0;
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
0
}
ZSTD_dStage::ZSTDds_decodeSkippableHeader => {
let offset = ZSTD_SKIPPABLEHEADERSIZE - src.len();
dctx.headerBuffer[offset..offset + src.len()].copy_from_slice(src);
dctx.expected = MEM_readLE32(&dctx.headerBuffer[ZSTD_FRAMEIDSIZE..]) as usize;
dctx.stage = ZSTD_dStage::ZSTDds_skipFrame;
0
}
ZSTD_dStage::ZSTDds_skipFrame => {
dctx.expected = 0;
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
0
}
ZSTD_dStage::ZSTDds_frameComplete => 0,
}
}
pub fn ZSTD_decompressContinue_into_history<'a>(
dctx: &'a mut ZSTD_DCtx,
src: &[u8],
) -> Result<&'a [u8], usize> {
use crate::common::error::{ErrorCode, ERROR};
use crate::common::xxhash::XXH64_update;
use crate::decompress::zstd_decompress_block::{
blockType_e, streaming_operation, ZSTD_blockHeaderSize, ZSTD_decoder_entropy_rep,
ZSTD_decompressBlock_internal, WILDCOPY_OVERLENGTH, ZSTD_BLOCKSIZE_MAX,
};
if !matches!(
dctx.stage,
ZSTD_dStage::ZSTDds_decompressBlock | ZSTD_dStage::ZSTDds_decompressLastBlock
) {
let mut empty = [];
let rc = ZSTD_decompressContinue(dctx, &mut empty, src);
if crate::common::error::ERR_isError(rc) {
return Err(rc);
}
return Ok(&[]);
}
let expected_src_size = if dctx.bType == blockType_e::bt_raw {
src.len().clamp(1, dctx.expected)
} else {
dctx.expected
};
if src.len() != expected_src_size {
return Err(ERROR(ErrorCode::SrcSizeWrong));
}
dctx.processedCSize = dctx.processedCSize.wrapping_add(src.len() as u64);
let cap = if dctx.fParams.windowSize > 0 {
dctx.fParams.windowSize as usize
} else {
ZSTD_BLOCKSIZE_MAX
};
let compact_at = cap.saturating_add(ZSTD_BLOCKSIZE_MAX * ZSTD_HISTORY_COMPACT_SLACK_BLOCKS);
if dctx.historyBuffer.len() > compact_at {
let drop = dctx.historyBuffer.len() - cap;
dctx.historyBuffer.drain(..drop);
}
let max_out = match dctx.bType {
blockType_e::bt_compressed => dctx.fParams.blockSizeMax as usize,
blockType_e::bt_raw => src.len(),
blockType_e::bt_rle => dctx.rleSize,
blockType_e::bt_reserved => return Err(ERROR(ErrorCode::CorruptionDetected)),
};
let old_len = dctx.historyBuffer.len();
let dst_capacity = max_out.saturating_add(WILDCOPY_OVERLENGTH);
dctx.historyBuffer.reserve(dst_capacity);
let dst = unsafe {
std::slice::from_raw_parts_mut(dctx.historyBuffer.as_mut_ptr().add(old_len), dst_capacity)
};
let mut entropy_rep = ZSTD_decoder_entropy_rep {
rep: dctx.ddict_rep,
};
let rSize = match dctx.bType {
blockType_e::bt_compressed => {
let r = ZSTD_decompressBlock_internal(
dctx,
&mut entropy_rep,
dst,
0,
src,
streaming_operation::is_streaming,
);
dctx.expected = 0;
r
}
blockType_e::bt_raw => {
let r = ZSTD_copyRawBlock(dst, src);
if crate::common::error::ERR_isError(r) {
return Err(r);
}
dctx.expected -= r;
r
}
blockType_e::bt_rle => {
let r = ZSTD_setRleBlock(dst, src[0], dctx.rleSize);
dctx.expected = 0;
r
}
blockType_e::bt_reserved => return Err(ERROR(ErrorCode::CorruptionDetected)),
};
if crate::common::error::ERR_isError(rSize) {
return Err(rSize);
}
if rSize > dctx.fParams.blockSizeMax as usize || rSize > max_out {
return Err(ERROR(ErrorCode::CorruptionDetected));
}
unsafe {
dctx.historyBuffer.set_len(old_len + rSize);
}
dctx.previousDstEnd =
Some(unsafe { dctx.historyBuffer.as_ptr().add(old_len + rSize) } as usize);
dctx.ddict_rep = entropy_rep.rep;
dctx.decodedSize = dctx.decodedSize.wrapping_add(rSize as u64);
if dctx.validateChecksum != 0 && rSize > 0 {
XXH64_update(
&mut dctx.xxhState,
&dctx.historyBuffer[old_len..old_len + rSize],
);
}
if dctx.expected > 0 {
return Ok(&dctx.historyBuffer[old_len..old_len + rSize]);
}
if dctx.stage == ZSTD_dStage::ZSTDds_decompressLastBlock {
if dctx.fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN
&& dctx.decodedSize != dctx.fParams.frameContentSize
{
return Err(ERROR(ErrorCode::CorruptionDetected));
}
if dctx.fParams.checksumFlag != 0 {
dctx.expected = 4;
dctx.stage = ZSTD_dStage::ZSTDds_checkChecksum;
} else {
dctx.expected = 0;
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
}
} else {
dctx.expected = ZSTD_blockHeaderSize;
dctx.stage = ZSTD_dStage::ZSTDds_decodeBlockHeader;
}
Ok(&dctx.historyBuffer[old_len..old_len + rSize])
}
pub fn ZSTD_decompressContinue_into_prefix(
dctx: &mut ZSTD_DCtx,
src: &[u8],
dst: &mut [u8],
op_start: usize,
ext_history: &[u8],
) -> Result<usize, usize> {
use crate::common::error::{ErrorCode, ERROR};
use crate::common::xxhash::XXH64_update;
use crate::decompress::zstd_decompress_block::{
blockType_e, streaming_operation, ZSTD_blockHeaderSize, ZSTD_decoder_entropy_rep,
ZSTD_decompressBlock_internal_with_ext_history,
};
if !matches!(
dctx.stage,
ZSTD_dStage::ZSTDds_decompressBlock | ZSTD_dStage::ZSTDds_decompressLastBlock
) {
return Err(ERROR(ErrorCode::StageWrong));
}
let expected_src_size = if dctx.bType == blockType_e::bt_raw {
src.len().clamp(1, dctx.expected)
} else {
dctx.expected
};
if src.len() != expected_src_size {
return Err(ERROR(ErrorCode::SrcSizeWrong));
}
dctx.processedCSize = dctx.processedCSize.wrapping_add(src.len() as u64);
let max_out = match dctx.bType {
blockType_e::bt_compressed => dctx.fParams.blockSizeMax as usize,
blockType_e::bt_raw => src.len(),
blockType_e::bt_rle => dctx.rleSize,
blockType_e::bt_reserved => return Err(ERROR(ErrorCode::CorruptionDetected)),
};
if op_start.saturating_add(max_out) > dst.len() {
return Err(ERROR(ErrorCode::DstSizeTooSmall));
}
let mut entropy_rep = ZSTD_decoder_entropy_rep {
rep: dctx.ddict_rep,
};
let rSize = match dctx.bType {
blockType_e::bt_compressed => {
let r = ZSTD_decompressBlock_internal_with_ext_history(
dctx,
&mut entropy_rep,
dst,
op_start,
src,
streaming_operation::is_streaming,
Some(ext_history),
);
dctx.expected = 0;
r
}
blockType_e::bt_raw => {
let r = ZSTD_copyRawBlock(&mut dst[op_start..], src);
if crate::common::error::ERR_isError(r) {
return Err(r);
}
dctx.expected -= r;
r
}
blockType_e::bt_rle => {
let r = ZSTD_setRleBlock(&mut dst[op_start..], src[0], dctx.rleSize);
dctx.expected = 0;
r
}
blockType_e::bt_reserved => return Err(ERROR(ErrorCode::CorruptionDetected)),
};
if crate::common::error::ERR_isError(rSize) {
return Err(rSize);
}
if rSize > dctx.fParams.blockSizeMax as usize || rSize > max_out {
return Err(ERROR(ErrorCode::CorruptionDetected));
}
dctx.previousDstEnd = Some(unsafe { dst.as_ptr().add(op_start + rSize) } as usize);
dctx.ddict_rep = entropy_rep.rep;
dctx.decodedSize = dctx.decodedSize.wrapping_add(rSize as u64);
if dctx.validateChecksum != 0 && rSize > 0 {
XXH64_update(&mut dctx.xxhState, &dst[op_start..op_start + rSize]);
}
if dctx.expected > 0 {
return Ok(rSize);
}
if dctx.stage == ZSTD_dStage::ZSTDds_decompressLastBlock {
if dctx.fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN
&& dctx.decodedSize != dctx.fParams.frameContentSize
{
return Err(ERROR(ErrorCode::CorruptionDetected));
}
if dctx.fParams.checksumFlag != 0 {
dctx.expected = 4;
dctx.stage = ZSTD_dStage::ZSTDds_checkChecksum;
} else {
dctx.expected = 0;
dctx.stage = ZSTD_dStage::ZSTDds_getFrameHeaderSize;
}
} else {
dctx.expected = ZSTD_blockHeaderSize;
dctx.stage = ZSTD_dStage::ZSTDds_decodeBlockHeader;
}
Ok(rSize)
}
#[inline]
pub fn ZSTD_copyDCtx(dst: &mut ZSTD_DCtx, src: &ZSTD_DCtx) {
dst.clone_from(src);
}
pub fn ZSTD_initDStream_usingDDict(
zds: &mut ZSTD_DCtx,
ddict: &crate::decompress::zstd_ddict::ZSTD_DDict,
) -> usize {
let rc = ZSTD_initDStream(zds);
if crate::common::error::ERR_isError(rc) {
return rc;
}
let rc = ZSTD_DCtx_refDDict(zds, ddict);
if crate::common::error::ERR_isError(rc) {
return rc;
}
ZSTD_startingInputLength(zds.format)
}
#[inline]
pub fn ZSTD_nextSrcSizeToDecompress(dctx: &ZSTD_DCtx) -> usize {
dctx.expected
}
pub fn ZSTD_resetDStream(zds: &mut ZSTD_DCtx) -> usize {
ZSTD_DCtx_resetSession(zds);
ZSTD_startingInputLength(zds.format)
}
pub fn ZSTD_DCtx_isOverflow(
zds: &ZSTD_DCtx,
neededInBuffSize: usize,
neededOutBuffSize: usize,
) -> i32 {
let retained = zds.stream_in_buffer.capacity() + zds.stream_out_buffer.capacity();
let needed = (neededInBuffSize + neededOutBuffSize)
* crate::common::zstd_internal::ZSTD_WORKSPACETOOLARGE_FACTOR;
(retained >= needed) as i32
}
pub fn ZSTD_DCtx_updateOversizedDuration(
zds: &mut ZSTD_DCtx,
neededInBuffSize: usize,
neededOutBuffSize: usize,
) {
if ZSTD_DCtx_isOverflow(zds, neededInBuffSize, neededOutBuffSize) != 0 {
zds.oversizedDuration += 1;
} else {
zds.oversizedDuration = 0;
}
}
#[inline]
pub fn ZSTD_checkOutBuffer(_zds: &ZSTD_DCtx, _output: &[u8], _output_pos: usize) -> usize {
0
}
pub fn ZSTD_decompressContinueStream(
zds: &mut ZSTD_DCtx,
output: &mut [u8],
output_pos: &mut usize,
src: &[u8],
) -> usize {
let dstSize = if ZSTD_isSkipFrame(zds) != 0 {
0
} else {
output.len() - *output_pos
};
let decodedSize =
ZSTD_decompressContinue(zds, &mut output[*output_pos..*output_pos + dstSize], src);
if crate::common::error::ERR_isError(decodedSize) {
return decodedSize;
}
*output_pos += decodedSize;
0
}
fn ZSTD_restoreStreamingDictAtFrameBoundary(zds: &mut ZSTD_DCtx) -> usize {
if !zds.stream_regular_frame_started {
return 0;
}
zds.stream_regular_frame_started = false;
match zds.dictUses {
ZSTD_dictUses_e::ZSTD_dont_use => 0,
ZSTD_dictUses_e::ZSTD_use_once => {
ZSTD_clearDict(zds);
0
}
ZSTD_dictUses_e::ZSTD_use_indefinitely => {
let source = zds.stream_dict_source.clone();
if source.is_empty() {
return 0;
}
ZSTD_clearDict(zds);
let result = ZSTD_decompress_insertDictionary(zds, &source);
if crate::common::error::ERR_isError(result) {
return result;
}
zds.dictUses = ZSTD_dictUses_e::ZSTD_use_indefinitely;
0
}
}
}
pub fn ZSTD_decompressStream(
zds: &mut ZSTD_DCtx,
output: &mut [u8],
output_pos: &mut usize,
input: &[u8],
input_pos: &mut usize,
) -> usize {
if *output_pos > output.len() {
return ERROR(ErrorCode::DstSizeTooSmall);
}
if *input_pos > input.len() {
return ERROR(ErrorCode::SrcSizeWrong);
}
if zds.stream_out_drained > zds.stream_out_buffer.len() {
return ERROR(ErrorCode::Generic);
}
loop {
let avail = output.len() - *output_pos;
let pending = zds.stream_out_buffer.len() - zds.stream_out_drained;
let n = avail.min(pending);
if n > 0 {
output[*output_pos..*output_pos + n].copy_from_slice(
&zds.stream_out_buffer[zds.stream_out_drained..zds.stream_out_drained + n],
);
zds.stream_out_drained += n;
*output_pos += n;
}
let pending = zds.stream_out_buffer.len() - zds.stream_out_drained;
if pending > 0 {
return pending.max(1);
}
zds.stream_out_buffer.clear();
zds.stream_out_drained = 0;
let mut expected = ZSTD_nextSrcSizeToDecompress(zds);
if expected == 0 {
let result = ZSTD_restoreStreamingDictAtFrameBoundary(zds);
if crate::common::error::ERR_isError(result) {
return result;
}
if *input_pos == input.len() && zds.stream_in_buffer.is_empty() {
return 0;
}
expected = ZSTD_resetDStream(zds);
if crate::common::error::ERR_isError(expected) {
return expected;
}
}
if *output_pos == output.len()
&& !matches!(
zds.stage,
ZSTD_dStage::ZSTDds_getFrameHeaderSize
| ZSTD_dStage::ZSTDds_decodeSkippableHeader
| ZSTD_dStage::ZSTDds_skipFrame
)
{
return expected.max(1);
}
let needed = expected.saturating_sub(zds.stream_in_buffer.len());
let available_input = input.len() - *input_pos;
let take = needed.min(available_input);
zds.stream_in_buffer
.extend_from_slice(&input[*input_pos..*input_pos + take]);
*input_pos += take;
zds.stream_active |= take != 0;
if zds.stream_in_buffer.len() < expected {
return (expected - zds.stream_in_buffer.len()).max(1);
}
let mut chunk = std::mem::take(&mut zds.stream_in_buffer);
if zds.stage == ZSTD_dStage::ZSTDds_getFrameHeaderSize
&& zds.format == ZSTD_format_e::ZSTD_f_zstd1
&& chunk.len() >= ZSTD_FRAMEIDSIZE
{
let magic = MEM_readLE32(&chunk[..ZSTD_FRAMEIDSIZE]);
if magic != ZSTD_MAGICNUMBER
&& (magic & ZSTD_MAGIC_SKIPPABLE_MASK) != ZSTD_MAGIC_SKIPPABLE_START
{
chunk.clear();
zds.stream_in_buffer = chunk;
return ERROR(ErrorCode::PrefixUnknown);
}
}
let produced_len = match ZSTD_decompressContinue_into_history(zds, &chunk) {
Ok(produced) => produced.len(),
Err(error) => {
chunk.clear();
zds.stream_in_buffer = chunk;
return error;
}
};
chunk.clear();
zds.stream_in_buffer = chunk;
let produced_end = zds.historyBuffer.len();
let produced_begin = produced_end - produced_len;
zds.stream_out_buffer.clear();
zds.stream_out_buffer
.extend_from_slice(&zds.historyBuffer[produced_begin..produced_end]);
zds.stream_out_drained = 0;
if zds.stream_out_buffer.is_empty()
&& *input_pos == input.len()
&& ZSTD_nextSrcSizeToDecompress(zds) == 0
{
return 0;
}
}
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn ZSTD_decompressStream_simpleArgs(
dctx: &mut ZSTD_DCtx,
dst: &mut [u8],
dst_pos: &mut usize,
src: &[u8],
src_pos: &mut usize,
) -> usize {
ZSTD_decompressStream(dctx, dst, dst_pos, src, src_pos)
}