Files
zstd-rs/rust/src/zstd_decompress.rs
T
ddidderr b550b14ceb refactor(decompress): remove redundant dictionary bridge
Call the authoritative DDict constructor directly from the Rust decompression
policy after converting the shared repr(C) allocator projection.  Remove the
C forwarding declaration and wrapper so dictionary creation no longer crosses
an unnecessary Rust-to-C-to-C boundary, while compile-time size and alignment
checks keep the allocator conversion tied to the ABI contract.

Test Plan:
- `ulimit -v 41943040; cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check`
- `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings`
- `ulimit -v 41943040; make -j1`
- `ulimit -v 41943040; make -j1 -C tests test`
- `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings`
- `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets`

The integrated checks ran at the combined working-tree tip, including the
parallel MT checksum seam that remains as the next commit. Standalone root
Rust unit linking remains unavailable because the crate imports C-owned bridge
symbols without a Cargo build/link setup.
2026-07-20 09:29:04 +02:00

5035 lines
167 KiB
Rust

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
//! Frame, context, and streaming decompression orchestration.
//!
//! The private `ZSTD_DCtx` layout deliberately remains C-owned. The companion
//! C translation unit projects its build-configuration-dependent leaves into
//! [`ZSTD_rustDctxView`], while this module owns allocation, the decoder state
//! machine, and the public ABI without assuming offsets for the private
//! context.
use crate::entropy_common::FSE_readNCount;
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
#[cfg(feature = "huf-force-decompress-x1")]
use crate::huf_decompress::HUF_readDTableX1_wksp;
#[cfg(not(feature = "huf-force-decompress-x1"))]
use crate::huf_decompress::HUF_readDTableX2_wksp;
use crate::mem::{MEM_32bits, MEM_readLE16, MEM_readLE32, MEM_readLE64};
use crate::xxhash::{XXH64_digest, XXH64_reset, XXH64_state_t, XXH64_update, XXH64};
use crate::zstd_ddict::{
ZSTD_DDict, ZSTD_DDict_dictContent, ZSTD_DDict_dictSize, ZSTD_copyDDictParameters,
ZSTD_createDDict_advanced, ZSTD_freeDDict, ZSTD_getDictID_fromDDict,
};
use std::cmp::{max, min};
use std::ffi::c_void;
use std::mem::{align_of, offset_of, size_of, MaybeUninit};
use std::os::raw::{c_int, c_uint};
use std::ptr;
const ZSTD_MAGICNUMBER: u32 = 0xFD2F_B528;
const ZSTD_MAGIC_DICTIONARY: u32 = 0xEC30_A437;
const ZSTD_MAGIC_SKIPPABLE_START: u32 = 0x184D_2A50;
const ZSTD_MAGIC_SKIPPABLE_MASK: u32 = 0xFFFF_FFF0;
const ZSTD_FRAMEIDSIZE: usize = 4;
const ZSTD_SKIPPABLEHEADERSIZE: usize = 8;
const ZSTD_BLOCKHEADERSIZE: usize = 3;
const ZSTD_BLOCKSIZE_MAX: usize = 128 << 10;
const ZSTD_BLOCKSIZE_MAX_MIN: usize = 1 << 10;
const ZSTD_WINDOWLOG_ABSOLUTEMIN: usize = 10;
const ZSTD_WINDOWLOG_LIMIT_DEFAULT: usize = 27;
const ZSTD_WINDOWLOG_MAX_32: usize = 30;
const ZSTD_WINDOWLOG_MAX_64: usize = 31;
const WILDCOPY_OVERLENGTH: usize = 32;
const ZSTD_WORKSPACETOOLARGE_FACTOR: usize = 3;
const ZSTD_WORKSPACETOOLARGE_MAXDURATION: usize = 128;
const ZSTD_HUFFDTABLE_CAPACITY_LOG: usize = 12;
const HUF_DTABLE_SIZE: usize = 1 + (1 << ZSTD_HUFFDTABLE_CAPACITY_LOG);
const ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32: usize = 157;
const LL_FSE_LOG: usize = 9;
const OFF_FSE_LOG: usize = 8;
const ML_FSE_LOG: usize = 9;
const MAX_LL: usize = 35;
const MAX_ML: usize = 52;
const MAX_OFF: usize = 31;
const ZSTD_REP_NUM: usize = 3;
const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX;
const ZSTD_CONTENTSIZE_ERROR: u64 = u64::MAX - 1;
const ZSTD_F_ZSTD1: c_int = 0;
const ZSTD_F_ZSTD1_MAGICLESS: c_int = 1;
const ZSTD_FRAME: c_int = 0;
const ZSTD_SKIPPABLE_FRAME: c_int = 1;
const ZSTD_BM_BUFFERED: c_int = 0;
const ZSTD_BM_STABLE: c_int = 1;
const ZSTD_D_VALIDATE_CHECKSUM: c_int = 0;
const ZSTD_D_IGNORE_CHECKSUM: c_int = 1;
const ZSTD_RMD_REF_SINGLE_DDICT: c_int = 0;
const ZSTD_RMD_REF_MULTIPLE_DDICTS: c_int = 1;
const ZSTD_DLM_BY_COPY: c_int = 0;
const ZSTD_DLM_BY_REF: c_int = 1;
const ZSTD_DCT_AUTO: c_int = 0;
const ZSTD_DCT_RAW_CONTENT: c_int = 1;
const ZSTD_USE_INDEFINITELY: c_int = -1;
const ZSTD_DONT_USE: c_int = 0;
const ZSTD_USE_ONCE: c_int = 1;
/* The C legacy dispatcher uses a static byte when a zero-sized input,
* output, or dictionary buffer is represented by NULL. Keep the same
* lifetime and mutability without making the private ZSTD_DCtx layout part
* of the Rust ABI. */
static mut LEGACY_EMPTY_BYTE: u8 = 0;
const ZSTDDS_GET_FRAME_HEADER_SIZE: c_int = 0;
const ZSTDDS_DECODE_FRAME_HEADER: c_int = 1;
const ZSTDDS_DECODE_BLOCK_HEADER: c_int = 2;
const ZSTDDS_DECOMPRESS_BLOCK: c_int = 3;
const ZSTDDS_DECOMPRESS_LAST_BLOCK: c_int = 4;
const ZSTDDS_CHECK_CHECKSUM: c_int = 5;
const ZSTDDS_DECODE_SKIPPABLE_HEADER: c_int = 6;
const ZSTDDS_SKIP_FRAME: c_int = 7;
const ZDSS_INIT: c_int = 0;
const ZDSS_LOAD_HEADER: c_int = 1;
const ZDSS_READ: c_int = 2;
const ZDSS_LOAD: c_int = 3;
const ZDSS_FLUSH: c_int = 4;
const BT_RAW: c_int = 0;
const BT_RLE: c_int = 1;
const BT_COMPRESSED: c_int = 2;
const BT_RESERVED: c_int = 3;
const ZSTD_D_WINDOW_LOG_MAX: c_int = 100;
const ZSTD_D_FORMAT: c_int = 1000;
const ZSTD_D_STABLE_OUT_BUFFER: c_int = 1001;
const ZSTD_D_FORCE_IGNORE_CHECKSUM: c_int = 1002;
const ZSTD_D_REF_MULTIPLE_DDICTS: c_int = 1003;
const ZSTD_D_DISABLE_HUFFMAN_ASSEMBLY: c_int = 1004;
const ZSTD_D_MAX_BLOCK_SIZE: c_int = 1005;
const ZSTD_RESET_SESSION_ONLY: c_int = 1;
const ZSTD_RESET_PARAMETERS: c_int = 2;
const ZSTD_RESET_SESSION_AND_PARAMETERS: c_int = 3;
const ZSTD_NIT_FRAME_HEADER: c_int = 0;
const ZSTD_NIT_BLOCK_HEADER: c_int = 1;
const ZSTD_NIT_BLOCK: c_int = 2;
const ZSTD_NIT_LAST_BLOCK: c_int = 3;
const ZSTD_NIT_CHECKSUM: c_int = 4;
const ZSTD_NIT_SKIPPABLE_FRAME: c_int = 5;
const LL_BASE: [u32; MAX_LL + 1] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 28, 32, 40, 48, 64,
0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000,
];
const OF_BASE: [u32; MAX_OFF + 1] = [
0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD,
0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD,
0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD,
];
const OF_BITS: [u8; MAX_OFF + 1] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31,
];
const ML_BASE: [u32; MAX_ML + 1] = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203,
0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003,
];
const LL_BITS: [u8; MAX_LL + 1] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16,
];
const ML_BITS: [u8; MAX_ML + 1] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
];
#[repr(C)]
pub struct ZSTD_DCtx {
_private: [u8; 0],
}
pub type ZSTD_DStream = ZSTD_DCtx;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct ZSTD_FrameHeader {
pub frame_content_size: u64,
pub window_size: u64,
pub block_size_max: c_uint,
pub frame_type: c_int,
pub header_size: c_uint,
pub dict_id: c_uint,
pub checksum_flag: c_uint,
pub reserved1: c_uint,
pub reserved2: c_uint,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ZSTD_inBuffer {
pub src: *const c_void,
pub size: usize,
pub pos: usize,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ZSTD_outBuffer {
pub dst: *mut c_void,
pub size: usize,
pub pos: usize,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct ZSTD_bounds {
pub error: usize,
pub lower_bound: c_int,
pub upper_bound: c_int,
}
type ZstdAllocFunction = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
type ZstdFreeFunction = unsafe extern "C" fn(*mut c_void, *mut c_void);
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ZSTD_customMem {
custom_alloc: Option<ZstdAllocFunction>,
custom_free: Option<ZstdFreeFunction>,
opaque: *mut c_void,
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct BlockProperties {
block_type: c_int,
last_block: u32,
orig_size: u32,
}
#[repr(C)]
pub struct ZSTD_entropyDTables_t {
ll_table: [crate::zstd_decompress_block::ZSTD_seqSymbol; 1 + (1 << LL_FSE_LOG)],
of_table: [crate::zstd_decompress_block::ZSTD_seqSymbol; 1 + (1 << OFF_FSE_LOG)],
ml_table: [crate::zstd_decompress_block::ZSTD_seqSymbol; 1 + (1 << ML_FSE_LOG)],
huf_table: [u32; HUF_DTABLE_SIZE],
rep: [u32; ZSTD_REP_NUM],
workspace: [u32; ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32],
}
/// C-provided leaves of `ZSTD_DCtx_s`. Every pointer is produced under the
/// active C preprocessor configuration; Rust never hard-codes a private
/// decoder-context offset.
#[repr(C)]
#[derive(Clone, Copy)]
struct ZSTD_rustDctxView {
dctx: *mut c_void,
llt_ptr: *mut c_void,
mlt_ptr: *mut c_void,
oft_ptr: *mut c_void,
huf_ptr: *mut c_void,
entropy: *mut c_void,
workspace: *mut c_void,
workspace_size: usize,
previous_dst_end: *mut c_void,
prefix_start: *mut c_void,
virtual_start: *mut c_void,
dict_end: *mut c_void,
expected: *mut c_void,
f_params: *mut c_void,
processed_c_size: *mut c_void,
decoded_size: *mut c_void,
b_type: *mut c_void,
stage: *mut c_void,
lit_entropy: *mut c_void,
fse_entropy: *mut c_void,
xxh_state: *mut c_void,
header_size: *mut c_void,
format: *mut c_void,
force_ignore_checksum: *mut c_void,
validate_checksum: *mut c_void,
lit_ptr: *mut c_void,
custom_mem: *mut c_void,
lit_size: *mut c_void,
rle_size: *mut c_void,
static_size: *mut c_void,
is_frame_decompression: *mut c_void,
ddict_local: *mut c_void,
ddict: *mut c_void,
dict_id: *mut c_void,
ddict_is_cold: *mut c_void,
dict_uses: *mut c_void,
ddict_set: *mut c_void,
ref_multiple_ddicts: *mut c_void,
disable_huf_asm: *mut c_void,
max_block_size_param: *mut c_void,
stream_stage: *mut c_void,
in_buff: *mut c_void,
in_buff_size: *mut c_void,
in_pos: *mut c_void,
max_window_size: *mut c_void,
out_buff: *mut c_void,
out_buff_size: *mut c_void,
out_start: *mut c_void,
out_end: *mut c_void,
lh_size: *mut c_void,
legacy_context: *mut c_void,
previous_legacy_version: *mut c_void,
legacy_version: *mut c_void,
hostage_byte: *mut c_void,
no_forward_progress: *mut c_void,
out_buffer_mode: *mut c_void,
expected_out_buffer: *mut c_void,
lit_buffer: *mut c_void,
lit_buffer_end: *mut c_void,
lit_buffer_location: *mut c_void,
lit_extra_buffer: *mut c_void,
lit_extra_buffer_size: usize,
header_buffer: *mut c_void,
header_buffer_size: usize,
oversized_duration: *mut c_void,
fuzz_begin: *mut c_void,
fuzz_end: *mut c_void,
dctx_size: usize,
}
const _: () = {
assert!(offset_of!(ZSTD_rustDctxView, dctx) == 0);
assert!(offset_of!(ZSTD_rustDctxView, static_size) == 29 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustDctxView, dctx_size) == 67 * size_of::<usize>());
assert!(size_of::<ZSTD_rustDctxView>() == 68 * size_of::<usize>());
assert!(size_of::<ZSTD_customMem>() == size_of::<crate::zstd_ddict::ZSTD_customMem>());
assert!(align_of::<ZSTD_customMem>() == align_of::<crate::zstd_ddict::ZSTD_customMem>());
assert!(align_of::<ZSTD_rustDctxView>() == align_of::<usize>());
};
type ZSTD_rustTraceBeginFn = unsafe extern "C" fn(*const c_void) -> u64;
type ZSTD_rustTraceEndFn = unsafe extern "C" fn(u64, *const c_void);
#[repr(C)]
#[derive(Clone, Copy)]
struct ZSTD_rustDctxTraceView {
trace_ctx: *mut u64,
begin: Option<ZSTD_rustTraceBeginFn>,
end: Option<ZSTD_rustTraceEndFn>,
dctx: *const c_void,
ddict: *const c_void,
dictionary_is_cold: c_int,
}
#[repr(C)]
struct ZSTD_rustTrace {
version: c_uint,
streaming: c_int,
dictionary_id: c_uint,
dictionary_is_cold: c_int,
dictionary_size: usize,
uncompressed_size: usize,
compressed_size: usize,
params: *const c_void,
cctx: *const c_void,
dctx: *const c_void,
}
/* Keep this in sync with ZSTD_VERSION_NUMBER in lib/zstd.h. */
const ZSTD_TRACE_VERSION: c_uint = 10_507;
unsafe extern "C" {
fn ZSTD_rust_dctx_view(dctx: *mut ZSTD_DCtx, out: *mut ZSTD_rustDctxView);
fn ZSTD_rust_dctx_trace_view(dctx: *mut ZSTD_DCtx, out: *mut ZSTD_rustDctxTraceView);
fn ZSTD_rust_dctx_sizeof() -> usize;
fn ZSTD_rust_dctx_init_platform(dctx: *mut ZSTD_DCtx);
fn ZSTD_rust_dctx_default_max_window_size() -> usize;
fn ZSTD_rust_no_forward_progress_max() -> c_int;
fn ZSTD_rust_heapmode() -> c_int;
fn ZSTD_rust_decompress_stack(
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize;
#[cfg(all(
not(test),
any(
feature = "legacy-v01",
feature = "legacy-v02",
feature = "legacy-v03",
feature = "legacy-v04",
feature = "legacy-v05",
feature = "legacy-v06",
feature = "legacy-v07"
)
))]
fn ZSTD_rust_legacy_support() -> c_uint;
fn ZSTD_decompressBlock_internal(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
streaming: c_int,
) -> usize;
fn ZSTD_checkContinuity(dctx: *mut ZSTD_DCtx, dst: *const c_void, dst_size: usize);
}
type ZstdDecompressDCtxFn =
unsafe extern "C" fn(*mut ZSTD_DCtx, *mut c_void, usize, *const c_void, usize) -> usize;
#[inline]
unsafe fn field<T: Copy>(slot: *mut c_void) -> T {
unsafe { slot.cast::<T>().read() }
}
#[inline]
unsafe fn set_field<T>(slot: *mut c_void, value: T) {
unsafe { slot.cast::<T>().write(value) }
}
unsafe fn dctx_view(dctx: *mut ZSTD_DCtx) -> ZSTD_rustDctxView {
let mut view = MaybeUninit::<ZSTD_rustDctxView>::zeroed();
unsafe { ZSTD_rust_dctx_view(dctx, view.as_mut_ptr()) };
unsafe { view.assume_init() }
}
unsafe fn dctx_trace_view(dctx: *mut ZSTD_DCtx) -> ZSTD_rustDctxTraceView {
let mut view = MaybeUninit::<ZSTD_rustDctxTraceView>::zeroed();
unsafe { ZSTD_rust_dctx_trace_view(dctx, view.as_mut_ptr()) };
unsafe { view.assume_init() }
}
unsafe fn trace_begin_with_view(view: &ZSTD_rustDctxTraceView) {
if view.trace_ctx.is_null() {
return;
}
let trace_ctx = view
.begin
.map(|begin| unsafe { begin(view.dctx) })
.unwrap_or(0);
unsafe { view.trace_ctx.write(trace_ctx) };
}
unsafe fn trace_end_with_view(
view: &ZSTD_rustDctxTraceView,
uncompressed_size: u64,
compressed_size: u64,
streaming: c_int,
) {
if view.trace_ctx.is_null() {
return;
}
let trace_ctx = unsafe { view.trace_ctx.read() };
if trace_ctx == 0 {
return;
}
let Some(end) = view.end else {
return;
};
let mut trace = ZSTD_rustTrace {
version: ZSTD_TRACE_VERSION,
streaming,
dictionary_id: 0,
dictionary_is_cold: 0,
dictionary_size: 0,
uncompressed_size: uncompressed_size as usize,
compressed_size: compressed_size as usize,
params: ptr::null(),
cctx: ptr::null(),
dctx: view.dctx,
};
if !view.ddict.is_null() {
let ddict = view.ddict.cast::<ZSTD_DDict>();
trace.dictionary_id = unsafe { ZSTD_getDictID_fromDDict(ddict) };
trace.dictionary_size = unsafe { ZSTD_DDict_dictSize(ddict) };
trace.dictionary_is_cold = view.dictionary_is_cold;
}
unsafe { end(trace_ctx, (&mut trace as *mut ZSTD_rustTrace).cast()) };
}
unsafe fn trace_begin(dctx: *mut ZSTD_DCtx) {
let view = unsafe { dctx_trace_view(dctx) };
unsafe { trace_begin_with_view(&view) };
}
unsafe fn trace_end(
dctx: *mut ZSTD_DCtx,
uncompressed_size: u64,
compressed_size: u64,
streaming: c_int,
) {
let view = unsafe { dctx_trace_view(dctx) };
unsafe { trace_end_with_view(&view, uncompressed_size, compressed_size, streaming) };
}
#[inline]
unsafe fn copy_dctx_prefix(dst: *mut u8, src: *const u8, in_buff: *const c_void) {
let to_copy = (in_buff as usize).wrapping_sub(dst as usize);
unsafe { ptr::copy_nonoverlapping(src, dst, to_copy) };
}
#[inline]
fn legacy_empty_const_ptr() -> *const c_void {
ptr::addr_of!(LEGACY_EMPTY_BYTE).cast()
}
#[inline]
fn legacy_empty_mut_ptr() -> *mut c_void {
ptr::addr_of_mut!(LEGACY_EMPTY_BYTE).cast()
}
/* This mirrors ZSTD_freeLegacyStreamContext(), including its deliberately
* ignored error result. The caller owns the projected pointer slot and the
* C helper's historical behavior is to leave that slot untouched while an
* initialization attempt replaces it only after successful creation. */
unsafe fn free_legacy_stream_context(_legacy_context: *mut c_void, version: u32) {
if _legacy_context.is_null() {
return;
}
match version {
#[cfg(feature = "legacy-v04")]
4 => {
let _ = crate::legacy::zstd_v04::ZBUFFv04_freeDCtx(
_legacy_context.cast::<crate::legacy::zstd_v04::ZBUFFv04_DCtx>(),
);
}
#[cfg(feature = "legacy-v05")]
5 => {
let _ = crate::legacy::zstd_v05::ZBUFFv05_freeDCtx(
_legacy_context.cast::<crate::legacy::zstd_v05::ZBUFFv05_DCtx>(),
);
}
#[cfg(feature = "legacy-v06")]
6 => {
let _ = crate::legacy::zstd_v06::ZBUFFv06_freeDCtx(
_legacy_context.cast::<crate::legacy::zstd_v06::ZBUFFv06_DCtx>(),
);
}
#[cfg(feature = "legacy-v07")]
7 => {
let _ = crate::legacy::zstd_v07::ZBUFFv07_freeDCtx(
_legacy_context.cast::<crate::legacy::zstd_v07::ZBUFFv07_DCtx>(),
);
}
_ => {}
}
}
/* The Rust archive used by the C test harness deliberately contains all
* ported legacy modules, even when a particular test target lowers
* ZSTD_LEGACY_SUPPORT. Query the active C setting instead of deriving the
* dispatch boundary from Cargo features alone. Standalone Rust tests have no
* C adapter to query; enabling all configured modules there gives the helper
* tests a deterministic support boundary without changing production builds. */
#[inline]
unsafe fn configured_legacy_support() -> u32 {
#[cfg(test)]
{
1
}
#[cfg(not(test))]
{
#[cfg(any(
feature = "legacy-v01",
feature = "legacy-v02",
feature = "legacy-v03",
feature = "legacy-v04",
feature = "legacy-v05",
feature = "legacy-v06",
feature = "legacy-v07"
))]
{
unsafe { ZSTD_rust_legacy_support() }
}
#[cfg(not(any(
feature = "legacy-v01",
feature = "legacy-v02",
feature = "legacy-v03",
feature = "legacy-v04",
feature = "legacy-v05",
feature = "legacy-v06",
feature = "legacy-v07"
)))]
{
0
}
}
}
/* This is the Rust equivalent of ZSTD_isLegacy(). The v0.1 magic is the one
* historical exception: its bytes are checked as the little-endian constant
* 0x1EB52FFD, while v0.2 through v0.7 use the usual 0xFD2FB52N values. */
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_is(src: *const c_void, src_size: usize) -> c_uint {
if src.is_null() || src_size < ZSTD_FRAMEIDSIZE {
return 0;
}
let support = unsafe { configured_legacy_support() };
if support == 0 {
return 0;
}
let _magic = unsafe { MEM_readLE32(src) };
#[cfg(feature = "legacy-v01")]
if support <= 1 && _magic == 0x1EB5_2FFD {
return 1;
}
#[cfg(feature = "legacy-v02")]
if support <= 2 && _magic == 0xFD2F_B522 {
return 2;
}
#[cfg(feature = "legacy-v03")]
if support <= 3 && _magic == 0xFD2F_B523 {
return 3;
}
#[cfg(feature = "legacy-v04")]
if support <= 4 && _magic == 0xFD2F_B524 {
return 4;
}
#[cfg(feature = "legacy-v05")]
if support <= 5 && _magic == 0xFD2F_B525 {
return 5;
}
#[cfg(feature = "legacy-v06")]
if support <= 6 && _magic == 0xFD2F_B526 {
return 6;
}
#[cfg(feature = "legacy-v07")]
if support <= 7 && _magic == 0xFD2F_B527 {
return 7;
}
0
}
#[inline]
unsafe fn legacy_frame_size_info_values(
src: *const c_void,
src_size: usize,
) -> (usize, u64, usize) {
let version = unsafe { ZSTD_rust_legacy_is(src, src_size) };
let mut compressed_size = ERROR(ZstdErrorCode::PrefixUnknown);
let mut decompressed_bound = ZSTD_CONTENTSIZE_ERROR;
match version {
#[cfg(feature = "legacy-v01")]
1 => unsafe {
crate::legacy::zstd_v01::ZSTDv01_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v02")]
2 => unsafe {
crate::legacy::zstd_v02::ZSTDv02_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v03")]
3 => unsafe {
crate::legacy::zstd_v03::ZSTDv03_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v04")]
4 => unsafe {
crate::legacy::zstd_v04::ZSTDv04_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v05")]
5 => unsafe {
crate::legacy::zstd_v05::ZSTDv05_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v06")]
6 => unsafe {
crate::legacy::zstd_v06::ZSTDv06_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v07")]
7 => unsafe {
crate::legacy::zstd_v07::ZSTDv07_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
_ => {}
}
if !ERR_isError(compressed_size) && compressed_size > src_size {
compressed_size = ERROR(ZstdErrorCode::SrcSizeWrong);
decompressed_bound = ZSTD_CONTENTSIZE_ERROR;
}
let nb_blocks = if decompressed_bound == ZSTD_CONTENTSIZE_ERROR {
0
} else {
debug_assert_eq!(decompressed_bound & (ZSTD_BLOCKSIZE_MAX as u64 - 1), 0);
(decompressed_bound / ZSTD_BLOCKSIZE_MAX as u64) as usize
};
(compressed_size, decompressed_bound, nb_blocks)
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_get_decompressed_size(
src: *const c_void,
src_size: usize,
) -> u64 {
let version = unsafe { ZSTD_rust_legacy_is(src, src_size) };
match version {
#[cfg(feature = "legacy-v05")]
5 => {
/* ZstdParameters is repr(C), and src_size is its first field. The
* field is private inside the frozen module because it is not part
* of that module's public Rust API, so read the documented C ABI
* prefix only after the version-specific parser succeeds. */
let mut params = MaybeUninit::<crate::legacy::zstd_v05::ZstdParameters>::zeroed();
let result = unsafe {
crate::legacy::zstd_v05::ZSTDv05_getFrameParams(params.as_mut_ptr(), src, src_size)
};
if result == 0 {
unsafe { params.as_ptr().cast::<u64>().read() }
} else {
0
}
}
#[cfg(feature = "legacy-v06")]
6 => {
let mut params = crate::legacy::zstd_v06::ZSTDv06_frameParams {
frame_content_size: 0,
window_log: 0,
};
let result = unsafe {
crate::legacy::zstd_v06::ZSTDv06_getFrameParams(&mut params, src, src_size)
};
if result == 0 {
params.frame_content_size
} else {
0
}
}
#[cfg(feature = "legacy-v07")]
7 => {
let mut params = crate::legacy::zstd_v07::ZSTDv07_frameParams {
frameContentSize: 0,
windowSize: 0,
dictID: 0,
checksumFlag: 0,
};
let result = unsafe {
crate::legacy::zstd_v07::ZSTDv07_getFrameParams(&mut params, src, src_size)
};
if result == 0 {
params.frameContentSize
} else {
0
}
}
_ => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_find_compressed_size(
src: *const c_void,
src_size: usize,
) -> usize {
unsafe { legacy_frame_size_info_values(src, src_size).0 }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_frame_size_info(
src: *const c_void,
src_size: usize,
compressed_size: *mut usize,
decompressed_bound: *mut u64,
nb_blocks: *mut usize,
) -> usize {
let (c_size, d_bound, blocks) = unsafe { legacy_frame_size_info_values(src, src_size) };
unsafe {
compressed_size.write(c_size);
decompressed_bound.write(d_bound);
nb_blocks.write(blocks);
}
if ERR_isError(c_size) {
c_size
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_decompress(
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
) -> usize {
let version = unsafe { ZSTD_rust_legacy_is(src, src_size) };
let _dst = if dst.is_null() {
debug_assert_eq!(dst_capacity, 0);
legacy_empty_mut_ptr()
} else {
dst
};
let _src = if src.is_null() {
debug_assert_eq!(src_size, 0);
legacy_empty_const_ptr()
} else {
src
};
let _dict = if dict.is_null() {
debug_assert_eq!(dict_size, 0);
legacy_empty_const_ptr()
} else {
dict
};
match version {
#[cfg(feature = "legacy-v01")]
1 => unsafe {
crate::legacy::zstd_v01::ZSTDv01_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v02")]
2 => unsafe {
crate::legacy::zstd_v02::ZSTDv02_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v03")]
3 => unsafe {
crate::legacy::zstd_v03::ZSTDv03_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v04")]
4 => unsafe {
crate::legacy::zstd_v04::ZSTDv04_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v05")]
5 => {
let dctx = unsafe { crate::legacy::zstd_v05::ZSTDv05_createDCtx() };
if dctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe {
crate::legacy::zstd_v05::ZSTDv05_decompress_usingDict(
dctx,
_dst,
dst_capacity,
_src,
src_size,
_dict,
dict_size,
)
};
unsafe { crate::legacy::zstd_v05::ZSTDv05_freeDCtx(dctx) };
result
}
#[cfg(feature = "legacy-v06")]
6 => {
let dctx = unsafe { crate::legacy::zstd_v06::ZSTDv06_createDCtx() };
if dctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe {
crate::legacy::zstd_v06::ZSTDv06_decompress_usingDict(
dctx,
_dst,
dst_capacity,
_src,
src_size,
_dict,
dict_size,
)
};
unsafe { crate::legacy::zstd_v06::ZSTDv06_freeDCtx(dctx) };
result
}
#[cfg(feature = "legacy-v07")]
7 => {
let dctx = unsafe { crate::legacy::zstd_v07::ZSTDv07_createDCtx() };
if dctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe {
crate::legacy::zstd_v07::ZSTDv07_decompress_usingDict(
dctx,
_dst,
dst_capacity,
_src,
src_size,
_dict,
dict_size,
)
};
unsafe { crate::legacy::zstd_v07::ZSTDv07_freeDCtx(dctx) };
result
}
_ => ERROR(ZstdErrorCode::PrefixUnknown),
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_free_stream(dctx: *mut ZSTD_DCtx) {
if dctx.is_null() {
return;
}
let view = unsafe { dctx_view(dctx) };
if view.legacy_context.is_null() {
return;
}
let legacy_context = unsafe { field::<*mut c_void>(view.legacy_context) };
let previous_version = unsafe { field::<u32>(view.previous_legacy_version) };
unsafe { free_legacy_stream_context(legacy_context, previous_version) };
unsafe { set_field(view.legacy_context, ptr::null_mut::<c_void>()) };
}
/* This is the narrow Rust equivalent of ZSTD_initLegacyStream(). It only
* touches the three legacy projections in ZSTD_rustDctxView; the enclosing
* decoder context and every version-specific stream context remain opaque to
* Rust callers outside this function. */
unsafe fn init_legacy_stream(
legacy_context_slot: *mut c_void,
previous_version: u32,
new_version: u32,
dict: *const c_void,
dict_size: usize,
) -> usize {
let _dict = if dict.is_null() {
debug_assert_eq!(dict_size, 0);
legacy_empty_const_ptr()
} else {
dict
};
if previous_version != new_version {
let legacy_context = field::<*mut c_void>(legacy_context_slot);
free_legacy_stream_context(legacy_context, previous_version);
}
match new_version {
/* The old formats can be recognized by ZSTD_isLegacy(), but their
* buffered streaming API was never supported by this dispatcher. */
1..=3 => 0,
#[cfg(feature = "legacy-v04")]
4 => {
let context = if previous_version == new_version {
field::<*mut c_void>(legacy_context_slot)
.cast::<crate::legacy::zstd_v04::ZBUFFv04_DCtx>()
} else {
crate::legacy::zstd_v04::ZBUFFv04_createDCtx()
};
if context.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let _ = crate::legacy::zstd_v04::ZBUFFv04_decompressInit(context);
let _ = crate::legacy::zstd_v04::ZBUFFv04_decompressWithDictionary(
context, _dict, dict_size,
);
set_field(legacy_context_slot, context.cast::<c_void>());
0
}
#[cfg(feature = "legacy-v05")]
5 => {
let context = if previous_version == new_version {
field::<*mut c_void>(legacy_context_slot)
.cast::<crate::legacy::zstd_v05::ZBUFFv05_DCtx>()
} else {
crate::legacy::zstd_v05::ZBUFFv05_createDCtx()
};
if context.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let _ = crate::legacy::zstd_v05::ZBUFFv05_decompressInitDictionary(
context, _dict, dict_size,
);
set_field(legacy_context_slot, context.cast::<c_void>());
0
}
#[cfg(feature = "legacy-v06")]
6 => {
let context = if previous_version == new_version {
field::<*mut c_void>(legacy_context_slot)
.cast::<crate::legacy::zstd_v06::ZBUFFv06_DCtx>()
} else {
crate::legacy::zstd_v06::ZBUFFv06_createDCtx()
};
if context.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let _ = crate::legacy::zstd_v06::ZBUFFv06_decompressInitDictionary(
context, _dict, dict_size,
);
set_field(legacy_context_slot, context.cast::<c_void>());
0
}
#[cfg(feature = "legacy-v07")]
7 => {
let context = if previous_version == new_version {
field::<*mut c_void>(legacy_context_slot)
.cast::<crate::legacy::zstd_v07::ZBUFFv07_DCtx>()
} else {
crate::legacy::zstd_v07::ZBUFFv07_createDCtx()
};
if context.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let _ = crate::legacy::zstd_v07::ZBUFFv07_decompressInitDictionary(
context, _dict, dict_size,
);
set_field(legacy_context_slot, context.cast::<c_void>());
0
}
/* Keep the C helper's default behavior: initialization itself is
* successful, and the subsequent stream dispatch reports the
* unsupported version. */
_ => 0,
}
}
/* This mirrors ZSTD_decompressLegacyStream(). In particular, legacy
* buffered decoders receive only the remaining input/output slices and then
* return the amount they consumed/produced through the public buffer
* positions. */
unsafe fn decompress_legacy_stream(
_legacy_context: *mut c_void,
version: u32,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
) -> usize {
if (*output).dst.is_null() {
debug_assert_eq!((*output).size, 0);
(*output).dst = legacy_empty_mut_ptr();
}
if (*input).src.is_null() {
debug_assert_eq!((*input).size, 0);
(*input).src = legacy_empty_const_ptr();
}
match version {
#[cfg(feature = "legacy-v04")]
4 => {
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
let mut read_size = (*input).size.wrapping_sub((*input).pos);
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
let hint = crate::legacy::zstd_v04::ZBUFFv04_decompressContinue(
_legacy_context.cast(),
dst.cast(),
&mut decoded_size,
src.cast(),
&mut read_size,
);
(*output).pos = (*output).pos.wrapping_add(decoded_size);
(*input).pos = (*input).pos.wrapping_add(read_size);
hint
}
#[cfg(feature = "legacy-v05")]
5 => {
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
let mut read_size = (*input).size.wrapping_sub((*input).pos);
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
let hint = crate::legacy::zstd_v05::ZBUFFv05_decompressContinue(
_legacy_context.cast(),
dst.cast(),
&mut decoded_size,
src.cast(),
&mut read_size,
);
(*output).pos = (*output).pos.wrapping_add(decoded_size);
(*input).pos = (*input).pos.wrapping_add(read_size);
hint
}
#[cfg(feature = "legacy-v06")]
6 => {
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
let mut read_size = (*input).size.wrapping_sub((*input).pos);
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
let hint = crate::legacy::zstd_v06::ZBUFFv06_decompressContinue(
_legacy_context.cast(),
dst.cast(),
&mut decoded_size,
src.cast(),
&mut read_size,
);
(*output).pos = (*output).pos.wrapping_add(decoded_size);
(*input).pos = (*input).pos.wrapping_add(read_size);
hint
}
#[cfg(feature = "legacy-v07")]
7 => {
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
let mut read_size = (*input).size.wrapping_sub((*input).pos);
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
let hint = crate::legacy::zstd_v07::ZBUFFv07_decompressContinue(
_legacy_context.cast(),
dst.cast(),
&mut decoded_size,
src.cast(),
&mut read_size,
);
(*output).pos = (*output).pos.wrapping_add(decoded_size);
(*input).pos = (*input).pos.wrapping_add(read_size);
hint
}
_ => ERROR(ZstdErrorCode::VersionUnsupported),
}
}
#[inline]
unsafe fn reset_legacy_stage_if_done(stream_stage: *mut c_void, hint: usize) -> usize {
if hint == 0 {
set_field(stream_stage, ZDSS_INIT);
}
hint
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_decompress_stream(
dctx: *mut ZSTD_DCtx,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
dict: *const c_void,
dict_size: usize,
) -> usize {
let view = dctx_view(dctx);
/* With ZSTD_LEGACY_SUPPORT disabled, the C projection deliberately leaves
* these slots null. This is the exact no-legacy-support fallback. */
if view.legacy_version.is_null() {
return ERROR(ZstdErrorCode::PrefixUnknown);
}
let current_version = field::<u32>(view.legacy_version);
if current_version != 0 {
let hint = decompress_legacy_stream(
field::<*mut c_void>(view.legacy_context),
current_version,
output,
input,
);
return reset_legacy_stage_if_done(view.stream_stage, hint);
}
let input_ref = &*input;
let istart = if input_ref.pos != 0 {
input_ref
.src
.cast::<u8>()
.wrapping_add(input_ref.pos)
.cast()
} else {
input_ref.src
};
let input_size = input_ref.size.wrapping_sub(input_ref.pos);
let legacy_version = ZSTD_rust_legacy_is(istart, input_size);
if legacy_version == 0 {
return ERROR(ZstdErrorCode::PrefixUnknown);
}
if field::<usize>(view.static_size) != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let previous_version = field::<u32>(view.previous_legacy_version);
let init = init_legacy_stream(
view.legacy_context,
previous_version,
legacy_version,
dict,
dict_size,
);
if ERR_isError(init) {
return init;
}
set_field(view.legacy_version, legacy_version);
set_field(view.previous_legacy_version, legacy_version);
let hint = decompress_legacy_stream(
field::<*mut c_void>(view.legacy_context),
legacy_version,
output,
input,
);
reset_legacy_stage_if_done(view.stream_stage, hint)
}
#[cfg(test)]
mod legacy_stream_dispatch_tests {
use super::*;
#[test]
fn rejects_unsupported_versions_after_normalizing_empty_buffers() {
let mut output = ZSTD_outBuffer {
dst: ptr::null_mut(),
size: 0,
pos: 0,
};
let mut input = ZSTD_inBuffer {
src: ptr::null(),
size: 0,
pos: 0,
};
let result =
unsafe { decompress_legacy_stream(ptr::null_mut(), 3, &mut output, &mut input) };
assert_eq!(result, ERROR(ZstdErrorCode::VersionUnsupported));
assert!(!output.dst.is_null());
assert!(!input.src.is_null());
assert_eq!(output.pos, 0);
assert_eq!(input.pos, 0);
}
#[test]
fn resets_stage_only_for_a_completed_legacy_call() {
let mut stage = ZDSS_READ;
let stage_slot = (&mut stage as *mut c_int).cast::<c_void>();
assert_eq!(unsafe { reset_legacy_stage_if_done(stage_slot, 0) }, 0);
assert_eq!(stage, ZDSS_INIT);
stage = ZDSS_READ;
assert_eq!(unsafe { reset_legacy_stage_if_done(stage_slot, 7) }, 7);
assert_eq!(stage, ZDSS_READ);
}
#[cfg(all(feature = "legacy-v04", feature = "legacy-v05"))]
#[test]
fn switches_and_reuses_versioned_stream_contexts() {
let mut context = ptr::null_mut::<c_void>();
let context_slot = (&mut context as *mut *mut c_void).cast::<c_void>();
unsafe {
assert_eq!(init_legacy_stream(context_slot, 0, 4, ptr::null(), 0), 0);
let v04_context = context;
assert!(!v04_context.is_null());
assert_eq!(init_legacy_stream(context_slot, 4, 4, ptr::null(), 0), 0);
assert_eq!(context, v04_context);
assert_eq!(init_legacy_stream(context_slot, 4, 5, ptr::null(), 0), 0);
assert!(!context.is_null());
free_legacy_stream_context(context, 5);
}
}
#[cfg(feature = "legacy-v04")]
#[test]
fn tracks_partial_input_and_output_positions() {
const FRAME: &[u8] = &[
0x24, 0xB5, 0x2F, 0xFD, 0x00, 0x40, 0x00, 0x0B, b'r', b'a', b'w', b' ', b'v', b'0',
b'.', b'4', b'!', b'!', b'!', 0xC0, 0x00, 0x00,
];
let expected = b"raw v0.4!!!";
let mut context = ptr::null_mut::<c_void>();
let context_slot = (&mut context as *mut *mut c_void).cast::<c_void>();
unsafe {
assert_eq!(init_legacy_stream(context_slot, 0, 4, ptr::null(), 0), 0);
}
let mut input_pos = 0;
let mut decoded = Vec::new();
for _ in 0..128 {
let input_end = min(FRAME.len(), input_pos + 1);
let mut input = ZSTD_inBuffer {
src: FRAME.as_ptr().cast(),
size: input_end,
pos: input_pos,
};
let mut chunk = [0u8; 2];
let mut output = ZSTD_outBuffer {
dst: chunk.as_mut_ptr().cast(),
size: chunk.len(),
pos: 0,
};
let old_input_pos = input.pos;
let hint = unsafe { decompress_legacy_stream(context, 4, &mut output, &mut input) };
assert!(!ERR_isError(hint));
assert!(input.pos > old_input_pos || output.pos != 0 || input_end == FRAME.len());
input_pos = input.pos;
decoded.extend_from_slice(&chunk[..output.pos]);
if input_pos == FRAME.len() && decoded == expected {
break;
}
}
assert_eq!(input_pos, FRAME.len());
assert_eq!(decoded, expected);
unsafe { free_legacy_stream_context(context, 4) };
}
}
#[cfg(test)]
mod legacy_dispatch_helper_tests {
use super::*;
#[test]
fn legacy_detection_rejects_short_null_and_unknown_input() {
assert_eq!(unsafe { ZSTD_rust_legacy_is(ptr::null(), 0) }, 0);
let short = [0x27, 0xB5, 0x2F];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(short.as_ptr().cast(), short.len()) },
0
);
let unknown = [0u8; ZSTD_FRAMEIDSIZE];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(unknown.as_ptr().cast(), unknown.len()) },
0
);
}
#[cfg(feature = "legacy-v01")]
#[test]
fn detects_v01_magic() {
let magic: [u8; 4] = [0xFD, 0x2F, 0xB5, 0x1E];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
1
);
}
#[cfg(feature = "legacy-v02")]
#[test]
fn detects_v02_magic() {
let magic: [u8; 4] = [0x22, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
2
);
}
#[cfg(feature = "legacy-v03")]
#[test]
fn detects_v03_magic() {
let magic: [u8; 4] = [0x23, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
3
);
}
#[cfg(feature = "legacy-v04")]
#[test]
fn detects_v04_magic() {
let magic: [u8; 4] = [0x24, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
4
);
}
#[cfg(feature = "legacy-v05")]
#[test]
fn detects_v05_magic() {
let magic: [u8; 4] = [0x25, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
5
);
}
#[cfg(feature = "legacy-v06")]
#[test]
fn detects_v06_magic() {
let magic: [u8; 4] = [0x26, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
6
);
}
#[cfg(feature = "legacy-v07")]
#[test]
fn legacy_v07_size_and_one_shot_helpers_match_frame() {
let payload = b"legacy-dispatch";
let mut frame = Vec::with_capacity(6 + 3 + payload.len() + 3);
frame.extend_from_slice(&[0x27, 0xB5, 0x2F, 0xFD]);
frame.extend_from_slice(&[0x20, payload.len() as u8]);
frame.extend_from_slice(&[
0x40 | (((payload.len() >> 16) & 7) as u8),
(payload.len() >> 8) as u8,
payload.len() as u8,
]);
frame.extend_from_slice(payload);
frame.extend_from_slice(&[0xC0, 0, 0]);
let mut compressed_size = 0;
let mut decompressed_bound = 0;
let mut nb_blocks = 0;
let status = unsafe {
ZSTD_rust_legacy_frame_size_info(
frame.as_ptr().cast(),
frame.len(),
&mut compressed_size,
&mut decompressed_bound,
&mut nb_blocks,
)
};
assert_eq!(status, 0);
assert_eq!(compressed_size, frame.len());
assert_eq!(
decompressed_bound, ZSTD_BLOCKSIZE_MAX as u64,
"legacy frame bounds are reported in whole legacy blocks"
);
assert_eq!(nb_blocks, 1);
assert_eq!(
unsafe { ZSTD_rust_legacy_find_compressed_size(frame.as_ptr().cast(), frame.len()) },
frame.len()
);
assert_eq!(
unsafe { ZSTD_rust_legacy_get_decompressed_size(frame.as_ptr().cast(), frame.len()) },
payload.len() as u64
);
let mut output = vec![0u8; payload.len()];
let decoded = unsafe {
ZSTD_rust_legacy_decompress(
output.as_mut_ptr().cast(),
output.len(),
frame.as_ptr().cast(),
frame.len(),
ptr::null(),
0,
)
};
assert_eq!(decoded, payload.len());
assert_eq!(output, payload);
}
#[cfg(feature = "legacy-v07")]
#[test]
fn legacy_size_and_decompress_helpers_report_truncation() {
let mut truncated = vec![0x27, 0xB5, 0x2F, 0xFD, 0x20, 4, 0x40, 0, 4, b't'];
let mut compressed_size = 0;
let mut decompressed_bound = 0;
let mut nb_blocks = 0;
let status = unsafe {
ZSTD_rust_legacy_frame_size_info(
truncated.as_ptr().cast(),
truncated.len(),
&mut compressed_size,
&mut decompressed_bound,
&mut nb_blocks,
)
};
assert_eq!(status, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!(compressed_size, status);
assert_eq!(decompressed_bound, ZSTD_CONTENTSIZE_ERROR);
assert_eq!(nb_blocks, 0);
assert_eq!(
unsafe {
ZSTD_rust_legacy_find_compressed_size(truncated.as_ptr().cast(), truncated.len())
},
status
);
let mut output = [0u8; 4];
let decoded = unsafe {
ZSTD_rust_legacy_decompress(
output.as_mut_ptr().cast(),
output.len(),
truncated.as_ptr().cast(),
truncated.len(),
ptr::null(),
0,
)
};
assert!(ERR_isError(decoded));
truncated[0] ^= 1;
assert_eq!(
unsafe {
ZSTD_rust_legacy_get_decompressed_size(truncated.as_ptr().cast(), truncated.len())
},
0
);
}
}
#[inline]
fn frame_header_prefix(format: c_int) -> usize {
if format == ZSTD_F_ZSTD1 {
5
} else {
1
}
}
#[inline]
fn frame_header_min(format: c_int) -> usize {
if format == ZSTD_F_ZSTD1 {
6
} else {
2
}
}
#[inline]
fn window_log_max() -> usize {
if MEM_32bits() {
ZSTD_WINDOWLOG_MAX_32
} else {
ZSTD_WINDOWLOG_MAX_64
}
}
#[inline]
unsafe fn const_ptr_add(ptr: *const u8, amount: usize) -> *const u8 {
if ptr.is_null() {
debug_assert_eq!(amount, 0);
ptr
} else {
unsafe { ptr.add(amount) }
}
}
#[inline]
unsafe fn ptr_distance(end: *const u8, start: *const u8) -> usize {
(end as usize).wrapping_sub(start as usize)
}
#[inline]
unsafe fn copy_bytes(dst: *mut u8, src: *const u8, len: usize) {
if len != 0 {
unsafe { ptr::copy(src, dst, len) };
}
}
#[inline]
unsafe fn limit_copy(dst: *mut u8, dst_capacity: usize, src: *const u8, src_size: usize) -> usize {
let len = min(dst_capacity, src_size);
if len != 0 {
unsafe { ptr::copy_nonoverlapping(src, dst, len) };
}
len
}
#[inline]
fn default_custom_mem() -> ZSTD_customMem {
ZSTD_customMem {
custom_alloc: None,
custom_free: None,
opaque: ptr::null_mut(),
}
}
#[inline]
fn custom_mem_valid(custom_mem: ZSTD_customMem) -> bool {
custom_mem.custom_alloc.is_some() == custom_mem.custom_free.is_some()
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_dctx_alloc(custom_mem: ZSTD_customMem) -> *mut ZSTD_DCtx {
if !custom_mem_valid(custom_mem) {
return ptr::null_mut();
}
let dctx_size = unsafe { ZSTD_rust_dctx_sizeof() };
unsafe { ZSTD_rust_custom_malloc(dctx_size, custom_mem) }.cast()
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_dctx_free_storage(
dctx: *mut ZSTD_DCtx,
custom_mem: ZSTD_customMem,
) {
unsafe { ZSTD_rust_custom_free(dctx.cast(), custom_mem) };
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_custom_malloc(
size: usize,
custom_mem: ZSTD_customMem,
) -> *mut c_void {
match custom_mem.custom_alloc {
Some(alloc) => unsafe { alloc(custom_mem.opaque, size) },
None => unsafe { libc::malloc(size) },
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_custom_calloc(
size: usize,
custom_mem: ZSTD_customMem,
) -> *mut c_void {
match custom_mem.custom_alloc {
Some(alloc) => {
let allocation = unsafe { alloc(custom_mem.opaque, size) };
if !allocation.is_null() {
unsafe { ptr::write_bytes(allocation.cast::<u8>(), 0, size) };
}
allocation
}
None => unsafe { libc::calloc(1, size) },
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_custom_free(
allocation: *mut c_void,
custom_mem: ZSTD_customMem,
) {
if allocation.is_null() {
return;
}
match custom_mem.custom_free {
Some(free) => unsafe { free(custom_mem.opaque, allocation) },
None => unsafe { libc::free(allocation) },
}
}
#[cfg(test)]
mod allocation_tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
struct AllocationStats {
allocations: AtomicUsize,
frees: AtomicUsize,
}
unsafe extern "C" fn tracking_alloc(opaque: *mut c_void, size: usize) -> *mut c_void {
let stats = unsafe { &*opaque.cast::<AllocationStats>() };
stats.allocations.fetch_add(1, Ordering::Relaxed);
let allocation = unsafe { libc::malloc(size) };
if !allocation.is_null() {
unsafe { ptr::write_bytes(allocation.cast::<u8>(), 0xA5, size) };
}
allocation
}
unsafe extern "C" fn tracking_free(opaque: *mut c_void, allocation: *mut c_void) {
let stats = unsafe { &*opaque.cast::<AllocationStats>() };
stats.frees.fetch_add(1, Ordering::Relaxed);
unsafe { libc::free(allocation) };
}
fn custom_mem(stats: &AllocationStats) -> ZSTD_customMem {
ZSTD_customMem {
custom_alloc: Some(tracking_alloc),
custom_free: Some(tracking_free),
opaque: (stats as *const AllocationStats).cast_mut().cast(),
}
}
#[test]
fn default_allocation_uses_libc_and_zeroes_calloc() {
let default_mem = default_custom_mem();
unsafe {
let allocation = ZSTD_rust_custom_malloc(32, default_mem);
assert!(!allocation.is_null());
ZSTD_rust_custom_free(allocation, default_mem);
let zeroed = ZSTD_rust_custom_calloc(32, default_mem);
assert!(!zeroed.is_null());
let bytes = std::slice::from_raw_parts(zeroed.cast::<u8>(), 32);
assert!(bytes.iter().all(|&byte| byte == 0));
ZSTD_rust_custom_free(zeroed, default_mem);
ZSTD_rust_custom_free(ptr::null_mut(), default_mem);
}
}
#[test]
fn custom_allocation_calls_callbacks_and_zeroes_calloc() {
let stats = AllocationStats {
allocations: AtomicUsize::new(0),
frees: AtomicUsize::new(0),
};
let custom_mem = custom_mem(&stats);
unsafe {
let allocation = ZSTD_rust_custom_malloc(32, custom_mem);
assert!(!allocation.is_null());
assert_eq!(stats.allocations.load(Ordering::Relaxed), 1);
let zeroed = ZSTD_rust_custom_calloc(32, custom_mem);
assert!(!zeroed.is_null());
assert_eq!(stats.allocations.load(Ordering::Relaxed), 2);
let bytes = std::slice::from_raw_parts(zeroed.cast::<u8>(), 32);
assert!(bytes.iter().all(|&byte| byte == 0));
ZSTD_rust_custom_free(allocation, custom_mem);
ZSTD_rust_custom_free(zeroed, custom_mem);
ZSTD_rust_custom_free(ptr::null_mut(), custom_mem);
}
assert_eq!(stats.frees.load(Ordering::Relaxed), 2);
}
#[test]
fn dctx_allocation_rejects_mismatched_callback_pairs() {
let invalid_alloc_only = ZSTD_customMem {
custom_alloc: Some(tracking_alloc),
custom_free: None,
opaque: ptr::null_mut(),
};
let invalid_free_only = ZSTD_customMem {
custom_alloc: None,
custom_free: Some(tracking_free),
opaque: ptr::null_mut(),
};
assert!(!custom_mem_valid(invalid_alloc_only));
assert!(!custom_mem_valid(invalid_free_only));
}
}
#[inline]
unsafe fn get_frame_header_ptr(view: &ZSTD_rustDctxView) -> *mut ZSTD_FrameHeader {
view.f_params.cast()
}
#[inline]
unsafe fn entropy_ptr(view: &ZSTD_rustDctxView) -> *mut ZSTD_entropyDTables_t {
view.entropy.cast()
}
#[inline]
unsafe fn dctx_custom_mem(view: &ZSTD_rustDctxView) -> ZSTD_customMem {
unsafe { field(view.custom_mem) }
}
#[inline]
unsafe fn ddict_custom_mem(view: &ZSTD_rustDctxView) -> crate::zstd_ddict::ZSTD_customMem {
unsafe { std::mem::transmute(dctx_custom_mem(view)) }
}
#[inline]
unsafe fn dctx_ddict(view: &ZSTD_rustDctxView) -> *const ZSTD_DDict {
unsafe { field(view.ddict) }
}
#[inline]
unsafe fn set_dctx_ddict(view: &ZSTD_rustDctxView, ddict: *const ZSTD_DDict) {
unsafe { set_field(view.ddict, ddict) }
}
#[inline]
unsafe fn dctx_ddict_local(view: &ZSTD_rustDctxView) -> *mut ZSTD_DDict {
unsafe { field(view.ddict_local) }
}
#[inline]
unsafe fn set_dctx_ddict_local(view: &ZSTD_rustDctxView, ddict: *mut ZSTD_DDict) {
unsafe { set_field(view.ddict_local, ddict) }
}
#[inline]
unsafe fn get_pointer(slot: *mut c_void) -> *const u8 {
unsafe { field(slot) }
}
#[inline]
unsafe fn set_pointer(slot: *mut c_void, value: *const u8) {
unsafe { set_field(slot, value) }
}
#[inline]
unsafe fn get_mut_pointer(slot: *mut c_void) -> *mut u8 {
unsafe { field(slot) }
}
#[inline]
unsafe fn set_mut_pointer(slot: *mut c_void, value: *mut u8) {
unsafe { set_field(slot, value) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_isFrame(buffer: *const c_void, size: usize) -> c_uint {
if size < ZSTD_FRAMEIDSIZE || buffer.is_null() {
return 0;
}
let magic = unsafe { MEM_readLE32(buffer) };
if magic == ZSTD_MAGICNUMBER
|| (magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START
{
return 1;
}
unsafe { ZSTD_rust_legacy_is(buffer, size) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_isSkippableFrame(buffer: *const c_void, size: usize) -> c_uint {
if size < ZSTD_FRAMEIDSIZE || buffer.is_null() {
return 0;
}
u32::from(
unsafe { MEM_readLE32(buffer) } & ZSTD_MAGIC_SKIPPABLE_MASK == ZSTD_MAGIC_SKIPPABLE_START,
)
}
unsafe fn frame_header_size_internal(src: *const u8, src_size: usize, format: c_int) -> usize {
let min_input_size = frame_header_prefix(format);
if src_size < min_input_size {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let fhd = unsafe { *src.add(min_input_size - 1) };
let dict_id = fhd & 3;
let single_segment = (fhd >> 5) & 1;
let fcs_id = fhd >> 6;
let did_size = [0usize, 1, 2, 4][dict_id as usize];
let fcs_size = [0usize, 2, 4, 8][fcs_id as usize];
min_input_size
+ usize::from(single_segment == 0)
+ did_size
+ fcs_size
+ usize::from(single_segment != 0 && fcs_id == 0)
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_frameHeaderSize(src: *const c_void, src_size: usize) -> usize {
unsafe { frame_header_size_internal(src.cast(), src_size, ZSTD_F_ZSTD1) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_getFrameHeader_advanced(
zfh: *mut ZSTD_FrameHeader,
src: *const c_void,
src_size: usize,
format: c_int,
) -> usize {
let min_input_size = frame_header_prefix(format);
if src_size != 0 && src.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if src_size < min_input_size {
if src_size != 0 && format != ZSTD_F_ZSTD1_MAGICLESS {
let mut header = ZSTD_MAGICNUMBER.to_le_bytes();
unsafe {
ptr::copy_nonoverlapping(src.cast::<u8>(), header.as_mut_ptr(), min(4, src_size))
};
if u32::from_le_bytes(header) != ZSTD_MAGICNUMBER {
header = ZSTD_MAGIC_SKIPPABLE_START.to_le_bytes();
unsafe {
ptr::copy_nonoverlapping(
src.cast::<u8>(),
header.as_mut_ptr(),
min(4, src_size),
)
};
if u32::from_le_bytes(header) & ZSTD_MAGIC_SKIPPABLE_MASK
!= ZSTD_MAGIC_SKIPPABLE_START
{
return ERROR(ZstdErrorCode::PrefixUnknown);
}
}
}
return min_input_size;
}
if zfh.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { zfh.write(ZSTD_FrameHeader::default()) };
let ip = src.cast::<u8>();
if format != ZSTD_F_ZSTD1_MAGICLESS && unsafe { MEM_readLE32(src) } != ZSTD_MAGICNUMBER {
let magic = unsafe { MEM_readLE32(src) };
if magic & ZSTD_MAGIC_SKIPPABLE_MASK == ZSTD_MAGIC_SKIPPABLE_START {
if src_size < ZSTD_SKIPPABLEHEADERSIZE {
return ZSTD_SKIPPABLEHEADERSIZE;
}
unsafe {
(*zfh).frame_type = ZSTD_SKIPPABLE_FRAME;
(*zfh).dict_id = magic - ZSTD_MAGIC_SKIPPABLE_START;
(*zfh).header_size = ZSTD_SKIPPABLEHEADERSIZE as c_uint;
(*zfh).frame_content_size = MEM_readLE32(ip.add(ZSTD_FRAMEIDSIZE).cast()) as u64;
}
return 0;
}
return ERROR(ZstdErrorCode::PrefixUnknown);
}
let fh_size = unsafe { frame_header_size_internal(ip, src_size, format) };
if ERR_isError(fh_size) {
return fh_size;
}
if src_size < fh_size {
return fh_size;
}
unsafe { (*zfh).header_size = fh_size as c_uint };
let fhd = unsafe { *ip.add(min_input_size - 1) };
if fhd & 0x08 != 0 {
return ERROR(ZstdErrorCode::FrameParameterUnsupported);
}
let dict_id_size_code = fhd & 3;
let checksum_flag = (fhd >> 2) & 1;
let single_segment = (fhd >> 5) & 1;
let fcs_id = fhd >> 6;
let mut pos = min_input_size;
let mut window_size = 0u64;
let mut dict_id = 0u32;
let mut frame_content_size = ZSTD_CONTENTSIZE_UNKNOWN;
if single_segment == 0 {
let wl = unsafe { *ip.add(pos) };
pos += 1;
let window_log = usize::from(wl >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN;
if window_log > window_log_max() {
return ERROR(ZstdErrorCode::FrameParameterWindowTooLarge);
}
window_size = 1u64 << window_log;
window_size = window_size.wrapping_add((window_size >> 3) * u64::from(wl & 7));
}
match dict_id_size_code {
0 => {}
1 => {
dict_id = unsafe { *ip.add(pos) } as u32;
pos += 1;
}
2 => {
dict_id = unsafe { MEM_readLE16(ip.add(pos).cast()) as u32 };
pos += 2;
}
_ => {
dict_id = unsafe { MEM_readLE32(ip.add(pos).cast()) };
pos += 4;
}
}
match fcs_id {
0 => {
if single_segment != 0 {
frame_content_size = unsafe { *ip.add(pos) } as u64;
}
}
1 => frame_content_size = unsafe { MEM_readLE16(ip.add(pos).cast()) as u64 + 256 },
2 => frame_content_size = unsafe { MEM_readLE32(ip.add(pos).cast()) as u64 },
_ => frame_content_size = unsafe { MEM_readLE64(ip.add(pos).cast()) },
}
if single_segment != 0 {
window_size = frame_content_size;
}
unsafe {
(*zfh).frame_type = ZSTD_FRAME;
(*zfh).frame_content_size = frame_content_size;
(*zfh).window_size = window_size;
(*zfh).block_size_max = min(window_size, ZSTD_BLOCKSIZE_MAX as u64) as c_uint;
(*zfh).dict_id = dict_id;
(*zfh).checksum_flag = checksum_flag as c_uint;
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_getFrameHeader(
zfh: *mut ZSTD_FrameHeader,
src: *const c_void,
src_size: usize,
) -> usize {
unsafe { ZSTD_getFrameHeader_advanced(zfh, src, src_size, ZSTD_F_ZSTD1) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_getFrameContentSize(src: *const c_void, src_size: usize) -> u64 {
if unsafe { ZSTD_rust_legacy_is(src, src_size) } != 0 {
let size = unsafe { ZSTD_rust_legacy_get_decompressed_size(src, src_size) };
return if size == 0 {
ZSTD_CONTENTSIZE_UNKNOWN
} else {
size
};
}
let mut zfh = ZSTD_FrameHeader::default();
if unsafe { ZSTD_getFrameHeader(&mut zfh, src, src_size) } != 0 {
return ZSTD_CONTENTSIZE_ERROR;
}
if zfh.frame_type == ZSTD_SKIPPABLE_FRAME {
0
} else {
zfh.frame_content_size
}
}
unsafe fn read_skippable_frame_size(src: *const u8, src_size: usize) -> usize {
if src_size < ZSTD_SKIPPABLEHEADERSIZE {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let size = unsafe { MEM_readLE32(src.add(ZSTD_FRAMEIDSIZE).cast()) };
if size.wrapping_add(ZSTD_SKIPPABLEHEADERSIZE as u32) < size {
return ERROR(ZstdErrorCode::FrameParameterUnsupported);
}
let frame_size = ZSTD_SKIPPABLEHEADERSIZE + size as usize;
if frame_size > src_size {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
frame_size
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_readSkippableFrame(
dst: *mut c_void,
dst_capacity: usize,
magic_variant: *mut c_uint,
src: *const c_void,
src_size: usize,
) -> usize {
if src_size < ZSTD_SKIPPABLEHEADERSIZE {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let src_u8 = src.cast::<u8>();
let magic = unsafe { MEM_readLE32(src) };
let frame_size = unsafe { read_skippable_frame_size(src_u8, src_size) };
if ERR_isError(frame_size) {
return frame_size;
}
let content_size = frame_size - ZSTD_SKIPPABLEHEADERSIZE;
if unsafe { ZSTD_isSkippableFrame(src, src_size) } == 0 {
return ERROR(ZstdErrorCode::FrameParameterUnsupported);
}
if content_size > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
if content_size != 0 && !dst.is_null() {
unsafe {
copy_bytes(
dst.cast(),
src_u8.add(ZSTD_SKIPPABLEHEADERSIZE),
content_size,
)
};
}
if !magic_variant.is_null() {
unsafe { magic_variant.write(magic - ZSTD_MAGIC_SKIPPABLE_START) };
}
content_size
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct FrameSizeInfo {
nb_blocks: usize,
compressed_size: usize,
decompressed_bound: u64,
}
#[inline]
fn error_frame_size_info(error: usize) -> FrameSizeInfo {
FrameSizeInfo {
nb_blocks: 0,
compressed_size: error,
decompressed_bound: ZSTD_CONTENTSIZE_ERROR,
}
}
unsafe fn find_frame_size_info(src: *const u8, src_size: usize, format: c_int) -> FrameSizeInfo {
if format == ZSTD_F_ZSTD1 && unsafe { ZSTD_rust_legacy_is(src.cast(), src_size) } != 0 {
let mut info = FrameSizeInfo::default();
let status = unsafe {
ZSTD_rust_legacy_frame_size_info(
src.cast(),
src_size,
&mut info.compressed_size,
&mut info.decompressed_bound,
&mut info.nb_blocks,
)
};
return if ERR_isError(status) {
error_frame_size_info(status)
} else {
info
};
}
if format == ZSTD_F_ZSTD1
&& src_size >= ZSTD_SKIPPABLEHEADERSIZE
&& unsafe { MEM_readLE32(src.cast()) } & ZSTD_MAGIC_SKIPPABLE_MASK
== ZSTD_MAGIC_SKIPPABLE_START
{
return FrameSizeInfo {
nb_blocks: 0,
compressed_size: unsafe { read_skippable_frame_size(src, src_size) },
decompressed_bound: 0,
};
}
let mut zfh = ZSTD_FrameHeader::default();
let header_result =
unsafe { ZSTD_getFrameHeader_advanced(&mut zfh, src.cast(), src_size, format) };
if ERR_isError(header_result) {
return error_frame_size_info(header_result);
}
if header_result != 0 {
return error_frame_size_info(ERROR(ZstdErrorCode::SrcSizeWrong));
}
let mut ip = unsafe { src.add(zfh.header_size as usize) };
let mut remaining = src_size - zfh.header_size as usize;
let mut nb_blocks = 0usize;
loop {
let mut block = BlockProperties::default();
let c_block_size = unsafe {
crate::zstd_decompress_block::ZSTD_getcBlockSize(
ip.cast(),
remaining,
(&mut block as *mut BlockProperties).cast(),
)
};
if ERR_isError(c_block_size) {
return error_frame_size_info(c_block_size);
}
let total_size = match ZSTD_BLOCKHEADERSIZE.checked_add(c_block_size) {
Some(size) => size,
None => return error_frame_size_info(ERROR(ZstdErrorCode::SrcSizeWrong)),
};
if total_size > remaining {
return error_frame_size_info(ERROR(ZstdErrorCode::SrcSizeWrong));
}
ip = unsafe { ip.add(total_size) };
remaining -= total_size;
nb_blocks += 1;
if block.last_block != 0 {
break;
}
}
if zfh.checksum_flag != 0 {
if remaining < 4 {
return error_frame_size_info(ERROR(ZstdErrorCode::SrcSizeWrong));
}
ip = unsafe { ip.add(4) };
}
FrameSizeInfo {
nb_blocks,
compressed_size: unsafe { ip.offset_from(src) as usize },
decompressed_bound: if zfh.frame_content_size != ZSTD_CONTENTSIZE_UNKNOWN {
zfh.frame_content_size
} else {
(nb_blocks as u64).wrapping_mul(zfh.block_size_max as u64)
},
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_findFrameCompressedSize(
src: *const c_void,
src_size: usize,
) -> usize {
unsafe { find_frame_size_info(src.cast(), src_size, ZSTD_F_ZSTD1).compressed_size }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_findDecompressedSize(src: *const c_void, mut src_size: usize) -> u64 {
let mut input = src.cast::<u8>();
let mut total = 0u64;
while src_size >= frame_header_prefix(ZSTD_F_ZSTD1) {
if unsafe { MEM_readLE32(input.cast()) } & ZSTD_MAGIC_SKIPPABLE_MASK
== ZSTD_MAGIC_SKIPPABLE_START
{
let size = unsafe { read_skippable_frame_size(input, src_size) };
if ERR_isError(size) {
return ZSTD_CONTENTSIZE_ERROR;
}
input = unsafe { input.add(size) };
src_size -= size;
continue;
}
let frame_size = unsafe { ZSTD_getFrameContentSize(input.cast(), src_size) };
if frame_size >= ZSTD_CONTENTSIZE_ERROR {
return frame_size;
}
let next = total.wrapping_add(frame_size);
if next < total {
return ZSTD_CONTENTSIZE_ERROR;
}
total = next;
let compressed = unsafe { ZSTD_findFrameCompressedSize(input.cast(), src_size) };
if ERR_isError(compressed) || compressed > src_size {
return ZSTD_CONTENTSIZE_ERROR;
}
input = unsafe { input.add(compressed) };
src_size -= compressed;
}
if src_size != 0 {
ZSTD_CONTENTSIZE_ERROR
} else {
total
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_getDecompressedSize(src: *const c_void, src_size: usize) -> u64 {
let result = unsafe { ZSTD_getFrameContentSize(src, src_size) };
if result >= ZSTD_CONTENTSIZE_ERROR {
0
} else {
result
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressBound(src: *const c_void, mut src_size: usize) -> u64 {
let mut input = src.cast::<u8>();
let mut bound = 0u64;
while src_size != 0 {
let info = unsafe { find_frame_size_info(input, src_size, ZSTD_F_ZSTD1) };
if ERR_isError(info.compressed_size) || info.decompressed_bound == ZSTD_CONTENTSIZE_ERROR {
return ZSTD_CONTENTSIZE_ERROR;
}
if info.compressed_size > src_size {
return ZSTD_CONTENTSIZE_ERROR;
}
bound = bound.wrapping_add(info.decompressed_bound);
input = unsafe { input.add(info.compressed_size) };
src_size -= info.compressed_size;
}
bound
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressionMargin(
src: *const c_void,
mut src_size: usize,
) -> usize {
let mut input = src.cast::<u8>();
let mut margin = 0usize;
let mut max_block_size = 0usize;
while src_size != 0 {
let info = unsafe { find_frame_size_info(input, src_size, ZSTD_F_ZSTD1) };
let mut zfh = ZSTD_FrameHeader::default();
let header = unsafe { ZSTD_getFrameHeader(&mut zfh, input.cast(), src_size) };
if ERR_isError(header) {
return header;
}
if header != 0
|| ERR_isError(info.compressed_size)
|| info.decompressed_bound == ZSTD_CONTENTSIZE_ERROR
{
return ERROR(ZstdErrorCode::CorruptionDetected);
}
if zfh.frame_type == ZSTD_FRAME {
margin = margin
.wrapping_add(zfh.header_size as usize)
.wrapping_add(if zfh.checksum_flag != 0 { 4 } else { 0 })
.wrapping_add(ZSTD_BLOCKHEADERSIZE.wrapping_mul(info.nb_blocks));
max_block_size = max(max_block_size, zfh.block_size_max as usize);
} else {
margin = margin.wrapping_add(info.compressed_size);
}
if info.compressed_size > src_size {
return ERROR(ZstdErrorCode::CorruptionDetected);
}
input = unsafe { input.add(info.compressed_size) };
src_size -= info.compressed_size;
}
margin.wrapping_add(max_block_size)
}
unsafe fn ref_dict_content(view: &ZSTD_rustDctxView, dict: *const u8, dict_size: usize) -> usize {
let previous = unsafe { get_pointer(view.previous_dst_end) };
let prefix = unsafe { get_pointer(view.prefix_start) };
unsafe {
set_pointer(view.dict_end, previous);
/* Do the same address arithmetic as the C implementation without
* forming a Rust pointer outside of an allocation. These are virtual
* history addresses and are only compared/subtracted by the block
* decoder, never dereferenced until they again name live history. */
let history = (previous as usize).wrapping_sub(prefix as usize);
set_pointer(
view.virtual_start,
(dict as usize).wrapping_sub(history) as *const u8,
);
set_pointer(view.prefix_start, dict);
set_pointer(view.previous_dst_end, const_ptr_add(dict, dict_size));
if !view.fuzz_begin.is_null() {
set_pointer(view.fuzz_begin, dict);
set_pointer(view.fuzz_end, const_ptr_add(dict, dict_size));
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_loadDEntropy(
entropy: *mut ZSTD_entropyDTables_t,
dict: *const c_void,
dict_size: usize,
) -> usize {
if dict_size <= 8 || dict.is_null() {
return ERROR(ZstdErrorCode::DictionaryCorrupted);
}
let mut dict_ptr = unsafe { dict.cast::<u8>().add(8) };
let dict_end = unsafe { dict.cast::<u8>().add(dict_size) };
let workspace = entropy.cast::<c_void>();
let workspace_size = size_of::<crate::zstd_decompress_block::ZSTD_seqSymbol>()
* ((1 << LL_FSE_LOG) + (1 << OFF_FSE_LOG) + (1 << ML_FSE_LOG) + 3);
let huf_size = unsafe {
#[cfg(feature = "huf-force-decompress-x1")]
{
HUF_readDTableX1_wksp(
(*entropy).huf_table.as_mut_ptr(),
dict_ptr.cast(),
ptr_distance(dict_end, dict_ptr),
workspace,
workspace_size,
0,
)
}
#[cfg(not(feature = "huf-force-decompress-x1"))]
{
HUF_readDTableX2_wksp(
(*entropy).huf_table.as_mut_ptr(),
dict_ptr.cast(),
ptr_distance(dict_end, dict_ptr),
workspace,
workspace_size,
0,
)
}
};
if ERR_isError(huf_size) {
return ERROR(ZstdErrorCode::DictionaryCorrupted);
}
dict_ptr = unsafe { dict_ptr.add(huf_size) };
unsafe fn load_table(
table: *mut crate::zstd_decompress_block::ZSTD_seqSymbol,
max_symbol: usize,
max_log: usize,
base: *const u32,
bits: *const u8,
workspace: *mut u32,
dict_ptr: *const u8,
dict_end: *const u8,
) -> Result<usize, usize> {
let mut norm = [0i16; MAX_ML + 1];
let mut max = max_symbol as c_uint;
let mut log = 0u32;
let size = unsafe {
FSE_readNCount(
norm.as_mut_ptr(),
&mut max,
&mut log,
dict_ptr.cast(),
ptr_distance(dict_end, dict_ptr),
)
};
if ERR_isError(size) || max as usize > max_symbol || log as usize > max_log {
return Err(ERROR(ZstdErrorCode::DictionaryCorrupted));
}
unsafe {
crate::zstd_decompress_block::ZSTD_buildFSETable(
table,
norm.as_ptr(),
max,
base,
bits,
log,
workspace.cast(),
ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 * size_of::<u32>(),
0,
);
}
Ok(size)
}
let entropy_ref = unsafe { &mut *entropy };
let off_size = match unsafe {
load_table(
entropy_ref.of_table.as_mut_ptr(),
MAX_OFF,
OFF_FSE_LOG,
OF_BASE.as_ptr(),
OF_BITS.as_ptr(),
entropy_ref.workspace.as_mut_ptr(),
dict_ptr,
dict_end,
)
} {
Ok(size) => size,
Err(error) => return error,
};
dict_ptr = unsafe { dict_ptr.add(off_size) };
let ml_size = match unsafe {
load_table(
entropy_ref.ml_table.as_mut_ptr(),
MAX_ML,
ML_FSE_LOG,
ML_BASE.as_ptr(),
ML_BITS.as_ptr(),
entropy_ref.workspace.as_mut_ptr(),
dict_ptr,
dict_end,
)
} {
Ok(size) => size,
Err(error) => return error,
};
dict_ptr = unsafe { dict_ptr.add(ml_size) };
let ll_size = match unsafe {
load_table(
entropy_ref.ll_table.as_mut_ptr(),
MAX_LL,
LL_FSE_LOG,
LL_BASE.as_ptr(),
LL_BITS.as_ptr(),
entropy_ref.workspace.as_mut_ptr(),
dict_ptr,
dict_end,
)
} {
Ok(size) => size,
Err(error) => return error,
};
dict_ptr = unsafe { dict_ptr.add(ll_size) };
if unsafe { ptr_distance(dict_end, dict_ptr) } < 12 {
return ERROR(ZstdErrorCode::DictionaryCorrupted);
}
let content_size = unsafe { ptr_distance(dict_end, dict_ptr.add(12)) };
for rep in &mut entropy_ref.rep {
let value = unsafe { MEM_readLE32(dict_ptr.cast()) };
dict_ptr = unsafe { dict_ptr.add(4) };
if value == 0 || value as usize > content_size {
return ERROR(ZstdErrorCode::DictionaryCorrupted);
}
*rep = value;
}
unsafe { dict_ptr.offset_from(dict.cast()) as usize }
}
#[repr(C)]
struct DDictHashSet {
table: *mut *const ZSTD_DDict,
size: usize,
count: usize,
}
unsafe fn ddict_hash_index(set: *const DDictHashSet, dict_id: u32) -> usize {
let hash = unsafe { XXH64((&dict_id as *const u32).cast(), size_of::<u32>(), 0) };
hash as usize & (unsafe { (*set).size } - 1)
}
unsafe fn ddict_hashset_create(custom_mem: ZSTD_customMem) -> *mut DDictHashSet {
let set = unsafe { ZSTD_rust_custom_malloc(size_of::<DDictHashSet>(), custom_mem) }
.cast::<DDictHashSet>();
if set.is_null() {
return ptr::null_mut();
}
let table = unsafe { ZSTD_rust_custom_calloc(64 * size_of::<*const ZSTD_DDict>(), custom_mem) }
.cast::<*const ZSTD_DDict>();
if table.is_null() {
unsafe { ZSTD_rust_custom_free(set.cast(), custom_mem) };
return ptr::null_mut();
}
unsafe {
set.write(DDictHashSet {
table,
size: 64,
count: 0,
});
}
set
}
unsafe fn ddict_hashset_free(set: *mut DDictHashSet, custom_mem: ZSTD_customMem) {
if set.is_null() {
return;
}
unsafe {
ZSTD_rust_custom_free((*set).table.cast(), custom_mem);
ZSTD_rust_custom_free(set.cast(), custom_mem);
}
}
unsafe fn ddict_hashset_emplace(set: *mut DDictHashSet, ddict: *const ZSTD_DDict) -> usize {
let dict_id = unsafe { ZSTD_getDictID_fromDDict(ddict) };
let mut index = unsafe { ddict_hash_index(set, dict_id) };
let mask = unsafe { (*set).size } - 1;
if unsafe { (*set).count == (*set).size } {
return ERROR(ZstdErrorCode::Generic);
}
while !unsafe { *(*set).table.add(index) }.is_null() {
if unsafe { ZSTD_getDictID_fromDDict(*(*set).table.add(index)) } == dict_id {
unsafe { (*set).table.add(index).write(ddict) };
return 0;
}
index = (index + 1) & mask;
}
unsafe {
(*set).table.add(index).write(ddict);
(*set).count += 1;
}
0
}
unsafe fn ddict_hashset_expand(set: *mut DDictHashSet, custom_mem: ZSTD_customMem) -> usize {
let old_table = unsafe { (*set).table };
let old_size = unsafe { (*set).size };
let new_size = match old_size.checked_mul(2) {
Some(size) => size,
None => return ERROR(ZstdErrorCode::MemoryAllocation),
};
let new_table =
unsafe { ZSTD_rust_custom_calloc(new_size * size_of::<*const ZSTD_DDict>(), custom_mem) }
.cast::<*const ZSTD_DDict>();
if new_table.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe {
(*set).table = new_table;
(*set).size = new_size;
(*set).count = 0;
for index in 0..old_size {
let ddict = *old_table.add(index);
if !ddict.is_null() {
let result = ddict_hashset_emplace(set, ddict);
if ERR_isError(result) {
return result;
}
}
}
ZSTD_rust_custom_free(old_table.cast(), custom_mem);
}
0
}
unsafe fn ddict_hashset_add(
set: *mut DDictHashSet,
ddict: *const ZSTD_DDict,
custom_mem: ZSTD_customMem,
) -> usize {
let should_expand = unsafe { (*set).count }
.wrapping_mul(4)
.wrapping_div(unsafe { (*set).size })
.wrapping_mul(3)
!= 0;
if should_expand {
let result = unsafe { ddict_hashset_expand(set, custom_mem) };
if ERR_isError(result) {
return result;
}
}
unsafe { ddict_hashset_emplace(set, ddict) }
}
unsafe fn ddict_hashset_get(set: *const DDictHashSet, dict_id: u32) -> *const ZSTD_DDict {
let mut index = unsafe { ddict_hash_index(set, dict_id) };
let mask = unsafe { (*set).size } - 1;
loop {
let ddict = unsafe { *(*set).table.add(index) };
let current_id = unsafe { ZSTD_getDictID_fromDDict(ddict) };
if current_id == dict_id || current_id == 0 {
return ddict;
}
index = (index + 1) & mask;
}
}
unsafe fn reset_parameters(view: &ZSTD_rustDctxView) {
unsafe {
set_field(view.format, ZSTD_F_ZSTD1);
set_field(
view.max_window_size,
ZSTD_rust_dctx_default_max_window_size(),
);
set_field(view.out_buffer_mode, ZSTD_BM_BUFFERED);
set_field(view.force_ignore_checksum, ZSTD_D_VALIDATE_CHECKSUM);
set_field(view.ref_multiple_ddicts, ZSTD_RMD_REF_SINGLE_DDICT);
set_field(view.disable_huf_asm, 0 as c_int);
set_field(view.max_block_size_param, 0 as c_int);
}
}
unsafe fn init_dctx_internal(view: &ZSTD_rustDctxView) {
unsafe {
set_field(view.static_size, 0usize);
set_dctx_ddict(view, ptr::null());
set_dctx_ddict_local(view, ptr::null_mut());
set_pointer(view.dict_end, ptr::null());
set_field(view.ddict_is_cold, 0 as c_int);
set_field(view.dict_uses, ZSTD_DONT_USE);
set_mut_pointer(view.in_buff, ptr::null_mut());
set_field(view.in_buff_size, 0usize);
set_field(view.out_buff_size, 0usize);
set_field(view.stream_stage, ZDSS_INIT);
if !view.legacy_context.is_null() {
set_field(view.legacy_context, ptr::null_mut::<c_void>());
set_field(view.previous_legacy_version, 0u32);
set_field(view.legacy_version, 0u32);
}
set_field(view.no_forward_progress, 0 as c_int);
set_field(view.oversized_duration, 0usize);
set_field(view.is_frame_decompression, 1 as c_int);
set_field(view.ddict_set, ptr::null_mut::<DDictHashSet>());
reset_parameters(view);
if !view.fuzz_end.is_null() {
set_pointer(view.fuzz_end, ptr::null());
}
ZSTD_rust_dctx_init_platform(view.dctx.cast());
}
}
unsafe fn clear_dict(view: &ZSTD_rustDctxView) {
unsafe {
let local = dctx_ddict_local(view);
if !local.is_null() {
ZSTD_freeDDict(local);
}
set_dctx_ddict_local(view, ptr::null_mut());
set_dctx_ddict(view, ptr::null());
set_field(view.dict_uses, ZSTD_DONT_USE);
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_sizeof_DCtx(dctx: *const ZSTD_DCtx) -> usize {
if dctx.is_null() {
return 0;
}
let view = unsafe { dctx_view(dctx.cast_mut()) };
let local = unsafe { dctx_ddict_local(&view) };
let ddict_size = if local.is_null() {
0
} else {
unsafe { crate::zstd_ddict::ZSTD_sizeof_DDict(local) }
};
view.dctx_size
.wrapping_add(ddict_size)
.wrapping_add(unsafe { field::<usize>(view.in_buff_size) })
.wrapping_add(unsafe { field::<usize>(view.out_buff_size) })
}
#[no_mangle]
pub extern "C" fn ZSTD_estimateDCtxSize() -> usize {
unsafe { ZSTD_rust_dctx_sizeof() }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_initStaticDCtx(
workspace: *mut c_void,
workspace_size: usize,
) -> *mut ZSTD_DCtx {
let dctx_size = unsafe { ZSTD_rust_dctx_sizeof() };
if workspace.is_null() || (workspace as usize & 7) != 0 || workspace_size < dctx_size {
return ptr::null_mut();
}
let dctx = workspace.cast::<ZSTD_DCtx>();
let view = unsafe { dctx_view(dctx) };
unsafe {
set_field(view.custom_mem, default_custom_mem());
init_dctx_internal(&view);
set_field(view.static_size, workspace_size);
set_mut_pointer(view.in_buff, workspace.cast::<u8>().add(dctx_size));
}
dctx
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_createDCtx_advanced(custom_mem: ZSTD_customMem) -> *mut ZSTD_DCtx {
if !custom_mem_valid(custom_mem) {
return ptr::null_mut();
}
let dctx = unsafe { ZSTD_rust_dctx_alloc(custom_mem) };
if dctx.is_null() {
return ptr::null_mut();
}
let view = unsafe { dctx_view(dctx) };
unsafe {
set_field(view.custom_mem, custom_mem);
init_dctx_internal(&view);
}
dctx
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_createDCtx() -> *mut ZSTD_DCtx {
unsafe { ZSTD_createDCtx_advanced(default_custom_mem()) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_freeDCtx(dctx: *mut ZSTD_DCtx) -> usize {
if dctx.is_null() {
return 0;
}
let view = unsafe { dctx_view(dctx) };
if unsafe { field::<usize>(view.static_size) } != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let custom_mem = unsafe { dctx_custom_mem(&view) };
unsafe {
clear_dict(&view);
let in_buff = get_mut_pointer(view.in_buff);
ZSTD_rust_custom_free(in_buff.cast(), custom_mem);
set_mut_pointer(view.in_buff, ptr::null_mut());
let set: *mut DDictHashSet = field(view.ddict_set);
ddict_hashset_free(set, custom_mem);
set_field(view.ddict_set, ptr::null_mut::<DDictHashSet>());
if !view.legacy_context.is_null() {
ZSTD_rust_legacy_free_stream(dctx);
}
ZSTD_rust_dctx_free_storage(dctx, custom_mem);
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_copyDCtx(dst: *mut ZSTD_DCtx, src: *const ZSTD_DCtx) {
if dst.is_null() || src.is_null() {
return;
}
let view = unsafe { dctx_view(dst) };
unsafe {
copy_dctx_prefix(dst.cast(), src.cast(), view.in_buff);
}
}
#[cfg(test)]
mod dctx_copy_tests {
use super::*;
#[test]
fn copy_prefix_stops_before_the_input_buffer_field() {
let source = [1u8, 2, 3, 4, 5];
let mut destination = [0xAAu8; 8];
let in_buff = unsafe { destination.as_mut_ptr().add(5).cast::<c_void>() };
unsafe {
copy_dctx_prefix(destination.as_mut_ptr(), source.as_ptr(), in_buff);
}
assert_eq!(&destination[..5], &source);
assert_eq!(&destination[5..], &[0xAA; 3]);
}
}
#[cfg(test)]
mod dctx_trace_tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
struct TraceProbe {
begin_calls: AtomicUsize,
end_calls: AtomicUsize,
last_context: AtomicUsize,
last_version: AtomicUsize,
last_streaming: AtomicUsize,
last_dictionary_id: AtomicUsize,
last_dictionary_is_cold: AtomicUsize,
last_dictionary_size: AtomicUsize,
last_uncompressed_size: AtomicUsize,
last_compressed_size: AtomicUsize,
}
impl TraceProbe {
fn new() -> Self {
Self {
begin_calls: AtomicUsize::new(0),
end_calls: AtomicUsize::new(0),
last_context: AtomicUsize::new(0),
last_version: AtomicUsize::new(0),
last_streaming: AtomicUsize::new(0),
last_dictionary_id: AtomicUsize::new(0),
last_dictionary_is_cold: AtomicUsize::new(0),
last_dictionary_size: AtomicUsize::new(0),
last_uncompressed_size: AtomicUsize::new(0),
last_compressed_size: AtomicUsize::new(0),
}
}
}
unsafe extern "C" fn trace_begin_probe(dctx: *const c_void) -> u64 {
let probe = dctx.cast::<TraceProbe>();
if probe.is_null() {
return 0;
}
unsafe { &*probe }
.begin_calls
.fetch_add(1, Ordering::SeqCst);
0xD1CE
}
unsafe extern "C" fn trace_end_probe(trace_ctx: u64, trace_ptr: *const c_void) {
if trace_ptr.is_null() {
return;
}
let trace = unsafe { &*trace_ptr.cast::<ZSTD_rustTrace>() };
let probe = trace.dctx.cast::<TraceProbe>();
if probe.is_null() {
return;
}
let probe = unsafe { &*probe };
probe.end_calls.fetch_add(1, Ordering::SeqCst);
probe
.last_context
.store(trace_ctx as usize, Ordering::SeqCst);
probe
.last_version
.store(trace.version as usize, Ordering::SeqCst);
probe
.last_streaming
.store(trace.streaming as usize, Ordering::SeqCst);
probe
.last_dictionary_id
.store(trace.dictionary_id as usize, Ordering::SeqCst);
probe
.last_dictionary_is_cold
.store(trace.dictionary_is_cold as usize, Ordering::SeqCst);
probe
.last_dictionary_size
.store(trace.dictionary_size, Ordering::SeqCst);
probe
.last_uncompressed_size
.store(trace.uncompressed_size, Ordering::SeqCst);
probe
.last_compressed_size
.store(trace.compressed_size, Ordering::SeqCst);
}
#[test]
fn trace_enabled_preserves_record_policy_and_context() {
let probe = TraceProbe::new();
let mut trace_ctx = 0u64;
let view = ZSTD_rustDctxTraceView {
trace_ctx: std::ptr::addr_of_mut!(trace_ctx),
begin: Some(trace_begin_probe),
end: Some(trace_end_probe),
dctx: (&probe as *const TraceProbe).cast(),
ddict: std::ptr::null(),
dictionary_is_cold: 1,
};
unsafe { trace_begin_with_view(&view) };
assert_eq!(trace_ctx, 0xD1CE);
unsafe {
trace_end_with_view(&view, 0x1_0000_0001, 0x2_0000_0002, 1);
}
assert_eq!(probe.begin_calls.load(Ordering::SeqCst), 1);
assert_eq!(probe.end_calls.load(Ordering::SeqCst), 1);
assert_eq!(probe.last_context.load(Ordering::SeqCst), 0xD1CE);
assert_eq!(probe.last_version.load(Ordering::SeqCst), 10_507);
assert_eq!(probe.last_streaming.load(Ordering::SeqCst), 1);
assert_eq!(probe.last_dictionary_id.load(Ordering::SeqCst), 0);
assert_eq!(probe.last_dictionary_is_cold.load(Ordering::SeqCst), 0);
assert_eq!(probe.last_dictionary_size.load(Ordering::SeqCst), 0);
assert_eq!(
probe.last_uncompressed_size.load(Ordering::SeqCst),
0x1_0000_0001u64 as usize
);
assert_eq!(
probe.last_compressed_size.load(Ordering::SeqCst),
0x2_0000_0002u64 as usize
);
assert_eq!(trace_ctx, 0xD1CE);
}
#[test]
fn trace_disabled_projection_is_a_noop() {
let probe = TraceProbe::new();
let trace_ctx = 0xBEEFu64;
let view = ZSTD_rustDctxTraceView {
trace_ctx: std::ptr::null_mut(),
begin: Some(trace_begin_probe),
end: Some(trace_end_probe),
dctx: (&probe as *const TraceProbe).cast(),
ddict: std::ptr::null(),
dictionary_is_cold: 1,
};
unsafe {
trace_begin_with_view(&view);
trace_end_with_view(&view, 1, 2, 0);
}
assert_eq!(trace_ctx, 0xBEEF);
assert_eq!(probe.begin_calls.load(Ordering::SeqCst), 0);
assert_eq!(probe.end_calls.load(Ordering::SeqCst), 0);
}
#[test]
fn trace_null_callbacks_follow_begin_and_end_policy() {
let mut trace_ctx = 0xBEEFu64;
let view = ZSTD_rustDctxTraceView {
trace_ctx: std::ptr::addr_of_mut!(trace_ctx),
begin: None,
end: None,
dctx: std::ptr::null(),
ddict: std::ptr::null(),
dictionary_is_cold: 0,
};
unsafe { trace_begin_with_view(&view) };
assert_eq!(trace_ctx, 0);
trace_ctx = 0xBEEF;
unsafe { trace_end_with_view(&view, 1, 2, 0) };
assert_eq!(trace_ctx, 0xBEEF);
}
}
#[cfg(test)]
mod stack_context_tests {
use super::*;
#[repr(C)]
struct StackDispatchProbe {
static_size: usize,
calls: usize,
dctx: usize,
dst: usize,
dst_capacity: usize,
src: usize,
src_size: usize,
result: usize,
}
unsafe extern "C" fn dispatch_probe(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
let probe = unsafe { &mut *dctx.cast::<StackDispatchProbe>() };
probe.calls += 1;
probe.dctx = dctx as usize;
probe.dst = dst as usize;
probe.dst_capacity = dst_capacity;
probe.src = src as usize;
probe.src_size = src_size;
probe.result
}
#[test]
fn stack_context_callback_clears_static_size_before_dispatch() {
let mut probe = StackDispatchProbe {
static_size: usize::MAX,
calls: 0,
dctx: 0,
dst: 0,
dst_capacity: 0,
src: 0,
src_size: 0,
result: 0x1234,
};
let mut view = unsafe { MaybeUninit::<ZSTD_rustDctxView>::zeroed().assume_init() };
view.dctx = (&mut probe as *mut StackDispatchProbe).cast();
view.static_size = std::ptr::addr_of_mut!(probe.static_size).cast();
let mut dst = [0u8; 8];
let src = [1u8, 2, 3];
let result = unsafe {
decompress_stack_context(
&view,
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
dispatch_probe,
)
};
assert_eq!(result, probe.result);
assert_eq!(probe.calls, 1);
assert_eq!(probe.static_size, 0);
assert_eq!(probe.dctx, view.dctx as usize);
assert_eq!(probe.dst, dst.as_mut_ptr() as usize);
assert_eq!(probe.dst_capacity, dst.len());
assert_eq!(probe.src, src.as_ptr() as usize);
assert_eq!(probe.src_size, src.len());
}
#[test]
fn stack_context_callback_rejects_null_context() {
let result = unsafe {
ZSTD_rust_decompress_stack_context(ptr::null_mut(), ptr::null_mut(), 0, ptr::null(), 0)
};
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
}
}
unsafe fn select_frame_ddict(view: &ZSTD_rustDctxView) {
let set: *mut DDictHashSet = unsafe { field(view.ddict_set) };
if set.is_null() || unsafe { dctx_ddict(view) }.is_null() {
return;
}
let dict_id = unsafe { (*get_frame_header_ptr(view)).dict_id };
let ddict = unsafe { ddict_hashset_get(set, dict_id) };
if !ddict.is_null() {
unsafe {
clear_dict(view);
set_field(view.dict_id, dict_id);
set_dctx_ddict(view, ddict);
set_field(view.dict_uses, ZSTD_USE_INDEFINITELY);
}
}
}
unsafe fn decode_frame_header(
view: &ZSTD_rustDctxView,
src: *const u8,
header_size: usize,
) -> usize {
let format = unsafe { field::<c_int>(view.format) };
let result = unsafe {
ZSTD_getFrameHeader_advanced(get_frame_header_ptr(view), src.cast(), header_size, format)
};
if ERR_isError(result) {
return result;
}
if result != 0 {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if unsafe { field::<c_int>(view.ref_multiple_ddicts) } == ZSTD_RMD_REF_MULTIPLE_DDICTS
&& !unsafe { field::<*mut DDictHashSet>(view.ddict_set) }.is_null()
{
unsafe { select_frame_ddict(view) };
}
if view.fuzz_begin.is_null()
&& unsafe { (*get_frame_header_ptr(view)).dict_id } != 0
&& unsafe { field::<u32>(view.dict_id) } != unsafe { (*get_frame_header_ptr(view)).dict_id }
{
return ERROR(ZstdErrorCode::DictionaryWrong);
}
let validate = u32::from(
unsafe { (*get_frame_header_ptr(view)).checksum_flag } != 0
&& unsafe { field::<c_int>(view.force_ignore_checksum) } == ZSTD_D_VALIDATE_CHECKSUM,
);
unsafe {
set_field(view.validate_checksum, validate);
if validate != 0 {
let _ = XXH64_reset(view.xxh_state.cast::<XXH64_state_t>(), 0);
}
let processed = field::<u64>(view.processed_c_size).wrapping_add(header_size as u64);
set_field(view.processed_c_size, processed);
}
0
}
unsafe fn decompress_begin(view: &ZSTD_rustDctxView) -> usize {
unsafe {
trace_begin(view.dctx.cast());
let format = field::<c_int>(view.format);
set_field(view.expected, frame_header_prefix(format));
set_field(view.stage, ZSTDDS_GET_FRAME_HEADER_SIZE);
set_field(view.processed_c_size, 0u64);
set_field(view.decoded_size, 0u64);
set_pointer(view.previous_dst_end, ptr::null());
set_pointer(view.prefix_start, ptr::null());
set_pointer(view.virtual_start, ptr::null());
set_pointer(view.dict_end, ptr::null());
let entropy = &mut *entropy_ptr(view);
entropy.huf_table[0] = (ZSTD_HUFFDTABLE_CAPACITY_LOG as u32).wrapping_mul(0x0100_0001);
set_field(view.lit_entropy, 0u32);
set_field(view.fse_entropy, 0u32);
set_field(view.dict_id, 0u32);
set_field(view.b_type, BT_RESERVED);
set_field(view.is_frame_decompression, 1 as c_int);
entropy.rep = [1, 4, 8];
set_field(view.llt_ptr, entropy.ll_table.as_ptr());
set_field(view.mlt_ptr, entropy.ml_table.as_ptr());
set_field(view.oft_ptr, entropy.of_table.as_ptr());
set_field(view.huf_ptr, entropy.huf_table.as_ptr());
}
0
}
unsafe fn decompress_insert_dictionary(
view: &ZSTD_rustDctxView,
mut dict: *const u8,
mut dict_size: usize,
) -> usize {
if dict_size < 8 || unsafe { MEM_readLE32(dict.cast()) } != ZSTD_MAGIC_DICTIONARY {
return unsafe { ref_dict_content(view, dict, dict_size) };
}
unsafe {
set_field(
view.dict_id,
MEM_readLE32(dict.add(ZSTD_FRAMEIDSIZE).cast()),
)
};
let entropy_size = unsafe { ZSTD_loadDEntropy(entropy_ptr(view), dict.cast(), dict_size) };
if ERR_isError(entropy_size) {
return ERROR(ZstdErrorCode::DictionaryCorrupted);
}
dict = unsafe { dict.add(entropy_size) };
dict_size -= entropy_size;
unsafe {
set_field(view.lit_entropy, 1u32);
set_field(view.fse_entropy, 1u32);
ref_dict_content(view, dict, dict_size)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressBegin(dctx: *mut ZSTD_DCtx) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { decompress_begin(&dctx_view(dctx)) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressBegin_usingDict(
dctx: *mut ZSTD_DCtx,
dict: *const c_void,
dict_size: usize,
) -> usize {
let view = unsafe { dctx_view(dctx) };
let result = unsafe { decompress_begin(&view) };
if ERR_isError(result) {
return result;
}
if !dict.is_null() && dict_size != 0 {
let result = unsafe { decompress_insert_dictionary(&view, dict.cast(), dict_size) };
if ERR_isError(result) {
return ERROR(ZstdErrorCode::DictionaryCorrupted);
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressBegin_usingDDict(
dctx: *mut ZSTD_DCtx,
ddict: *const ZSTD_DDict,
) -> usize {
let view = unsafe { dctx_view(dctx) };
if !ddict.is_null() {
let dict_start = unsafe { ZSTD_DDict_dictContent(ddict) };
let dict_size = unsafe { ZSTD_DDict_dictSize(ddict) };
let dict_end = unsafe { dict_start.cast::<u8>().add(dict_size).cast::<c_void>() };
unsafe {
set_field(
view.ddict_is_cold,
c_int::from(get_pointer(view.dict_end).cast::<c_void>() != dict_end),
)
};
}
let result = unsafe { decompress_begin(&view) };
if ERR_isError(result) {
return result;
}
if !ddict.is_null() {
unsafe { ZSTD_copyDDictParameters(dctx.cast(), ddict) };
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_getDictID_fromDict(dict: *const c_void, dict_size: usize) -> c_uint {
if dict.is_null() || dict_size < 8 || unsafe { MEM_readLE32(dict) } != ZSTD_MAGIC_DICTIONARY {
0
} else {
unsafe { MEM_readLE32(dict.cast::<u8>().add(ZSTD_FRAMEIDSIZE).cast()) }
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_getDictID_fromFrame(src: *const c_void, src_size: usize) -> c_uint {
let mut zfh = ZSTD_FrameHeader::default();
if ERR_isError(unsafe { ZSTD_getFrameHeader(&mut zfh, src, src_size) }) {
0
} else {
zfh.dict_id
}
}
/*-*************************************************************
* Frame decoding
***************************************************************/
#[inline]
unsafe fn copy_raw_block(
dst: *mut u8,
dst_capacity: usize,
src: *const u8,
src_size: usize,
) -> usize {
if src_size > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
if dst.is_null() {
return if src_size == 0 {
0
} else {
ERROR(ZstdErrorCode::DstBufferNull)
};
}
unsafe { copy_bytes(dst, src, src_size) };
src_size
}
#[inline]
unsafe fn set_rle_block(
dst: *mut u8,
dst_capacity: usize,
byte: u8,
regenerated_size: usize,
) -> usize {
if regenerated_size > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
if dst.is_null() {
return if regenerated_size == 0 {
0
} else {
ERROR(ZstdErrorCode::DstBufferNull)
};
}
if regenerated_size != 0 {
unsafe { dst.write_bytes(byte, regenerated_size) };
}
regenerated_size
}
/// Decode exactly one non-skippable modern frame and advance the caller's
/// input cursor. The state preparation deliberately stays in the public
/// `decompressBegin*()` calls, just as in the C implementation.
unsafe fn decompress_frame(
dctx: *mut ZSTD_DCtx,
view: &ZSTD_rustDctxView,
dst: *mut c_void,
dst_capacity: usize,
src_ptr: &mut *const u8,
src_size_ptr: &mut usize,
) -> usize {
let istart = *src_ptr;
let mut ip = istart;
let ostart = dst.cast::<u8>();
/* `dst == NULL, dstCapacity == 0` is supported for empty frames. A
* wrapping endpoint preserves C's address-only calculation until a
* block decoder reports the appropriate null/size error. */
let oend = ostart.wrapping_add(dst_capacity);
let mut op = ostart;
let mut remaining = *src_size_ptr;
let format = unsafe { field::<c_int>(view.format) };
if remaining < frame_header_min(format) + ZSTD_BLOCKHEADERSIZE {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let header_size =
unsafe { frame_header_size_internal(ip, frame_header_prefix(format), format) };
if ERR_isError(header_size) {
return header_size;
}
if remaining < header_size + ZSTD_BLOCKHEADERSIZE {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let result = unsafe { decode_frame_header(view, ip, header_size) };
if ERR_isError(result) {
return result;
}
ip = unsafe { ip.add(header_size) };
remaining -= header_size;
let max_block_size_param = unsafe { field::<c_int>(view.max_block_size_param) };
if max_block_size_param != 0 {
let mut params = unsafe { field::<ZSTD_FrameHeader>(view.f_params) };
params.block_size_max = min(params.block_size_max, max_block_size_param as c_uint);
unsafe { set_field(view.f_params, params) };
}
loop {
let mut block = BlockProperties::default();
let c_block_size = unsafe {
crate::zstd_decompress_block::ZSTD_getcBlockSize(
ip.cast(),
remaining,
(&mut block as *mut BlockProperties).cast(),
)
};
if ERR_isError(c_block_size) {
return c_block_size;
}
ip = unsafe { ip.add(ZSTD_BLOCKHEADERSIZE) };
remaining -= ZSTD_BLOCKHEADERSIZE;
if c_block_size > remaining {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let mut block_end = oend;
if (ip as usize) >= (op as usize) && (ip as usize) < (block_end as usize) {
block_end = op.wrapping_add((ip as usize).wrapping_sub(op as usize));
}
let block_capacity = (block_end as usize).wrapping_sub(op as usize);
let decoded_size = match block.block_type {
BT_COMPRESSED => unsafe {
ZSTD_decompressBlock_internal(
dctx,
op.cast(),
block_capacity,
ip.cast(),
c_block_size,
0,
)
},
/* This deliberately uses `oend`, not `block_end`: memmove is
* overlap-safe for raw blocks. */
BT_RAW => unsafe {
copy_raw_block(
op,
(oend as usize).wrapping_sub(op as usize),
ip,
c_block_size,
)
},
BT_RLE => {
if c_block_size == 0 {
return ERROR(ZstdErrorCode::CorruptionDetected);
}
unsafe { set_rle_block(op, block_capacity, *ip, block.orig_size as usize) }
}
_ => ERROR(ZstdErrorCode::CorruptionDetected),
};
if ERR_isError(decoded_size) {
return decoded_size;
}
if unsafe { field::<u32>(view.validate_checksum) } != 0 {
let _ = unsafe {
XXH64_update(
view.xxh_state.cast::<XXH64_state_t>(),
op.cast(),
decoded_size,
)
};
}
if decoded_size != 0 {
op = unsafe { op.add(decoded_size) };
}
ip = unsafe { ip.add(c_block_size) };
remaining -= c_block_size;
if block.last_block != 0 {
break;
}
}
let params = unsafe { field::<ZSTD_FrameHeader>(view.f_params) };
let decoded = (op as usize).wrapping_sub(ostart as usize);
if params.frame_content_size != ZSTD_CONTENTSIZE_UNKNOWN
&& decoded as u64 != params.frame_content_size
{
return ERROR(ZstdErrorCode::CorruptionDetected);
}
if params.checksum_flag != 0 {
if remaining < 4 {
return ERROR(ZstdErrorCode::ChecksumWrong);
}
if unsafe { field::<c_int>(view.force_ignore_checksum) } == ZSTD_D_VALIDATE_CHECKSUM {
let calculated = unsafe { XXH64_digest(view.xxh_state.cast::<XXH64_state_t>()) } as u32;
let read = unsafe { MEM_readLE32(ip.cast()) };
if calculated != read {
return ERROR(ZstdErrorCode::ChecksumWrong);
}
}
ip = unsafe { ip.add(4) };
remaining -= 4;
}
unsafe {
trace_end(
dctx,
decoded as u64,
(ip as usize).wrapping_sub(istart as usize) as u64,
0,
);
}
*src_ptr = ip;
*src_size_ptr = remaining;
decoded
}
unsafe fn decompress_multi_frame(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
mut dst_capacity: usize,
src: *const c_void,
mut src_size: usize,
mut dict: *const c_void,
mut dict_size: usize,
ddict: *const ZSTD_DDict,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
if !ddict.is_null() {
dict = unsafe { ZSTD_DDict_dictContent(ddict) };
dict_size = unsafe { ZSTD_DDict_dictSize(ddict) };
}
let dst_start = dst.cast::<u8>();
let mut output = dst_start;
let mut input = src.cast::<u8>();
let mut more_than_one_frame = false;
let starting_input = frame_header_prefix(unsafe { field::<c_int>(view.format) });
while src_size >= starting_input {
if unsafe { field::<c_int>(view.format) } == ZSTD_F_ZSTD1
&& unsafe { ZSTD_rust_legacy_is(input.cast(), src_size) } != 0
{
let frame_size =
unsafe { ZSTD_rust_legacy_find_compressed_size(input.cast(), src_size) };
if ERR_isError(frame_size) {
return frame_size;
}
if unsafe { field::<usize>(view.static_size) } != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
if frame_size > src_size {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
let decoded = unsafe {
ZSTD_rust_legacy_decompress(
output.cast(),
dst_capacity,
input.cast(),
frame_size,
dict,
dict_size,
)
};
if ERR_isError(decoded) {
return decoded;
}
let expected = unsafe { ZSTD_getFrameContentSize(input.cast(), src_size) };
if expected == ZSTD_CONTENTSIZE_ERROR
|| (expected != ZSTD_CONTENTSIZE_UNKNOWN && expected != decoded as u64)
{
return ERROR(ZstdErrorCode::CorruptionDetected);
}
if decoded > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
if decoded != 0 {
output = unsafe { output.add(decoded) };
}
dst_capacity -= decoded;
input = unsafe { input.add(frame_size) };
src_size -= frame_size;
continue;
}
if unsafe { field::<c_int>(view.format) } == ZSTD_F_ZSTD1 && src_size >= ZSTD_FRAMEIDSIZE {
let magic = unsafe { MEM_readLE32(input.cast()) };
if magic & ZSTD_MAGIC_SKIPPABLE_MASK == ZSTD_MAGIC_SKIPPABLE_START {
let size = unsafe { read_skippable_frame_size(input, src_size) };
if ERR_isError(size) {
return size;
}
input = unsafe { input.add(size) };
src_size -= size;
continue;
}
}
let init = if !ddict.is_null() {
unsafe { ZSTD_decompressBegin_usingDDict(dctx, ddict) }
} else {
unsafe { ZSTD_decompressBegin_usingDict(dctx, dict, dict_size) }
};
if ERR_isError(init) {
return init;
}
unsafe { ZSTD_checkContinuity(dctx, output.cast(), dst_capacity) };
let decoded = unsafe {
decompress_frame(
dctx,
&view,
output.cast(),
dst_capacity,
&mut input,
&mut src_size,
)
};
if decoded == ERROR(ZstdErrorCode::PrefixUnknown) && more_than_one_frame {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if ERR_isError(decoded) {
return decoded;
}
if decoded > dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
if decoded != 0 {
output = unsafe { output.add(decoded) };
}
dst_capacity -= decoded;
more_than_one_frame = true;
}
if src_size != 0 {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
(output as usize).wrapping_sub(dst_start as usize)
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_insertBlock(
dctx: *mut ZSTD_DCtx,
block_start: *const c_void,
block_size: usize,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
ZSTD_checkContinuity(dctx, block_start, block_size);
let view = dctx_view(dctx);
set_pointer(
view.previous_dst_end,
const_ptr_add(block_start.cast(), block_size),
);
}
block_size
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompress_usingDict(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
) -> usize {
unsafe {
decompress_multi_frame(
dctx,
dst,
dst_capacity,
src,
src_size,
dict,
dict_size,
ptr::null(),
)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompress_usingDDict(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
ddict: *const ZSTD_DDict,
) -> usize {
unsafe {
decompress_multi_frame(
dctx,
dst,
dst_capacity,
src,
src_size,
ptr::null(),
0,
ddict,
)
}
}
unsafe fn get_ddict(view: &ZSTD_rustDctxView) -> *const ZSTD_DDict {
match unsafe { field::<c_int>(view.dict_uses) } {
ZSTD_DONT_USE => {
unsafe { clear_dict(view) };
ptr::null()
}
ZSTD_USE_INDEFINITELY => unsafe { dctx_ddict(view) },
ZSTD_USE_ONCE => {
unsafe { set_field(view.dict_uses, ZSTD_DONT_USE) };
unsafe { dctx_ddict(view) }
}
_ => {
unsafe { clear_dict(view) };
ptr::null()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressDCtx(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
let ddict = unsafe { get_ddict(&view) };
unsafe { ZSTD_decompress_usingDDict(dctx, dst, dst_capacity, src, src_size, ddict) }
}
unsafe fn decompress_stack_context(
view: &ZSTD_rustDctxView,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dispatch: ZstdDecompressDCtxFn,
) -> usize {
if view.dctx.is_null() || view.static_size.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
/* A stack DCtx is not a user-provided static workspace. Clear the
* projected marker before dispatch so heapmode=0 retains the historical
* one-shot behavior, including legacy decoding and allocations. */
unsafe { set_field(view.static_size, 0usize) };
unsafe { dispatch(view.dctx.cast(), dst, dst_capacity, src, src_size) }
}
/// Rust policy/dispatch for the C-owned stack context used by heapmode=0.
///
/// The context remains opaque here; the C projection supplies only the field
/// addresses Rust needs and keeps `ZSTD_DCtx_s` layout private to C.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_decompress_stack_context(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
unsafe {
decompress_stack_context(&view, dst, dst_capacity, src, src_size, ZSTD_decompressDCtx)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompress(
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if unsafe { ZSTD_rust_heapmode() } < 1 {
return unsafe { ZSTD_rust_decompress_stack(dst, dst_capacity, src, src_size) };
}
let dctx = unsafe { ZSTD_createDCtx() };
if dctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe { ZSTD_decompressDCtx(dctx, dst, dst_capacity, src, src_size) };
let _ = unsafe { ZSTD_freeDCtx(dctx) };
result
}
/*-**************************************
* Advanced bufferless decompression
****************************************/
#[inline]
unsafe fn next_src_size_with_input_size(view: &ZSTD_rustDctxView, input_size: usize) -> usize {
let stage = unsafe { field::<c_int>(view.stage) };
if (stage == ZSTDDS_DECOMPRESS_BLOCK || stage == ZSTDDS_DECOMPRESS_LAST_BLOCK)
&& unsafe { field::<c_int>(view.b_type) } == BT_RAW
{
let expected = unsafe { field::<usize>(view.expected) };
return max(1, min(input_size, expected));
}
unsafe { field::<usize>(view.expected) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_nextSrcSizeToDecompress(dctx: *mut ZSTD_DCtx) -> usize {
if dctx.is_null() {
return 0;
}
let view = unsafe { dctx_view(dctx) };
unsafe { field(view.expected) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_nextInputType(dctx: *mut ZSTD_DCtx) -> c_int {
if dctx.is_null() {
return ZSTD_NIT_FRAME_HEADER;
}
let view = unsafe { dctx_view(dctx) };
match unsafe { field::<c_int>(view.stage) } {
ZSTDDS_GET_FRAME_HEADER_SIZE | ZSTDDS_DECODE_FRAME_HEADER => ZSTD_NIT_FRAME_HEADER,
ZSTDDS_DECODE_BLOCK_HEADER => ZSTD_NIT_BLOCK_HEADER,
ZSTDDS_DECOMPRESS_BLOCK => ZSTD_NIT_BLOCK,
ZSTDDS_DECOMPRESS_LAST_BLOCK => ZSTD_NIT_LAST_BLOCK,
ZSTDDS_CHECK_CHECKSUM => ZSTD_NIT_CHECKSUM,
ZSTDDS_DECODE_SKIPPABLE_HEADER | ZSTDDS_SKIP_FRAME => ZSTD_NIT_SKIPPABLE_FRAME,
_ => ZSTD_NIT_FRAME_HEADER,
}
}
#[inline]
unsafe fn is_skip_frame(view: &ZSTD_rustDctxView) -> bool {
(unsafe { field::<c_int>(view.stage) }) == ZSTDDS_SKIP_FRAME
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressContinue(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
if src_size != unsafe { next_src_size_with_input_size(&view, src_size) } {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
unsafe { ZSTD_checkContinuity(dctx, dst.cast(), dst_capacity) };
let processed = unsafe { field::<u64>(view.processed_c_size) }.wrapping_add(src_size as u64);
unsafe { set_field(view.processed_c_size, processed) };
match unsafe { field::<c_int>(view.stage) } {
ZSTDDS_GET_FRAME_HEADER_SIZE => {
let format = unsafe { field::<c_int>(view.format) };
let header_buffer = view.header_buffer.cast::<u8>();
if format == ZSTD_F_ZSTD1
&& src_size >= ZSTD_FRAMEIDSIZE
&& unsafe { MEM_readLE32(src) } & ZSTD_MAGIC_SKIPPABLE_MASK
== ZSTD_MAGIC_SKIPPABLE_START
{
unsafe { copy_bytes(header_buffer, src.cast(), src_size) };
unsafe {
set_field(view.expected, ZSTD_SKIPPABLEHEADERSIZE - src_size);
set_field(view.stage, ZSTDDS_DECODE_SKIPPABLE_HEADER);
}
return 0;
}
let header_size = unsafe { frame_header_size_internal(src.cast(), src_size, format) };
if ERR_isError(header_size) {
return header_size;
}
unsafe {
copy_bytes(header_buffer, src.cast(), src_size);
set_field(view.header_size, header_size);
set_field(view.expected, header_size - src_size);
set_field(view.stage, ZSTDDS_DECODE_FRAME_HEADER);
}
0
}
ZSTDDS_DECODE_FRAME_HEADER => {
let header_size = unsafe { field::<usize>(view.header_size) };
let offset = header_size - src_size;
unsafe {
copy_bytes(
view.header_buffer.cast::<u8>().add(offset),
src.cast(),
src_size,
);
}
let result =
unsafe { decode_frame_header(&view, view.header_buffer.cast(), header_size) };
if ERR_isError(result) {
return result;
}
unsafe {
set_field(view.expected, ZSTD_BLOCKHEADERSIZE);
set_field(view.stage, ZSTDDS_DECODE_BLOCK_HEADER);
}
0
}
ZSTDDS_DECODE_BLOCK_HEADER => {
let mut block = BlockProperties::default();
let c_block_size = unsafe {
crate::zstd_decompress_block::ZSTD_getcBlockSize(
src,
ZSTD_BLOCKHEADERSIZE,
(&mut block as *mut BlockProperties).cast(),
)
};
if ERR_isError(c_block_size) {
return c_block_size;
}
let params = unsafe { field::<ZSTD_FrameHeader>(view.f_params) };
if c_block_size > params.block_size_max as usize {
return ERROR(ZstdErrorCode::CorruptionDetected);
}
unsafe {
set_field(view.expected, c_block_size);
set_field(view.b_type, block.block_type);
set_field(view.rle_size, block.orig_size as usize);
}
if c_block_size != 0 {
unsafe {
set_field(
view.stage,
if block.last_block != 0 {
ZSTDDS_DECOMPRESS_LAST_BLOCK
} else {
ZSTDDS_DECOMPRESS_BLOCK
},
);
}
return 0;
}
unsafe {
if block.last_block != 0 {
if params.checksum_flag != 0 {
set_field(view.expected, 4usize);
set_field(view.stage, ZSTDDS_CHECK_CHECKSUM);
} else {
set_field(view.expected, 0usize);
set_field(view.stage, ZSTDDS_GET_FRAME_HEADER_SIZE);
}
} else {
set_field(view.expected, ZSTD_BLOCKHEADERSIZE);
set_field(view.stage, ZSTDDS_DECODE_BLOCK_HEADER);
}
}
0
}
ZSTDDS_DECOMPRESS_BLOCK | ZSTDDS_DECOMPRESS_LAST_BLOCK => {
let stage = unsafe { field::<c_int>(view.stage) };
let b_type = unsafe { field::<c_int>(view.b_type) };
let decoded = match b_type {
BT_COMPRESSED => {
let result = unsafe {
ZSTD_decompressBlock_internal(dctx, dst, dst_capacity, src, src_size, 1)
};
unsafe { set_field(view.expected, 0usize) };
result
}
BT_RAW => {
let result =
unsafe { copy_raw_block(dst.cast(), dst_capacity, src.cast(), src_size) };
if ERR_isError(result) {
return result;
}
let expected = unsafe { field::<usize>(view.expected) } - result;
unsafe { set_field(view.expected, expected) };
result
}
BT_RLE => {
if src_size == 0 {
return ERROR(ZstdErrorCode::CorruptionDetected);
}
let result = unsafe {
set_rle_block(
dst.cast(),
dst_capacity,
*src.cast::<u8>(),
field::<usize>(view.rle_size),
)
};
unsafe { set_field(view.expected, 0usize) };
result
}
_ => return ERROR(ZstdErrorCode::CorruptionDetected),
};
if ERR_isError(decoded) {
return decoded;
}
let params = unsafe { field::<ZSTD_FrameHeader>(view.f_params) };
if decoded > params.block_size_max as usize {
return ERROR(ZstdErrorCode::CorruptionDetected);
}
let decoded_total =
unsafe { field::<u64>(view.decoded_size) }.wrapping_add(decoded as u64);
unsafe {
set_field(view.decoded_size, decoded_total);
if field::<u32>(view.validate_checksum) != 0 {
let _ = XXH64_update(view.xxh_state.cast::<XXH64_state_t>(), dst, decoded);
}
set_pointer(view.previous_dst_end, const_ptr_add(dst.cast(), decoded));
}
if unsafe { field::<usize>(view.expected) } != 0 {
return decoded;
}
if stage == ZSTDDS_DECOMPRESS_LAST_BLOCK {
if params.frame_content_size != ZSTD_CONTENTSIZE_UNKNOWN
&& decoded_total != params.frame_content_size
{
return ERROR(ZstdErrorCode::CorruptionDetected);
}
unsafe {
if params.checksum_flag != 0 {
set_field(view.expected, 4usize);
set_field(view.stage, ZSTDDS_CHECK_CHECKSUM);
} else {
trace_end(dctx, decoded_total, field::<u64>(view.processed_c_size), 1);
set_field(view.expected, 0usize);
set_field(view.stage, ZSTDDS_GET_FRAME_HEADER_SIZE);
}
}
} else {
unsafe {
set_field(view.stage, ZSTDDS_DECODE_BLOCK_HEADER);
set_field(view.expected, ZSTD_BLOCKHEADERSIZE);
}
}
decoded
}
ZSTDDS_CHECK_CHECKSUM => {
if src_size != 4 {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if unsafe { field::<u32>(view.validate_checksum) } != 0 {
let calculated =
unsafe { XXH64_digest(view.xxh_state.cast::<XXH64_state_t>()) } as u32;
let read = unsafe { MEM_readLE32(src) };
if calculated != read {
return ERROR(ZstdErrorCode::ChecksumWrong);
}
}
unsafe {
trace_end(
dctx,
field::<u64>(view.decoded_size),
field::<u64>(view.processed_c_size),
1,
);
set_field(view.expected, 0usize);
set_field(view.stage, ZSTDDS_GET_FRAME_HEADER_SIZE);
}
0
}
ZSTDDS_DECODE_SKIPPABLE_HEADER => {
if src_size > ZSTD_SKIPPABLEHEADERSIZE {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
unsafe {
let header = view.header_buffer.cast::<u8>();
copy_bytes(
header.add(ZSTD_SKIPPABLEHEADERSIZE - src_size),
src.cast(),
src_size,
);
set_field(
view.expected,
MEM_readLE32(header.add(ZSTD_FRAMEIDSIZE).cast()) as usize,
);
set_field(view.stage, ZSTDDS_SKIP_FRAME);
}
0
}
ZSTDDS_SKIP_FRAME => {
unsafe {
set_field(view.expected, 0usize);
set_field(view.stage, ZSTDDS_GET_FRAME_HEADER_SIZE);
}
0
}
_ => ERROR(ZstdErrorCode::Generic),
}
}
/*-**************************************
* Streaming context management
****************************************/
#[no_mangle]
pub unsafe extern "C" fn ZSTD_createDStream() -> *mut ZSTD_DStream {
unsafe { ZSTD_createDCtx() }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_initStaticDStream(
workspace: *mut c_void,
workspace_size: usize,
) -> *mut ZSTD_DStream {
unsafe { ZSTD_initStaticDCtx(workspace, workspace_size) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_createDStream_advanced(
custom_mem: ZSTD_customMem,
) -> *mut ZSTD_DStream {
unsafe { ZSTD_createDCtx_advanced(custom_mem) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_freeDStream(zds: *mut ZSTD_DStream) -> usize {
unsafe { ZSTD_freeDCtx(zds) }
}
#[no_mangle]
pub extern "C" fn ZSTD_DStreamInSize() -> usize {
ZSTD_BLOCKSIZE_MAX + ZSTD_BLOCKHEADERSIZE
}
#[no_mangle]
pub extern "C" fn ZSTD_DStreamOutSize() -> usize {
ZSTD_BLOCKSIZE_MAX
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_loadDictionary_advanced(
dctx: *mut ZSTD_DCtx,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
if unsafe { field::<c_int>(view.stream_stage) } != ZDSS_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe { clear_dict(&view) };
if !dict.is_null() && dict_size != 0 {
let ddict = unsafe {
ZSTD_createDDict_advanced(
dict,
dict_size,
dict_load_method,
dict_content_type,
ddict_custom_mem(&view),
)
};
if ddict.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe {
set_dctx_ddict_local(&view, ddict);
set_dctx_ddict(&view, ddict);
set_field(view.dict_uses, ZSTD_USE_INDEFINITELY);
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_loadDictionary_byReference(
dctx: *mut ZSTD_DCtx,
dict: *const c_void,
dict_size: usize,
) -> usize {
unsafe {
ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dict_size, ZSTD_DLM_BY_REF, ZSTD_DCT_AUTO)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_loadDictionary(
dctx: *mut ZSTD_DCtx,
dict: *const c_void,
dict_size: usize,
) -> usize {
unsafe {
ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dict_size, ZSTD_DLM_BY_COPY, ZSTD_DCT_AUTO)
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_refPrefix_advanced(
dctx: *mut ZSTD_DCtx,
prefix: *const c_void,
prefix_size: usize,
dict_content_type: c_int,
) -> usize {
let result = unsafe {
ZSTD_DCtx_loadDictionary_advanced(
dctx,
prefix,
prefix_size,
ZSTD_DLM_BY_REF,
dict_content_type,
)
};
if ERR_isError(result) {
return result;
}
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
unsafe { set_field(view.dict_uses, ZSTD_USE_ONCE) };
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_refPrefix(
dctx: *mut ZSTD_DCtx,
prefix: *const c_void,
prefix_size: usize,
) -> usize {
unsafe { ZSTD_DCtx_refPrefix_advanced(dctx, prefix, prefix_size, ZSTD_DCT_RAW_CONTENT) }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_initDStream_usingDict(
zds: *mut ZSTD_DStream,
dict: *const c_void,
dict_size: usize,
) -> usize {
let result = unsafe { ZSTD_DCtx_reset(zds, ZSTD_RESET_SESSION_ONLY) };
if ERR_isError(result) {
return result;
}
let result = unsafe { ZSTD_DCtx_loadDictionary(zds, dict, dict_size) };
if ERR_isError(result) {
return result;
}
if zds.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(zds) };
frame_header_prefix(unsafe { field::<c_int>(view.format) })
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_initDStream(zds: *mut ZSTD_DStream) -> usize {
let result = unsafe { ZSTD_DCtx_reset(zds, ZSTD_RESET_SESSION_ONLY) };
if ERR_isError(result) {
return result;
}
let result = unsafe { ZSTD_DCtx_refDDict(zds, ptr::null()) };
if ERR_isError(result) {
return result;
}
if zds.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(zds) };
frame_header_prefix(unsafe { field::<c_int>(view.format) })
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_initDStream_usingDDict(
zds: *mut ZSTD_DStream,
ddict: *const ZSTD_DDict,
) -> usize {
let result = unsafe { ZSTD_DCtx_reset(zds, ZSTD_RESET_SESSION_ONLY) };
if ERR_isError(result) {
return result;
}
let result = unsafe { ZSTD_DCtx_refDDict(zds, ddict) };
if ERR_isError(result) {
return result;
}
if zds.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(zds) };
frame_header_prefix(unsafe { field::<c_int>(view.format) })
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_resetDStream(zds: *mut ZSTD_DStream) -> usize {
let result = unsafe { ZSTD_DCtx_reset(zds, ZSTD_RESET_SESSION_ONLY) };
if ERR_isError(result) {
return result;
}
if zds.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(zds) };
frame_header_prefix(unsafe { field::<c_int>(view.format) })
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_refDDict(
dctx: *mut ZSTD_DCtx,
ddict: *const ZSTD_DDict,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
if unsafe { field::<c_int>(view.stream_stage) } != ZDSS_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe { clear_dict(&view) };
if ddict.is_null() {
return 0;
}
unsafe {
set_dctx_ddict(&view, ddict);
set_field(view.dict_uses, ZSTD_USE_INDEFINITELY);
}
if unsafe { field::<c_int>(view.ref_multiple_ddicts) } == ZSTD_RMD_REF_MULTIPLE_DDICTS {
let mut set: *mut DDictHashSet = unsafe { field(view.ddict_set) };
if set.is_null() {
if unsafe { field::<usize>(view.static_size) } != 0 {
return ERROR(ZstdErrorCode::ParameterUnsupported);
}
set = unsafe { ddict_hashset_create(dctx_custom_mem(&view)) };
if set.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe { set_field(view.ddict_set, set) };
}
let result = unsafe { ddict_hashset_add(set, ddict, dctx_custom_mem(&view)) };
if ERR_isError(result) {
return result;
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_setMaxWindowSize(
dctx: *mut ZSTD_DCtx,
max_window_size: usize,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
let bounds = ZSTD_dParam_getBounds(ZSTD_D_WINDOW_LOG_MAX);
let minimum = 1usize << bounds.lower_bound;
let maximum = 1usize << bounds.upper_bound;
if unsafe { field::<c_int>(view.stream_stage) } != ZDSS_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
if max_window_size < minimum || max_window_size > maximum {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
unsafe { set_field(view.max_window_size, max_window_size) };
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_setFormat(dctx: *mut ZSTD_DCtx, format: c_int) -> usize {
unsafe { ZSTD_DCtx_setParameter(dctx, ZSTD_D_FORMAT, format) }
}
#[no_mangle]
pub extern "C" fn ZSTD_dParam_getBounds(param: c_int) -> ZSTD_bounds {
match param {
ZSTD_D_WINDOW_LOG_MAX => ZSTD_bounds {
error: 0,
lower_bound: ZSTD_WINDOWLOG_ABSOLUTEMIN as c_int,
upper_bound: window_log_max() as c_int,
},
ZSTD_D_FORMAT => ZSTD_bounds {
error: 0,
lower_bound: ZSTD_F_ZSTD1,
upper_bound: ZSTD_F_ZSTD1_MAGICLESS,
},
ZSTD_D_STABLE_OUT_BUFFER => ZSTD_bounds {
error: 0,
lower_bound: ZSTD_BM_BUFFERED,
upper_bound: ZSTD_BM_STABLE,
},
ZSTD_D_FORCE_IGNORE_CHECKSUM => ZSTD_bounds {
error: 0,
lower_bound: ZSTD_D_VALIDATE_CHECKSUM,
upper_bound: ZSTD_D_IGNORE_CHECKSUM,
},
ZSTD_D_REF_MULTIPLE_DDICTS => ZSTD_bounds {
error: 0,
lower_bound: ZSTD_RMD_REF_SINGLE_DDICT,
upper_bound: ZSTD_RMD_REF_MULTIPLE_DDICTS,
},
ZSTD_D_DISABLE_HUFFMAN_ASSEMBLY => ZSTD_bounds {
error: 0,
lower_bound: 0,
upper_bound: 1,
},
ZSTD_D_MAX_BLOCK_SIZE => ZSTD_bounds {
error: 0,
lower_bound: ZSTD_BLOCKSIZE_MAX_MIN as c_int,
upper_bound: ZSTD_BLOCKSIZE_MAX as c_int,
},
_ => ZSTD_bounds {
error: ERROR(ZstdErrorCode::ParameterUnsupported),
lower_bound: 0,
upper_bound: 0,
},
}
}
#[inline]
fn dparam_within_bounds(param: c_int, value: c_int) -> bool {
let bounds = ZSTD_dParam_getBounds(param);
!ERR_isError(bounds.error) && value >= bounds.lower_bound && value <= bounds.upper_bound
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_getParameter(
dctx: *mut ZSTD_DCtx,
param: c_int,
value: *mut c_int,
) -> usize {
if dctx.is_null() || value.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
let result = match param {
ZSTD_D_WINDOW_LOG_MAX => {
let window = unsafe { field::<usize>(view.max_window_size) } as u32;
(u32::BITS - 1 - window.leading_zeros()) as c_int
}
ZSTD_D_FORMAT => unsafe { field::<c_int>(view.format) },
ZSTD_D_STABLE_OUT_BUFFER => unsafe { field::<c_int>(view.out_buffer_mode) },
ZSTD_D_FORCE_IGNORE_CHECKSUM => unsafe { field::<c_int>(view.force_ignore_checksum) },
ZSTD_D_REF_MULTIPLE_DDICTS => unsafe { field::<c_int>(view.ref_multiple_ddicts) },
ZSTD_D_DISABLE_HUFFMAN_ASSEMBLY => unsafe { field::<c_int>(view.disable_huf_asm) },
ZSTD_D_MAX_BLOCK_SIZE => unsafe { field::<c_int>(view.max_block_size_param) },
_ => return ERROR(ZstdErrorCode::ParameterUnsupported),
};
unsafe { value.write(result) };
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_setParameter(
dctx: *mut ZSTD_DCtx,
param: c_int,
mut value: c_int,
) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
if unsafe { field::<c_int>(view.stream_stage) } != ZDSS_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
match param {
ZSTD_D_WINDOW_LOG_MAX => {
if value == 0 {
value = ZSTD_WINDOWLOG_LIMIT_DEFAULT as c_int;
}
if !dparam_within_bounds(param, value) {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
unsafe { set_field(view.max_window_size, 1usize << value) };
}
ZSTD_D_FORMAT => {
if !dparam_within_bounds(param, value) {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
unsafe { set_field(view.format, value) };
}
ZSTD_D_STABLE_OUT_BUFFER => {
if !dparam_within_bounds(param, value) {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
unsafe { set_field(view.out_buffer_mode, value) };
}
ZSTD_D_FORCE_IGNORE_CHECKSUM => {
if !dparam_within_bounds(param, value) {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
unsafe { set_field(view.force_ignore_checksum, value) };
}
ZSTD_D_REF_MULTIPLE_DDICTS => {
if !dparam_within_bounds(param, value) {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
if unsafe { field::<usize>(view.static_size) } != 0 {
return ERROR(ZstdErrorCode::ParameterUnsupported);
}
unsafe { set_field(view.ref_multiple_ddicts, value) };
}
ZSTD_D_DISABLE_HUFFMAN_ASSEMBLY => {
if !dparam_within_bounds(param, value) {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
unsafe { set_field(view.disable_huf_asm, c_int::from(value != 0)) };
}
ZSTD_D_MAX_BLOCK_SIZE => {
if value != 0 && !dparam_within_bounds(param, value) {
return ERROR(ZstdErrorCode::ParameterOutOfBound);
}
unsafe { set_field(view.max_block_size_param, value) };
}
_ => return ERROR(ZstdErrorCode::ParameterUnsupported),
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_DCtx_reset(dctx: *mut ZSTD_DCtx, reset: c_int) -> usize {
if dctx.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let view = unsafe { dctx_view(dctx) };
if reset == ZSTD_RESET_SESSION_ONLY || reset == ZSTD_RESET_SESSION_AND_PARAMETERS {
unsafe {
set_field(view.stream_stage, ZDSS_INIT);
set_field(view.no_forward_progress, 0 as c_int);
set_field(view.is_frame_decompression, 1 as c_int);
}
}
if reset == ZSTD_RESET_PARAMETERS || reset == ZSTD_RESET_SESSION_AND_PARAMETERS {
if unsafe { field::<c_int>(view.stream_stage) } != ZDSS_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe {
clear_dict(&view);
reset_parameters(&view);
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_sizeof_DStream(dctx: *const ZSTD_DStream) -> usize {
unsafe { ZSTD_sizeof_DCtx(dctx) }
}
unsafe fn decoding_buffer_size_internal(
window_size: u64,
frame_content_size: u64,
block_size_max: usize,
) -> usize {
let block_size = min(
min(window_size, ZSTD_BLOCKSIZE_MAX as u64) as usize,
block_size_max,
);
let needed_ring = window_size
.wrapping_add((block_size as u64).wrapping_mul(2))
.wrapping_add((WILDCOPY_OVERLENGTH as u64).wrapping_mul(2));
let needed = min(frame_content_size, needed_ring);
let result = needed as usize;
if result as u64 != needed {
return ERROR(ZstdErrorCode::FrameParameterWindowTooLarge);
}
result
}
#[no_mangle]
pub extern "C" fn ZSTD_decodingBufferSize_min(window_size: u64, frame_content_size: u64) -> usize {
unsafe { decoding_buffer_size_internal(window_size, frame_content_size, ZSTD_BLOCKSIZE_MAX) }
}
#[no_mangle]
pub extern "C" fn ZSTD_estimateDStreamSize(window_size: usize) -> usize {
let block_size = min(window_size, ZSTD_BLOCKSIZE_MAX);
let out_size = ZSTD_decodingBufferSize_min(window_size as u64, ZSTD_CONTENTSIZE_UNKNOWN);
ZSTD_estimateDCtxSize()
.wrapping_add(block_size)
.wrapping_add(out_size)
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_estimateDStreamSize_fromFrame(
src: *const c_void,
src_size: usize,
) -> usize {
let mut zfh = ZSTD_FrameHeader::default();
let result = unsafe { ZSTD_getFrameHeader(&mut zfh, src, src_size) };
if ERR_isError(result) {
return result;
}
if result != 0 {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if zfh.window_size > (1u64 << window_log_max()) {
return ERROR(ZstdErrorCode::FrameParameterWindowTooLarge);
}
ZSTD_estimateDStreamSize(zfh.window_size as usize)
}
#[inline]
unsafe fn dctx_is_overflow(
view: &ZSTD_rustDctxView,
needed_in_size: usize,
needed_out_size: usize,
) -> bool {
let current = unsafe { field::<usize>(view.in_buff_size) }
.wrapping_add(unsafe { field::<usize>(view.out_buff_size) });
let needed = needed_in_size
.wrapping_add(needed_out_size)
.wrapping_mul(ZSTD_WORKSPACETOOLARGE_FACTOR);
current >= needed
}
#[inline]
unsafe fn update_oversized_duration(
view: &ZSTD_rustDctxView,
needed_in_size: usize,
needed_out_size: usize,
) {
let duration = if unsafe { dctx_is_overflow(view, needed_in_size, needed_out_size) } {
unsafe { field::<usize>(view.oversized_duration) }.wrapping_add(1)
} else {
0
};
unsafe { set_field(view.oversized_duration, duration) };
}
#[inline]
unsafe fn oversized_too_long(view: &ZSTD_rustDctxView) -> bool {
(unsafe { field::<usize>(view.oversized_duration) }) >= ZSTD_WORKSPACETOOLARGE_MAXDURATION
}
unsafe fn check_out_buffer(view: &ZSTD_rustDctxView, output: &ZSTD_outBuffer) -> usize {
if unsafe { field::<c_int>(view.out_buffer_mode) } != ZSTD_BM_STABLE
|| unsafe { field::<c_int>(view.stream_stage) } == ZDSS_INIT
{
return 0;
}
let expected = unsafe { field::<ZSTD_outBuffer>(view.expected_out_buffer) };
if expected.dst == output.dst && expected.size == output.size && expected.pos == output.pos {
0
} else {
ERROR(ZstdErrorCode::DstBufferWrong)
}
}
/// Invoke the bufferless state machine from the streaming adapter and translate
/// its output into either the rolling internal buffer or the stable user buffer.
unsafe fn decompress_continue_stream(
dctx: *mut ZSTD_DCtx,
view: &ZSTD_rustDctxView,
op: &mut *mut u8,
oend: *mut u8,
src: *const u8,
src_size: usize,
) -> usize {
let skip = unsafe { is_skip_frame(view) };
if unsafe { field::<c_int>(view.out_buffer_mode) } == ZSTD_BM_BUFFERED {
let out_start = unsafe { field::<usize>(view.out_start) };
let out_size = unsafe { field::<usize>(view.out_buff_size) };
let dst_size = if skip {
0
} else {
out_size.wrapping_sub(out_start)
};
let out_buff = unsafe { get_mut_pointer(view.out_buff) };
let decoded = unsafe {
ZSTD_decompressContinue(
dctx,
out_buff.wrapping_add(out_start).cast(),
dst_size,
src.cast(),
src_size,
)
};
if ERR_isError(decoded) {
return decoded;
}
unsafe {
if decoded == 0 && !skip {
set_field(view.stream_stage, ZDSS_READ);
} else {
set_field(view.out_end, out_start.wrapping_add(decoded));
set_field(view.stream_stage, ZDSS_FLUSH);
}
}
} else {
let dst_size = (oend as usize).wrapping_sub(*op as usize);
let decoded =
unsafe { ZSTD_decompressContinue(dctx, (*op).cast(), dst_size, src.cast(), src_size) };
if ERR_isError(decoded) {
return decoded;
}
if decoded != 0 {
*op = unsafe { (*op).add(decoded) };
}
unsafe { set_field(view.stream_stage, ZDSS_READ) };
}
0
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressStream(
zds: *mut ZSTD_DStream,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
) -> usize {
if zds.is_null() || output.is_null() || input.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let output_ref = unsafe { &mut *output };
let input_ref = unsafe { &mut *input };
if input_ref.pos > input_ref.size {
return ERROR(ZstdErrorCode::SrcSizeWrong);
}
if output_ref.pos > output_ref.size {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let view = unsafe { dctx_view(zds) };
let src = input_ref.src.cast::<u8>();
let istart = src.wrapping_add(input_ref.pos);
let iend = src.wrapping_add(input_ref.size);
let mut ip = istart;
let dst = output_ref.dst.cast::<u8>();
let ostart = dst.wrapping_add(output_ref.pos);
let oend = dst.wrapping_add(output_ref.size);
let mut op = ostart;
let mut some_more_work = true;
let output_check = unsafe { check_out_buffer(&view, output_ref) };
if ERR_isError(output_check) {
return output_check;
}
while some_more_work {
match unsafe { field::<c_int>(view.stream_stage) } {
ZDSS_INIT => {
unsafe {
set_field(view.stream_stage, ZDSS_LOAD_HEADER);
set_field(view.lh_size, 0usize);
set_field(view.in_pos, 0usize);
set_field(view.out_start, 0usize);
set_field(view.out_end, 0usize);
if !view.legacy_version.is_null() {
set_field(view.legacy_version, 0u32);
}
set_field(view.hostage_byte, 0u32);
set_field(view.expected_out_buffer, *output_ref);
}
continue;
}
ZDSS_LOAD_HEADER => {
if !view.legacy_version.is_null()
&& unsafe { field::<u32>(view.legacy_version) } != 0
{
let ddict = unsafe { dctx_ddict(&view) };
let dict = if ddict.is_null() {
ptr::null()
} else {
unsafe { ZSTD_DDict_dictContent(ddict) }
};
let dict_size = if ddict.is_null() {
0
} else {
unsafe { ZSTD_DDict_dictSize(ddict) }
};
return unsafe {
ZSTD_rust_legacy_decompress_stream(zds, output, input, dict, dict_size)
};
}
let lh_size = unsafe { field::<usize>(view.lh_size) };
let header_result = unsafe {
ZSTD_getFrameHeader_advanced(
get_frame_header_ptr(&view),
view.header_buffer.cast(),
lh_size,
field::<c_int>(view.format),
)
};
if unsafe { field::<c_int>(view.ref_multiple_ddicts) }
== ZSTD_RMD_REF_MULTIPLE_DDICTS
&& !unsafe { field::<*mut DDictHashSet>(view.ddict_set) }.is_null()
{
unsafe { select_frame_ddict(&view) };
}
if ERR_isError(header_result) {
let available = (iend as usize).wrapping_sub(istart as usize);
if unsafe { ZSTD_rust_legacy_is(istart.cast(), available) } != 0 {
if unsafe { field::<usize>(view.static_size) } != 0 {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let ddict = unsafe { get_ddict(&view) };
let dict = if ddict.is_null() {
ptr::null()
} else {
unsafe { ZSTD_DDict_dictContent(ddict) }
};
let dict_size = if ddict.is_null() {
0
} else {
unsafe { ZSTD_DDict_dictSize(ddict) }
};
return unsafe {
ZSTD_rust_legacy_decompress_stream(zds, output, input, dict, dict_size)
};
}
return header_result;
}
if header_result != 0 {
let to_load = header_result - lh_size;
let remaining_input = (iend as usize).wrapping_sub(ip as usize);
if to_load > remaining_input {
if remaining_input != 0 {
unsafe {
copy_bytes(
view.header_buffer.cast::<u8>().add(lh_size),
ip,
remaining_input,
);
set_field(view.lh_size, lh_size + remaining_input);
}
}
input_ref.pos = input_ref.size;
let check = unsafe {
ZSTD_getFrameHeader_advanced(
get_frame_header_ptr(&view),
view.header_buffer.cast(),
field::<usize>(view.lh_size),
field::<c_int>(view.format),
)
};
if ERR_isError(check) {
return check;
}
let minimum = max(
frame_header_min(unsafe { field::<c_int>(view.format) }),
header_result,
);
return minimum
.wrapping_sub(unsafe { field::<usize>(view.lh_size) })
.wrapping_add(ZSTD_BLOCKHEADERSIZE);
}
unsafe {
copy_bytes(view.header_buffer.cast::<u8>().add(lh_size), ip, to_load);
set_field(view.lh_size, header_result);
}
ip = unsafe { ip.add(to_load) };
continue;
}
let params = unsafe { field::<ZSTD_FrameHeader>(view.f_params) };
let available = (iend as usize).wrapping_sub(istart as usize);
if params.frame_content_size != ZSTD_CONTENTSIZE_UNKNOWN
&& params.frame_type != ZSTD_SKIPPABLE_FRAME
&& (oend as usize).wrapping_sub(op as usize)
>= params.frame_content_size as usize
{
let frame_size = unsafe {
find_frame_size_info(istart, available, field::<c_int>(view.format))
.compressed_size
};
if frame_size <= available {
let ddict = unsafe { get_ddict(&view) };
let decoded = unsafe {
ZSTD_decompress_usingDDict(
zds,
op.cast(),
(oend as usize).wrapping_sub(op as usize),
istart.cast(),
frame_size,
ddict,
)
};
if ERR_isError(decoded) {
return decoded;
}
ip = unsafe { istart.add(frame_size) };
if decoded != 0 {
op = unsafe { op.add(decoded) };
}
unsafe {
set_field(view.expected, 0usize);
set_field(view.stream_stage, ZDSS_INIT);
}
some_more_work = false;
continue;
}
}
if unsafe { field::<c_int>(view.out_buffer_mode) } == ZSTD_BM_STABLE
&& params.frame_type != ZSTD_SKIPPABLE_FRAME
&& params.frame_content_size != ZSTD_CONTENTSIZE_UNKNOWN
&& (oend as usize).wrapping_sub(op as usize)
< params.frame_content_size as usize
{
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
let ddict = unsafe { get_ddict(&view) };
let begin = unsafe { ZSTD_decompressBegin_usingDDict(zds, ddict) };
if ERR_isError(begin) {
return begin;
}
let format = unsafe { field::<c_int>(view.format) };
if format == ZSTD_F_ZSTD1
&& unsafe { MEM_readLE32(view.header_buffer) } & ZSTD_MAGIC_SKIPPABLE_MASK
== ZSTD_MAGIC_SKIPPABLE_START
{
unsafe {
set_field(
view.expected,
MEM_readLE32(
view.header_buffer.cast::<u8>().add(ZSTD_FRAMEIDSIZE).cast(),
) as usize,
);
set_field(view.stage, ZSTDDS_SKIP_FRAME);
}
} else {
let result = unsafe {
decode_frame_header(
&view,
view.header_buffer.cast(),
field::<usize>(view.lh_size),
)
};
if ERR_isError(result) {
return result;
}
unsafe {
set_field(view.expected, ZSTD_BLOCKHEADERSIZE);
set_field(view.stage, ZSTDDS_DECODE_BLOCK_HEADER);
}
}
let mut frame_params = unsafe { field::<ZSTD_FrameHeader>(view.f_params) };
frame_params.window_size =
max(frame_params.window_size, 1u64 << ZSTD_WINDOWLOG_ABSOLUTEMIN);
if frame_params.window_size > unsafe { field::<usize>(view.max_window_size) } as u64
{
return ERROR(ZstdErrorCode::FrameParameterWindowTooLarge);
}
let max_block_size_param = unsafe { field::<c_int>(view.max_block_size_param) };
if max_block_size_param != 0 {
frame_params.block_size_max =
min(frame_params.block_size_max, max_block_size_param as c_uint);
}
unsafe { set_field(view.f_params, frame_params) };
let needed_in_size = max(frame_params.block_size_max as usize, 4);
let needed_out_size =
if unsafe { field::<c_int>(view.out_buffer_mode) } == ZSTD_BM_BUFFERED {
let size = unsafe {
decoding_buffer_size_internal(
frame_params.window_size,
frame_params.frame_content_size,
frame_params.block_size_max as usize,
)
};
if ERR_isError(size) {
return size;
}
size
} else {
0
};
unsafe { update_oversized_duration(&view, needed_in_size, needed_out_size) };
let too_small = unsafe { field::<usize>(view.in_buff_size) } < needed_in_size
|| unsafe { field::<usize>(view.out_buff_size) } < needed_out_size;
let too_large = unsafe { oversized_too_long(&view) };
if too_small || too_large {
let buffer_size = needed_in_size.wrapping_add(needed_out_size);
if unsafe { field::<usize>(view.static_size) } != 0 {
let static_size = unsafe { field::<usize>(view.static_size) };
if buffer_size > static_size.wrapping_sub(view.dctx_size) {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
} else {
unsafe {
ZSTD_rust_custom_free(
get_mut_pointer(view.in_buff).cast(),
dctx_custom_mem(&view),
);
set_field(view.in_buff_size, 0usize);
set_field(view.out_buff_size, 0usize);
}
let allocation =
unsafe { ZSTD_rust_custom_malloc(buffer_size, dctx_custom_mem(&view)) };
if allocation.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe { set_mut_pointer(view.in_buff, allocation.cast()) };
}
let in_buff = unsafe { get_mut_pointer(view.in_buff) };
unsafe {
set_field(view.in_buff_size, needed_in_size);
set_mut_pointer(view.out_buff, in_buff.wrapping_add(needed_in_size));
set_field(view.out_buff_size, needed_out_size);
}
}
unsafe { set_field(view.stream_stage, ZDSS_READ) };
continue;
}
ZDSS_READ => {
let available = (iend as usize).wrapping_sub(ip as usize);
let needed = unsafe { next_src_size_with_input_size(&view, available) };
if needed == 0 {
unsafe { set_field(view.stream_stage, ZDSS_INIT) };
some_more_work = false;
continue;
}
if available >= needed {
let result = unsafe {
decompress_continue_stream(zds, &view, &mut op, oend, ip, needed)
};
if ERR_isError(result) {
return result;
}
ip = unsafe { ip.add(needed) };
continue;
}
if ip == iend {
some_more_work = false;
continue;
}
unsafe { set_field(view.stream_stage, ZDSS_LOAD) };
continue;
}
ZDSS_LOAD => {
let needed = unsafe { field::<usize>(view.expected) };
let in_pos = unsafe { field::<usize>(view.in_pos) };
let to_load = needed.wrapping_sub(in_pos);
let skip = unsafe { is_skip_frame(&view) };
let available = (iend as usize).wrapping_sub(ip as usize);
let loaded = if skip {
min(to_load, available)
} else {
let in_size = unsafe { field::<usize>(view.in_buff_size) };
if to_load > in_size.wrapping_sub(in_pos) {
return ERROR(ZstdErrorCode::CorruptionDetected);
}
unsafe {
limit_copy(
get_mut_pointer(view.in_buff).wrapping_add(in_pos),
to_load,
ip,
available,
)
}
};
if loaded != 0 {
ip = unsafe { ip.add(loaded) };
unsafe { set_field(view.in_pos, in_pos + loaded) };
}
if loaded < to_load {
some_more_work = false;
continue;
}
unsafe { set_field(view.in_pos, 0usize) };
let result = unsafe {
decompress_continue_stream(
zds,
&view,
&mut op,
oend,
get_mut_pointer(view.in_buff).cast(),
needed,
)
};
if ERR_isError(result) {
return result;
}
continue;
}
ZDSS_FLUSH => {
let out_start = unsafe { field::<usize>(view.out_start) };
let out_end = unsafe { field::<usize>(view.out_end) };
let to_flush = out_end.wrapping_sub(out_start);
let flushed = unsafe {
limit_copy(
op,
(oend as usize).wrapping_sub(op as usize),
get_mut_pointer(view.out_buff).wrapping_add(out_start),
to_flush,
)
};
if flushed != 0 {
op = unsafe { op.add(flushed) };
}
let new_out_start = out_start + flushed;
unsafe { set_field(view.out_start, new_out_start) };
if flushed == to_flush {
unsafe {
set_field(view.stream_stage, ZDSS_READ);
let frame_params = field::<ZSTD_FrameHeader>(view.f_params);
if field::<usize>(view.out_buff_size)
< frame_params.frame_content_size as usize
&& new_out_start + frame_params.block_size_max as usize
> field::<usize>(view.out_buff_size)
{
set_field(view.out_start, 0usize);
set_field(view.out_end, 0usize);
}
}
continue;
}
some_more_work = false;
continue;
}
_ => return ERROR(ZstdErrorCode::Generic),
}
}
input_ref.pos = (ip as usize).wrapping_sub(src as usize);
output_ref.pos = (op as usize).wrapping_sub(dst as usize);
unsafe { set_field(view.expected_out_buffer, *output_ref) };
if ip == istart && op == ostart {
let stalled = unsafe { field::<c_int>(view.no_forward_progress) } + 1;
unsafe { set_field(view.no_forward_progress, stalled) };
if stalled >= unsafe { ZSTD_rust_no_forward_progress_max() } {
if op == oend {
return ERROR(ZstdErrorCode::NoForwardProgressDestFull);
}
if ip == iend {
return ERROR(ZstdErrorCode::NoForwardProgressInputEmpty);
}
return ERROR(ZstdErrorCode::Generic);
}
} else {
unsafe { set_field(view.no_forward_progress, 0 as c_int) };
}
let mut hint = unsafe { field::<usize>(view.expected) };
if hint == 0 {
if unsafe { field::<usize>(view.out_end) } == unsafe { field::<usize>(view.out_start) } {
if unsafe { field::<u32>(view.hostage_byte) } != 0 {
if input_ref.pos >= input_ref.size {
unsafe { set_field(view.stream_stage, ZDSS_READ) };
return 1;
}
input_ref.pos += 1;
}
return 0;
}
if unsafe { field::<u32>(view.hostage_byte) } == 0 {
if input_ref.pos == 0 {
return ERROR(ZstdErrorCode::Generic);
}
input_ref.pos -= 1;
unsafe { set_field(view.hostage_byte, 1u32) };
}
return 1;
}
if unsafe { ZSTD_nextInputType(zds) } == ZSTD_NIT_BLOCK {
hint = hint.wrapping_add(ZSTD_BLOCKHEADERSIZE);
}
let in_pos = unsafe { field::<usize>(view.in_pos) };
if in_pos > hint {
return ERROR(ZstdErrorCode::CorruptionDetected);
}
hint - in_pos
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_decompressStream_simpleArgs(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
dst_capacity: usize,
dst_pos: *mut usize,
src: *const c_void,
src_size: usize,
src_pos: *mut usize,
) -> usize {
if dst_pos.is_null() || src_pos.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let mut output = ZSTD_outBuffer {
dst,
size: dst_capacity,
pos: unsafe { dst_pos.read() },
};
let mut input = ZSTD_inBuffer {
src,
size: src_size,
pos: unsafe { src_pos.read() },
};
let result = unsafe { ZSTD_decompressStream(dctx, &mut output, &mut input) };
unsafe {
dst_pos.write(output.pos);
src_pos.write(input.pos);
}
result
}