Move long-distance matching and high-level decompression from C shims into Rust. The decoder now owns context, dictionary, parameter, one-shot, and buffered streaming state while C retains allocation/configuration, legacy, and trace leaves. Move CLI parsing, safety policy, and dispatch into a separate Rust static archive. Keeping it separate prevents library builds from retaining FIO symbols, while C continues to own file opening, replacement, and I/O. Program targets now select matching compression/decompression archives. The remaining C boundary is intentional: high-level compression, optimal parsing, dictionary building, legacy callbacks, and CLI file I/O still need migration. Test Plan: - cargo test --all-targets (native and i686) - cargo test --all-targets in rust/cli (native and i686) - CLI crate compression-only and decompression-only feature tests - native and i686 fuzzer/zstreamtest runs, plus legacy and dictionary tests - ZSTD_C_PREDICT and ZSTD_HEAPMODE=0 fuzzer coverage - library, dynamic-link, and program-target build/round-trip matrix Refs: rust/README.md
3475 lines
117 KiB
Rust
3475 lines
117 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.
|
|
//!
|
|
//! `ZSTD_DCtx` deliberately remains C-owned. The companion C translation
|
|
//! unit projects its build-configuration-dependent leaves into
|
|
//! [`ZSTD_rustDctxView`]; this module owns the decoder state machine and
|
|
//! public ABI while never assumes offsets for the private C 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_freeDDict, ZSTD_getDictID_fromDDict,
|
|
};
|
|
use std::cmp::{max, min};
|
|
use std::ffi::c_void;
|
|
use std::mem::{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;
|
|
|
|
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,
|
|
}
|
|
|
|
unsafe extern "C" {
|
|
fn ZSTD_rust_dctx_view(dctx: *mut ZSTD_DCtx, out: *mut ZSTD_rustDctxView);
|
|
fn ZSTD_rust_dctx_sizeof() -> usize;
|
|
fn ZSTD_rust_dctx_alloc(custom_mem: ZSTD_customMem) -> *mut ZSTD_DCtx;
|
|
fn ZSTD_rust_dctx_free_storage(dctx: *mut ZSTD_DCtx, custom_mem: ZSTD_customMem);
|
|
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;
|
|
fn ZSTD_rust_custom_malloc(size: usize, custom_mem: ZSTD_customMem) -> *mut c_void;
|
|
fn ZSTD_rust_custom_calloc(size: usize, custom_mem: ZSTD_customMem) -> *mut c_void;
|
|
fn ZSTD_rust_custom_free(allocation: *mut c_void, custom_mem: ZSTD_customMem);
|
|
fn ZSTD_rust_create_ddict(
|
|
dict: *const c_void,
|
|
dict_size: usize,
|
|
dict_load_method: c_int,
|
|
dict_content_type: c_int,
|
|
custom_mem: ZSTD_customMem,
|
|
) -> *mut ZSTD_DDict;
|
|
fn ZSTD_rust_dctx_trace_end(
|
|
dctx: *mut ZSTD_DCtx,
|
|
uncompressed_size: u64,
|
|
compressed_size: u64,
|
|
streaming: c_int,
|
|
);
|
|
fn ZSTD_rust_dctx_trace_begin(dctx: *mut ZSTD_DCtx);
|
|
fn ZSTD_rust_dctx_copy_prefix(dst: *mut ZSTD_DCtx, src: *const ZSTD_DCtx);
|
|
fn ZSTD_rust_legacy_is(src: *const c_void, src_size: usize) -> c_uint;
|
|
fn ZSTD_rust_legacy_get_decompressed_size(src: *const c_void, src_size: usize) -> u64;
|
|
fn ZSTD_rust_legacy_find_compressed_size(src: *const c_void, src_size: usize) -> usize;
|
|
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;
|
|
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;
|
|
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;
|
|
fn ZSTD_rust_legacy_free_stream(dctx: *mut ZSTD_DCtx);
|
|
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);
|
|
}
|
|
|
|
#[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() }
|
|
}
|
|
|
|
#[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()
|
|
}
|
|
|
|
#[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 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) {
|
|
unsafe { ZSTD_rust_dctx_copy_prefix(dst, src) }
|
|
}
|
|
|
|
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 {
|
|
ZSTD_rust_dctx_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 {
|
|
ZSTD_rust_dctx_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) }
|
|
}
|
|
|
|
#[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 {
|
|
ZSTD_rust_dctx_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 {
|
|
ZSTD_rust_dctx_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_rust_create_ddict(
|
|
dict,
|
|
dict_size,
|
|
dict_load_method,
|
|
dict_content_type,
|
|
dctx_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
|
|
}
|