Move the hidden fullbench block-decoder entrypoints and the public ZSTD_decompressBlock compatibility wrapper into Rust. The C translation unit now only projects the configuration-dependent private DCtx fields into the Rust block context, preserving the existing ABI and decoder feature modes. Test Plan: - cargo test --manifest-path rust/Cargo.toml --all-targets -- --test-threads=1 - cargo clippy --manifest-path rust/Cargo.toml --tests -- -D warnings - make -B -C lib -j2 lib - make -B -C tests -j2 test-rust-lib-smoke - make -B -C tests -j2 test-cli-tests
2081 lines
66 KiB
Rust
2081 lines
66 KiB
Rust
#![allow(non_camel_case_types)]
|
|
#![allow(non_snake_case)]
|
|
#![allow(clippy::missing_safety_doc)]
|
|
#![allow(clippy::too_many_arguments)]
|
|
|
|
//! Compressed-block decoding.
|
|
//!
|
|
//! The C decoder context deliberately remains opaque. The companion C shim
|
|
//! extracts only the pointers and scalar values this translation needs into
|
|
//! [`ZSTD_rustBlockCtx`], so optional C context fields cannot silently change
|
|
//! this Rust module's ABI. Literal parsing, FSE table construction, sequence
|
|
//! decoding, and block execution live here.
|
|
|
|
use crate::bitstream::{
|
|
BIT_DStream_t, BIT_endOfDStream, BIT_initDStream, BIT_readBits, BIT_readBitsFast,
|
|
BIT_reloadDStream,
|
|
};
|
|
use crate::common::{
|
|
DEFAULT_MAX_OFF, LL_BITS, LL_DEFAULT_NORM, LL_DEFAULT_NORM_LOG, MAX_FSE_LOG, MAX_LL, MAX_ML,
|
|
MAX_OFF, MIN_CBLOCK_SIZE, MIN_LITERALS_FOR_4_STREAMS, MIN_SEQUENCES_SIZE, ML_BITS,
|
|
ML_DEFAULT_NORM, ML_DEFAULT_NORM_LOG, OF_DEFAULT_NORM, OF_DEFAULT_NORM_LOG, ZSTD_REP_NUM,
|
|
};
|
|
use crate::entropy_common::FSE_readNCount;
|
|
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
|
|
#[cfg(not(feature = "huf-force-decompress-x2"))]
|
|
use crate::huf_decompress::HUF_decompress1X1_DCtx_wksp;
|
|
#[cfg(feature = "huf-force-decompress-x2")]
|
|
use crate::huf_decompress::HUF_decompress1X_DCtx_wksp;
|
|
use crate::huf_decompress::{
|
|
HUF_decompress1X_usingDTable, HUF_decompress4X_hufOnly_wksp, HUF_decompress4X_usingDTable,
|
|
};
|
|
use crate::mem::{MEM_32bits, MEM_64bits, MEM_readLE16, MEM_readLE24, U32};
|
|
use crate::zstd_decompress::ZSTD_DCtx;
|
|
use std::cmp::min;
|
|
use std::ffi::c_void;
|
|
use std::mem::MaybeUninit;
|
|
use std::os::raw::{c_int, c_short, c_uint};
|
|
use std::ptr;
|
|
use std::sync::OnceLock;
|
|
|
|
const ZSTD_BLOCKSIZE_MAX: usize = 128 << 10;
|
|
const ZSTD_BLOCK_HEADER_SIZE: usize = 3;
|
|
const WILDCOPY_OVERLENGTH: usize = 32;
|
|
const HUF_FLAGS_BMI2: c_int = 1 << 0;
|
|
const HUF_FLAGS_DISABLE_ASM: c_int = 1 << 4;
|
|
const SET_BASIC: c_int = 0;
|
|
const SET_RLE: c_int = 1;
|
|
const SET_COMPRESSED: c_int = 2;
|
|
const SET_REPEAT: c_int = 3;
|
|
const NOT_STREAMING: c_int = 0;
|
|
const ZSTD_NOT_IN_DST: c_int = 0;
|
|
const ZSTD_IN_DST: c_int = 1;
|
|
const ZSTD_SPLIT: c_int = 2;
|
|
const LONG_NB_SEQ: usize = 0x7f00;
|
|
const LL_FSE_LOG: u32 = 9;
|
|
const OFF_FSE_LOG: u32 = 8;
|
|
const ML_FSE_LOG: u32 = 9;
|
|
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 STREAM_ACCUMULATOR_MIN_32: usize = 25;
|
|
const STREAM_ACCUMULATOR_MIN_64: usize = 57;
|
|
const SEQUENCE_DECODER_RUNTIME: c_int = 0;
|
|
const SEQUENCE_DECODER_FORCE_SHORT: c_int = 1;
|
|
const SEQUENCE_DECODER_FORCE_LONG: c_int = 2;
|
|
const LONG_OFFSET_ADDITIONAL_BITS: u8 = 22;
|
|
const LONG_OFFSET_HISTORY_THRESHOLD: usize = 1 << 24;
|
|
const LONG_OFFSET_MIN_SHARE_32: u32 = 20;
|
|
const LONG_OFFSET_MIN_SHARE_64: u32 = 7;
|
|
|
|
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,
|
|
];
|
|
|
|
/// `ZSTD_seqSymbol` from `zstd_decompress_internal.h`.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
pub struct ZSTD_seqSymbol {
|
|
next_state: u16,
|
|
nb_additional_bits: u8,
|
|
nb_bits: u8,
|
|
base_value: u32,
|
|
}
|
|
|
|
/// The first entry of a sequence table is overlaid as this C header.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
struct ZSTD_seqSymbol_header {
|
|
fast_mode: u32,
|
|
table_log: u32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct ZSTD_entropyDTables_t {
|
|
ll_table: [ZSTD_seqSymbol; 1 + (1 << LL_FSE_LOG)],
|
|
of_table: [ZSTD_seqSymbol; 1 + (1 << OFF_FSE_LOG)],
|
|
ml_table: [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-owned leaves of `ZSTD_DCtx_s` used by this translation unit.
|
|
///
|
|
/// Every pointer is produced by `zstd_decompress_block.c` under the active C
|
|
/// configuration. This avoids assuming offsets for optional context members
|
|
/// such as `DYNAMIC_BMI2`, fuzzing bounds, and tracing state.
|
|
#[repr(C)]
|
|
pub struct ZSTD_rustBlockCtx {
|
|
llt_ptr: *mut *const ZSTD_seqSymbol,
|
|
mlt_ptr: *mut *const ZSTD_seqSymbol,
|
|
oft_ptr: *mut *const ZSTD_seqSymbol,
|
|
huf_ptr: *mut *const u32,
|
|
entropy: *mut ZSTD_entropyDTables_t,
|
|
workspace: *mut u32,
|
|
workspace_size: usize,
|
|
previous_dst_end: *mut *const u8,
|
|
prefix_start: *mut *const u8,
|
|
virtual_start: *mut *const u8,
|
|
dict_end: *mut *const u8,
|
|
block_size_max: usize,
|
|
is_frame_decompression: *mut c_int,
|
|
lit_entropy: *mut u32,
|
|
fse_entropy: *mut u32,
|
|
bmi2: c_int,
|
|
ddict_is_cold: *mut c_int,
|
|
disable_huf_asm: c_int,
|
|
lit_ptr: *mut *const u8,
|
|
lit_size: *mut usize,
|
|
rle_size: *mut usize,
|
|
lit_buffer: *mut *mut u8,
|
|
lit_buffer_end: *mut *const u8,
|
|
lit_buffer_location: *mut c_int,
|
|
lit_extra_buffer: *mut u8,
|
|
lit_extra_buffer_size: usize,
|
|
sequence_decoder_mode: c_int,
|
|
}
|
|
|
|
unsafe extern "C" {
|
|
fn ZSTD_rust_block_context_init(out: *mut ZSTD_rustBlockCtx, dctx: *mut ZSTD_DCtx);
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn block_context(dctx: *mut ZSTD_DCtx) -> ZSTD_rustBlockCtx {
|
|
let mut ctx = MaybeUninit::<ZSTD_rustBlockCtx>::uninit();
|
|
unsafe { ZSTD_rust_block_context_init(ctx.as_mut_ptr(), dctx) };
|
|
unsafe { ctx.assume_init() }
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
pub struct ZSTD_rustSeq {
|
|
lit_length: usize,
|
|
match_length: usize,
|
|
offset: usize,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct block_properties_t {
|
|
block_type: c_int,
|
|
last_block: u32,
|
|
orig_size: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
struct ZSTD_offsetInfo {
|
|
long_offset_share: u32,
|
|
max_nb_additional_bits: u8,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
enum SequenceDecoder {
|
|
Short,
|
|
Long,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct ZSTD_fseState {
|
|
state: usize,
|
|
table: *const ZSTD_seqSymbol,
|
|
}
|
|
|
|
struct seq_state_t {
|
|
dstream: BIT_DStream_t,
|
|
state_ll: ZSTD_fseState,
|
|
state_off: ZSTD_fseState,
|
|
state_ml: ZSTD_fseState,
|
|
prev_offset: [usize; ZSTD_REP_NUM],
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn entropy(ctx: *mut ZSTD_rustBlockCtx) -> *mut ZSTD_entropyDTables_t {
|
|
unsafe { (*ctx).entropy }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn address_distance(end: *const u8, start: *const u8) -> usize {
|
|
(end as usize).wrapping_sub(start as usize)
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn ptr_add(ptr: *mut u8, amount: usize) -> *mut u8 {
|
|
if ptr.is_null() {
|
|
debug_assert_eq!(amount, 0);
|
|
ptr
|
|
} else {
|
|
unsafe { ptr.add(amount) }
|
|
}
|
|
}
|
|
|
|
#[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 seq_header(table: *const ZSTD_seqSymbol) -> ZSTD_seqSymbol_header {
|
|
unsafe { table.cast::<ZSTD_seqSymbol_header>().read_unaligned() }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_seq_header(table: *mut ZSTD_seqSymbol, header: ZSTD_seqSymbol_header) {
|
|
unsafe {
|
|
table
|
|
.cast::<ZSTD_seqSymbol_header>()
|
|
.write_unaligned(header)
|
|
};
|
|
}
|
|
|
|
#[inline]
|
|
fn stream_accumulator_min() -> usize {
|
|
if MEM_32bits() {
|
|
STREAM_ACCUMULATOR_MIN_32
|
|
} else {
|
|
STREAM_ACCUMULATOR_MIN_64
|
|
}
|
|
}
|
|
|
|
/// Port of the C decoder's `ZSTD_getOffsetInfo()` policy helper.
|
|
///
|
|
/// `long_offset_share` is scaled to the full offset-table log, so the caller
|
|
/// can compare tables with different table logs using the same thresholds.
|
|
/// The table is valid when this helper is reached from sequence-header
|
|
/// decoding; the defensive checks keep the helper harmless in focused tests
|
|
/// and preserve the C zero-sequence behavior.
|
|
#[inline]
|
|
unsafe fn get_offset_info(off_table: *const ZSTD_seqSymbol, nb_seq: c_int) -> ZSTD_offsetInfo {
|
|
if nb_seq == 0 || off_table.is_null() {
|
|
return ZSTD_offsetInfo::default();
|
|
}
|
|
let table_log = unsafe { seq_header(off_table).table_log };
|
|
if table_log > OFF_FSE_LOG {
|
|
return ZSTD_offsetInfo::default();
|
|
}
|
|
let table_size = 1usize << table_log;
|
|
let table = unsafe { off_table.add(1) };
|
|
let mut info = ZSTD_offsetInfo::default();
|
|
for index in 0..table_size {
|
|
let additional_bits = unsafe { (*table.add(index)).nb_additional_bits };
|
|
info.max_nb_additional_bits = info.max_nb_additional_bits.max(additional_bits);
|
|
if additional_bits > LONG_OFFSET_ADDITIONAL_BITS {
|
|
info.long_offset_share = info.long_offset_share.wrapping_add(1);
|
|
}
|
|
}
|
|
info.long_offset_share <<= OFF_FSE_LOG - table_log;
|
|
info
|
|
}
|
|
|
|
#[inline]
|
|
fn choose_sequence_decoder(
|
|
mode: c_int,
|
|
ddict_is_cold: c_int,
|
|
potential_long_offsets: bool,
|
|
total_history_size: usize,
|
|
nb_seq: c_int,
|
|
offset_info: ZSTD_offsetInfo,
|
|
) -> SequenceDecoder {
|
|
match mode {
|
|
SEQUENCE_DECODER_FORCE_SHORT => return SequenceDecoder::Short,
|
|
SEQUENCE_DECODER_FORCE_LONG => return SequenceDecoder::Long,
|
|
SEQUENCE_DECODER_RUNTIME => {}
|
|
_ => {}
|
|
}
|
|
|
|
let mut use_prefetch_decoder = ddict_is_cold != 0;
|
|
if !use_prefetch_decoder
|
|
&& (potential_long_offsets
|
|
|| (total_history_size > LONG_OFFSET_HISTORY_THRESHOLD && nb_seq > 8))
|
|
{
|
|
let minimum_share = if MEM_32bits() {
|
|
LONG_OFFSET_MIN_SHARE_32
|
|
} else {
|
|
LONG_OFFSET_MIN_SHARE_64
|
|
};
|
|
use_prefetch_decoder = offset_info.long_offset_share >= minimum_share;
|
|
}
|
|
if use_prefetch_decoder {
|
|
SequenceDecoder::Long
|
|
} else {
|
|
SequenceDecoder::Short
|
|
}
|
|
}
|
|
|
|
#[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 fill_bytes(dst: *mut u8, value: u8, len: usize) {
|
|
if len != 0 {
|
|
unsafe { ptr::write_bytes(dst, value, len) };
|
|
}
|
|
}
|
|
|
|
/// Copy a match using forward byte semantics, which is required for repeated
|
|
/// short-offset matches (unlike `memmove`, which would not expand overlap).
|
|
#[inline]
|
|
unsafe fn copy_match(mut dst: *mut u8, mut src: *const u8, len: usize) {
|
|
for _ in 0..len {
|
|
unsafe { dst.write(src.read()) };
|
|
dst = unsafe { dst.add(1) };
|
|
src = unsafe { src.add(1) };
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_getcBlockSize(
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
bp_ptr: *mut c_void,
|
|
) -> usize {
|
|
if src_size < ZSTD_BLOCK_HEADER_SIZE {
|
|
return ERROR(ZstdErrorCode::SrcSizeWrong);
|
|
}
|
|
let header = unsafe { MEM_readLE24(src) };
|
|
let bp = bp_ptr.cast::<block_properties_t>();
|
|
unsafe {
|
|
(*bp).last_block = header & 1;
|
|
(*bp).block_type = ((header >> 1) & 3) as c_int;
|
|
(*bp).orig_size = header >> 3;
|
|
}
|
|
if unsafe { (*bp).block_type } == 1 {
|
|
return 1;
|
|
}
|
|
if unsafe { (*bp).block_type } == 3 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
(header >> 3) as usize
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn block_size_max(ctx: *const ZSTD_rustBlockCtx) -> usize {
|
|
let value = unsafe {
|
|
if *(*ctx).is_frame_decompression != 0 {
|
|
(*ctx).block_size_max
|
|
} else {
|
|
ZSTD_BLOCKSIZE_MAX
|
|
}
|
|
};
|
|
debug_assert!(value <= ZSTD_BLOCKSIZE_MAX);
|
|
value
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn total_history_size(
|
|
ctx: *const ZSTD_rustBlockCtx,
|
|
dst: *mut u8,
|
|
dst_capacity: usize,
|
|
) -> usize {
|
|
let block_size = min(dst_capacity, unsafe { block_size_max(ctx) });
|
|
let history_end = unsafe { ptr_add(dst, block_size) };
|
|
unsafe { address_distance(history_end.cast_const(), *(*ctx).virtual_start) }
|
|
}
|
|
|
|
unsafe fn allocate_literals_buffer(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
dst: *mut u8,
|
|
dst_capacity: usize,
|
|
lit_size: usize,
|
|
streaming: c_int,
|
|
expected_write_size: usize,
|
|
split_immediately: bool,
|
|
) {
|
|
let block_max = unsafe { block_size_max(ctx) };
|
|
debug_assert!(lit_size <= block_max);
|
|
debug_assert!(*unsafe { (*ctx).is_frame_decompression } != 0 || streaming == NOT_STREAMING);
|
|
if streaming == NOT_STREAMING
|
|
&& dst_capacity
|
|
> block_max
|
|
.saturating_add(WILDCOPY_OVERLENGTH)
|
|
.saturating_add(lit_size)
|
|
.saturating_add(WILDCOPY_OVERLENGTH)
|
|
{
|
|
let buffer = unsafe { ptr_add(dst, block_max + WILDCOPY_OVERLENGTH) };
|
|
unsafe {
|
|
*(*ctx).lit_buffer = buffer;
|
|
*(*ctx).lit_buffer_end = buffer.add(lit_size);
|
|
*(*ctx).lit_buffer_location = ZSTD_IN_DST;
|
|
}
|
|
} else if lit_size <= unsafe { (*ctx).lit_extra_buffer_size } {
|
|
let buffer = unsafe { (*ctx).lit_extra_buffer };
|
|
unsafe {
|
|
*(*ctx).lit_buffer = buffer;
|
|
*(*ctx).lit_buffer_end = buffer.add(lit_size);
|
|
*(*ctx).lit_buffer_location = ZSTD_NOT_IN_DST;
|
|
}
|
|
} else {
|
|
let extra = unsafe { (*ctx).lit_extra_buffer_size };
|
|
debug_assert!(block_max > extra);
|
|
let buffer = if split_immediately {
|
|
unsafe {
|
|
ptr_add(
|
|
dst,
|
|
expected_write_size - lit_size + extra - WILDCOPY_OVERLENGTH,
|
|
)
|
|
}
|
|
} else {
|
|
unsafe { ptr_add(dst, expected_write_size - lit_size) }
|
|
};
|
|
unsafe {
|
|
*(*ctx).lit_buffer = buffer;
|
|
*(*ctx).lit_buffer_end = if split_immediately {
|
|
buffer.add(lit_size - extra)
|
|
} else {
|
|
ptr_add(dst, expected_write_size).cast_const()
|
|
};
|
|
*(*ctx).lit_buffer_location = ZSTD_SPLIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsafe fn decode_literals_block(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
src: *const u8,
|
|
src_size: usize,
|
|
dst: *mut u8,
|
|
dst_capacity: usize,
|
|
streaming: c_int,
|
|
) -> usize {
|
|
if src_size < MIN_CBLOCK_SIZE {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
let lit_type = unsafe { *src & 3 } as c_int;
|
|
let block_max = unsafe { block_size_max(ctx) };
|
|
match lit_type {
|
|
SET_REPEAT | SET_COMPRESSED => {
|
|
if lit_type == SET_REPEAT && unsafe { *(*ctx).lit_entropy } == 0 {
|
|
return ERROR(ZstdErrorCode::DictionaryCorrupted);
|
|
}
|
|
if src_size < 5 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
let lhl_code = unsafe { (*src >> 2) & 3 };
|
|
let header = unsafe { crate::mem::MEM_readLE32(src.cast()) };
|
|
let (header_size, lit_size, lit_c_size, single_stream) = match lhl_code {
|
|
0 | 1 => (
|
|
3usize,
|
|
((header >> 4) & 0x3ff) as usize,
|
|
((header >> 14) & 0x3ff) as usize,
|
|
lhl_code == 0,
|
|
),
|
|
2 => (
|
|
4usize,
|
|
((header >> 4) & 0x3fff) as usize,
|
|
(header >> 18) as usize,
|
|
false,
|
|
),
|
|
_ => (
|
|
5usize,
|
|
((header >> 4) & 0x3ffff) as usize,
|
|
((header >> 22) as usize).wrapping_add((unsafe { *src.add(4) } as usize) << 10),
|
|
false,
|
|
),
|
|
};
|
|
if (lit_size != 0 && dst.is_null())
|
|
|| lit_size > block_max
|
|
|| (!single_stream && lit_size < MIN_LITERALS_FOR_4_STREAMS)
|
|
|| header_size.checked_add(lit_c_size).is_none()
|
|
|| header_size + lit_c_size > src_size
|
|
|| min(block_max, dst_capacity) < lit_size
|
|
{
|
|
return if lit_size != 0 && dst.is_null() || min(block_max, dst_capacity) < lit_size
|
|
{
|
|
ERROR(ZstdErrorCode::DstSizeTooSmall)
|
|
} else {
|
|
ERROR(ZstdErrorCode::CorruptionDetected)
|
|
};
|
|
}
|
|
unsafe {
|
|
allocate_literals_buffer(
|
|
ctx,
|
|
dst,
|
|
dst_capacity,
|
|
lit_size,
|
|
streaming,
|
|
min(block_max, dst_capacity),
|
|
false,
|
|
);
|
|
}
|
|
let flags = (if unsafe { (*ctx).bmi2 } != 0 {
|
|
HUF_FLAGS_BMI2
|
|
} else {
|
|
0
|
|
}) | (if unsafe { (*ctx).disable_huf_asm } != 0 {
|
|
HUF_FLAGS_DISABLE_ASM
|
|
} else {
|
|
0
|
|
});
|
|
let lit_buffer = unsafe { *(*ctx).lit_buffer };
|
|
let huf_result = if lit_type == SET_REPEAT {
|
|
if single_stream {
|
|
unsafe {
|
|
HUF_decompress1X_usingDTable(
|
|
lit_buffer.cast(),
|
|
lit_size,
|
|
src.add(header_size).cast(),
|
|
lit_c_size,
|
|
*(*ctx).huf_ptr,
|
|
flags,
|
|
)
|
|
}
|
|
} else {
|
|
unsafe {
|
|
HUF_decompress4X_usingDTable(
|
|
lit_buffer.cast(),
|
|
lit_size,
|
|
src.add(header_size).cast(),
|
|
lit_c_size,
|
|
*(*ctx).huf_ptr,
|
|
flags,
|
|
)
|
|
}
|
|
}
|
|
} else if single_stream {
|
|
#[cfg(feature = "huf-force-decompress-x2")]
|
|
{
|
|
unsafe {
|
|
HUF_decompress1X_DCtx_wksp(
|
|
(*entropy(ctx)).huf_table.as_mut_ptr(),
|
|
lit_buffer.cast(),
|
|
lit_size,
|
|
src.add(header_size).cast(),
|
|
lit_c_size,
|
|
(*ctx).workspace.cast(),
|
|
(*ctx).workspace_size,
|
|
flags,
|
|
)
|
|
}
|
|
}
|
|
#[cfg(not(feature = "huf-force-decompress-x2"))]
|
|
{
|
|
unsafe {
|
|
HUF_decompress1X1_DCtx_wksp(
|
|
(*entropy(ctx)).huf_table.as_mut_ptr(),
|
|
lit_buffer.cast(),
|
|
lit_size,
|
|
src.add(header_size).cast(),
|
|
lit_c_size,
|
|
(*ctx).workspace.cast(),
|
|
(*ctx).workspace_size,
|
|
flags,
|
|
)
|
|
}
|
|
}
|
|
} else {
|
|
unsafe {
|
|
HUF_decompress4X_hufOnly_wksp(
|
|
(*entropy(ctx)).huf_table.as_mut_ptr(),
|
|
lit_buffer.cast(),
|
|
lit_size,
|
|
src.add(header_size).cast(),
|
|
lit_c_size,
|
|
(*ctx).workspace.cast(),
|
|
(*ctx).workspace_size,
|
|
flags,
|
|
)
|
|
}
|
|
};
|
|
if ERR_isError(huf_result) {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
if unsafe { *(*ctx).lit_buffer_location } == ZSTD_SPLIT {
|
|
let extra = unsafe { (*ctx).lit_extra_buffer_size };
|
|
unsafe {
|
|
copy_bytes(
|
|
(*ctx).lit_extra_buffer,
|
|
(*(*ctx).lit_buffer_end).sub(extra),
|
|
extra,
|
|
);
|
|
ptr::copy(
|
|
*(*ctx).lit_buffer,
|
|
(*(*ctx).lit_buffer).add(extra - WILDCOPY_OVERLENGTH),
|
|
lit_size - extra,
|
|
);
|
|
*(*ctx).lit_buffer = (*(*ctx).lit_buffer).add(extra - WILDCOPY_OVERLENGTH);
|
|
*(*ctx).lit_buffer_end = (*(*ctx).lit_buffer_end).sub(WILDCOPY_OVERLENGTH);
|
|
}
|
|
}
|
|
unsafe {
|
|
*(*ctx).lit_ptr = *(*ctx).lit_buffer;
|
|
*(*ctx).lit_size = lit_size;
|
|
*(*ctx).lit_entropy = 1;
|
|
if lit_type == SET_COMPRESSED {
|
|
*(*ctx).huf_ptr = (*entropy(ctx)).huf_table.as_ptr();
|
|
}
|
|
}
|
|
header_size + lit_c_size
|
|
}
|
|
SET_BASIC => {
|
|
let lhl_code = unsafe { (*src >> 2) & 3 };
|
|
let (header_size, lit_size) = match lhl_code {
|
|
0 | 2 => (1usize, (unsafe { *src } >> 3) as usize),
|
|
1 => (2usize, (unsafe { MEM_readLE16(src.cast()) } >> 4) as usize),
|
|
_ => {
|
|
if src_size < 3 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
(3usize, (unsafe { MEM_readLE24(src.cast()) } >> 4) as usize)
|
|
}
|
|
};
|
|
if (lit_size != 0 && dst.is_null())
|
|
|| lit_size > block_max
|
|
|| min(block_max, dst_capacity) < lit_size
|
|
{
|
|
return if lit_size != 0 && dst.is_null() || min(block_max, dst_capacity) < lit_size
|
|
{
|
|
ERROR(ZstdErrorCode::DstSizeTooSmall)
|
|
} else {
|
|
ERROR(ZstdErrorCode::CorruptionDetected)
|
|
};
|
|
}
|
|
unsafe {
|
|
allocate_literals_buffer(
|
|
ctx,
|
|
dst,
|
|
dst_capacity,
|
|
lit_size,
|
|
streaming,
|
|
min(block_max, dst_capacity),
|
|
true,
|
|
);
|
|
}
|
|
if header_size
|
|
.checked_add(lit_size)
|
|
.and_then(|size| size.checked_add(WILDCOPY_OVERLENGTH))
|
|
.is_none_or(|size| size > src_size)
|
|
{
|
|
if header_size
|
|
.checked_add(lit_size)
|
|
.is_none_or(|size| size > src_size)
|
|
{
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
unsafe {
|
|
if *(*ctx).lit_buffer_location == ZSTD_SPLIT {
|
|
let extra = (*ctx).lit_extra_buffer_size;
|
|
copy_bytes(*(*ctx).lit_buffer, src.add(header_size), lit_size - extra);
|
|
copy_bytes(
|
|
(*ctx).lit_extra_buffer,
|
|
src.add(header_size + lit_size - extra),
|
|
extra,
|
|
);
|
|
} else {
|
|
copy_bytes(*(*ctx).lit_buffer, src.add(header_size), lit_size);
|
|
}
|
|
*(*ctx).lit_ptr = *(*ctx).lit_buffer;
|
|
*(*ctx).lit_size = lit_size;
|
|
}
|
|
return header_size + lit_size;
|
|
}
|
|
unsafe {
|
|
*(*ctx).lit_ptr = src.add(header_size);
|
|
*(*ctx).lit_size = lit_size;
|
|
*(*ctx).lit_buffer_end = src.add(header_size + lit_size);
|
|
*(*ctx).lit_buffer_location = ZSTD_NOT_IN_DST;
|
|
}
|
|
header_size + lit_size
|
|
}
|
|
SET_RLE => {
|
|
let lhl_code = unsafe { (*src >> 2) & 3 };
|
|
let (header_size, lit_size) = match lhl_code {
|
|
0 | 2 => (1usize, (unsafe { *src } >> 3) as usize),
|
|
1 => {
|
|
if src_size < 3 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
(2usize, (unsafe { MEM_readLE16(src.cast()) } >> 4) as usize)
|
|
}
|
|
_ => {
|
|
if src_size < 4 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
(3usize, (unsafe { MEM_readLE24(src.cast()) } >> 4) as usize)
|
|
}
|
|
};
|
|
if (lit_size != 0 && dst.is_null())
|
|
|| lit_size > block_max
|
|
|| min(block_max, dst_capacity) < lit_size
|
|
{
|
|
return if lit_size != 0 && dst.is_null() || min(block_max, dst_capacity) < lit_size
|
|
{
|
|
ERROR(ZstdErrorCode::DstSizeTooSmall)
|
|
} else {
|
|
ERROR(ZstdErrorCode::CorruptionDetected)
|
|
};
|
|
}
|
|
unsafe {
|
|
allocate_literals_buffer(
|
|
ctx,
|
|
dst,
|
|
dst_capacity,
|
|
lit_size,
|
|
streaming,
|
|
min(block_max, dst_capacity),
|
|
true,
|
|
);
|
|
let value = *src.add(header_size);
|
|
if *(*ctx).lit_buffer_location == ZSTD_SPLIT {
|
|
let extra = (*ctx).lit_extra_buffer_size;
|
|
fill_bytes(*(*ctx).lit_buffer, value, lit_size - extra);
|
|
fill_bytes((*ctx).lit_extra_buffer, value, extra);
|
|
} else {
|
|
fill_bytes(*(*ctx).lit_buffer, value, lit_size);
|
|
}
|
|
*(*ctx).lit_ptr = *(*ctx).lit_buffer;
|
|
*(*ctx).lit_size = lit_size;
|
|
}
|
|
header_size + 1
|
|
}
|
|
_ => ERROR(ZstdErrorCode::CorruptionDetected),
|
|
}
|
|
}
|
|
|
|
unsafe fn build_seq_table_rle(table: *mut ZSTD_seqSymbol, base_value: u32, nb_add_bits: u8) {
|
|
unsafe {
|
|
set_seq_header(
|
|
table,
|
|
ZSTD_seqSymbol_header {
|
|
fast_mode: 0,
|
|
table_log: 0,
|
|
},
|
|
);
|
|
*table.add(1) = ZSTD_seqSymbol {
|
|
next_state: 0,
|
|
nb_additional_bits: nb_add_bits,
|
|
nb_bits: 0,
|
|
base_value,
|
|
};
|
|
}
|
|
}
|
|
|
|
unsafe fn build_fse_table_body(
|
|
table: *mut ZSTD_seqSymbol,
|
|
normalized_counter: *const i16,
|
|
max_symbol_value: u32,
|
|
base_value: *const u32,
|
|
nb_additional_bits: *const u8,
|
|
table_log: u32,
|
|
workspace: *mut u32,
|
|
workspace_size: usize,
|
|
) {
|
|
debug_assert!(max_symbol_value as usize <= MAX_ML);
|
|
debug_assert!(table_log as usize <= MAX_FSE_LOG);
|
|
debug_assert!(
|
|
workspace_size >= ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 * std::mem::size_of::<u32>()
|
|
);
|
|
let table_decode = unsafe { table.add(1) };
|
|
let table_size = 1usize << table_log;
|
|
let symbol_next = workspace.cast::<u16>();
|
|
let spread = unsafe { symbol_next.add(MAX_ML + 1).cast::<u8>() };
|
|
let mut high_threshold = table_size - 1;
|
|
let mut fast_mode = 1u32;
|
|
let large_limit = 1i16 << (table_log - 1);
|
|
for symbol in 0..=max_symbol_value as usize {
|
|
let count = unsafe { *normalized_counter.add(symbol) };
|
|
if count == -1 {
|
|
unsafe {
|
|
(*table_decode.add(high_threshold)).base_value = symbol as u32;
|
|
*symbol_next.add(symbol) = 1;
|
|
}
|
|
high_threshold = high_threshold.wrapping_sub(1);
|
|
} else {
|
|
if count >= large_limit {
|
|
fast_mode = 0;
|
|
}
|
|
debug_assert!(count >= 0);
|
|
unsafe { *symbol_next.add(symbol) = count as u16 };
|
|
}
|
|
}
|
|
unsafe {
|
|
set_seq_header(
|
|
table,
|
|
ZSTD_seqSymbol_header {
|
|
fast_mode,
|
|
table_log,
|
|
},
|
|
);
|
|
}
|
|
let table_mask = table_size - 1;
|
|
let step = (table_size >> 1) + (table_size >> 3) + 3;
|
|
if high_threshold == table_size - 1 {
|
|
let mut pos = 0usize;
|
|
for symbol in 0..=max_symbol_value as usize {
|
|
let count = unsafe { *normalized_counter.add(symbol) };
|
|
debug_assert!(count >= 0);
|
|
for index in 0..count as usize {
|
|
unsafe { *spread.add(pos + index) = symbol as u8 };
|
|
}
|
|
pos += count as usize;
|
|
}
|
|
let mut position = 0usize;
|
|
for symbol_index in 0..table_size {
|
|
unsafe { (*table_decode.add(position)).base_value = *spread.add(symbol_index) as u32 };
|
|
position = (position + step) & table_mask;
|
|
}
|
|
debug_assert_eq!(position, 0);
|
|
} else {
|
|
let mut position = 0usize;
|
|
for symbol in 0..=max_symbol_value as usize {
|
|
let count = unsafe { *normalized_counter.add(symbol) };
|
|
for _ in 0..count.max(0) as usize {
|
|
unsafe { (*table_decode.add(position)).base_value = symbol as u32 };
|
|
position = (position + step) & table_mask;
|
|
while position > high_threshold {
|
|
position = (position + step) & table_mask;
|
|
}
|
|
}
|
|
}
|
|
debug_assert_eq!(position, 0);
|
|
}
|
|
for index in 0..table_size {
|
|
let symbol = unsafe { (*table_decode.add(index)).base_value as usize };
|
|
let next_state = unsafe { *symbol_next.add(symbol) } as u32;
|
|
unsafe { *symbol_next.add(symbol) = next_state.wrapping_add(1) as u16 };
|
|
let nb_bits = table_log - crate::bits::ZSTD_highbit32(next_state);
|
|
unsafe {
|
|
let entry = &mut *table_decode.add(index);
|
|
entry.nb_bits = nb_bits as u8;
|
|
entry.next_state = ((next_state << nb_bits) - table_size as u32) as u16;
|
|
entry.nb_additional_bits = *nb_additional_bits.add(symbol);
|
|
entry.base_value = *base_value.add(symbol);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_buildFSETable_body(
|
|
table: *mut ZSTD_seqSymbol,
|
|
normalized_counter: *const c_short,
|
|
max_symbol_value: c_uint,
|
|
base_value: *const U32,
|
|
nb_additional_bits: *const u8,
|
|
table_log: c_uint,
|
|
workspace: *mut c_void,
|
|
workspace_size: usize,
|
|
) {
|
|
unsafe {
|
|
build_fse_table_body(
|
|
table,
|
|
normalized_counter,
|
|
max_symbol_value,
|
|
base_value,
|
|
nb_additional_bits,
|
|
table_log,
|
|
workspace.cast(),
|
|
workspace_size,
|
|
);
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_buildFSETable(
|
|
table: *mut ZSTD_seqSymbol,
|
|
normalized_counter: *const c_short,
|
|
max_symbol_value: c_uint,
|
|
base_value: *const U32,
|
|
nb_additional_bits: *const u8,
|
|
table_log: c_uint,
|
|
workspace: *mut c_void,
|
|
workspace_size: usize,
|
|
_bmi2: c_int,
|
|
) {
|
|
unsafe {
|
|
build_fse_table_body(
|
|
table,
|
|
normalized_counter,
|
|
max_symbol_value,
|
|
base_value,
|
|
nb_additional_bits,
|
|
table_log,
|
|
workspace.cast(),
|
|
workspace_size,
|
|
);
|
|
}
|
|
}
|
|
|
|
fn build_default_table<const N: usize>(
|
|
normalized: &[i16],
|
|
base: &[u32],
|
|
bits: &[u8],
|
|
max_symbol_value: u32,
|
|
table_log: u32,
|
|
) -> [ZSTD_seqSymbol; N] {
|
|
let mut table = [ZSTD_seqSymbol::default(); N];
|
|
let mut workspace = [0u32; ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32];
|
|
unsafe {
|
|
build_fse_table_body(
|
|
table.as_mut_ptr(),
|
|
normalized.as_ptr(),
|
|
max_symbol_value,
|
|
base.as_ptr(),
|
|
bits.as_ptr(),
|
|
table_log,
|
|
workspace.as_mut_ptr(),
|
|
std::mem::size_of_val(&workspace),
|
|
);
|
|
}
|
|
table
|
|
}
|
|
|
|
static LL_DEFAULT_TABLE: OnceLock<[ZSTD_seqSymbol; 1 + (1 << LL_FSE_LOG)]> = OnceLock::new();
|
|
static OF_DEFAULT_TABLE: OnceLock<[ZSTD_seqSymbol; 1 + (1 << OFF_FSE_LOG)]> = OnceLock::new();
|
|
static ML_DEFAULT_TABLE: OnceLock<[ZSTD_seqSymbol; 1 + (1 << ML_FSE_LOG)]> = OnceLock::new();
|
|
|
|
#[inline]
|
|
fn ll_default_table() -> *const ZSTD_seqSymbol {
|
|
LL_DEFAULT_TABLE
|
|
.get_or_init(|| {
|
|
build_default_table(
|
|
&LL_DEFAULT_NORM,
|
|
&LL_BASE,
|
|
&LL_BITS,
|
|
MAX_LL as u32,
|
|
LL_DEFAULT_NORM_LOG,
|
|
)
|
|
})
|
|
.as_ptr()
|
|
}
|
|
|
|
#[inline]
|
|
fn of_default_table() -> *const ZSTD_seqSymbol {
|
|
OF_DEFAULT_TABLE
|
|
.get_or_init(|| {
|
|
build_default_table(
|
|
&OF_DEFAULT_NORM,
|
|
&OF_BASE,
|
|
&OF_BITS,
|
|
DEFAULT_MAX_OFF as u32,
|
|
OF_DEFAULT_NORM_LOG,
|
|
)
|
|
})
|
|
.as_ptr()
|
|
}
|
|
|
|
#[inline]
|
|
fn ml_default_table() -> *const ZSTD_seqSymbol {
|
|
ML_DEFAULT_TABLE
|
|
.get_or_init(|| {
|
|
build_default_table(
|
|
&ML_DEFAULT_NORM,
|
|
&ML_BASE,
|
|
&ML_BITS,
|
|
MAX_ML as u32,
|
|
ML_DEFAULT_NORM_LOG,
|
|
)
|
|
})
|
|
.as_ptr()
|
|
}
|
|
|
|
unsafe fn build_seq_table(
|
|
table_space: *mut ZSTD_seqSymbol,
|
|
table_ptr: *mut *const ZSTD_seqSymbol,
|
|
table_type: c_int,
|
|
mut max_symbol: u32,
|
|
max_log: u32,
|
|
src: *const u8,
|
|
src_size: usize,
|
|
base: &[u32],
|
|
bits: &[u8],
|
|
default_table: *const ZSTD_seqSymbol,
|
|
repeat_table_available: u32,
|
|
_ddict_is_cold: c_int,
|
|
_nb_seq: c_int,
|
|
workspace: *mut u32,
|
|
workspace_size: usize,
|
|
bmi2: c_int,
|
|
) -> usize {
|
|
match table_type {
|
|
SET_RLE => {
|
|
if src_size == 0 {
|
|
return ERROR(ZstdErrorCode::SrcSizeWrong);
|
|
}
|
|
let symbol = unsafe { *src } as usize;
|
|
if symbol > max_symbol as usize {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
unsafe { build_seq_table_rle(table_space, base[symbol], bits[symbol]) };
|
|
unsafe { *table_ptr = table_space };
|
|
1
|
|
}
|
|
SET_BASIC => {
|
|
unsafe { *table_ptr = default_table };
|
|
0
|
|
}
|
|
SET_REPEAT => {
|
|
if repeat_table_available == 0 {
|
|
ERROR(ZstdErrorCode::CorruptionDetected)
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
SET_COMPRESSED => {
|
|
let mut table_log = 0u32;
|
|
let mut norm = [0i16; MAX_ML + 1];
|
|
let header_size = unsafe {
|
|
FSE_readNCount(
|
|
norm.as_mut_ptr(),
|
|
&mut max_symbol,
|
|
&mut table_log,
|
|
src.cast(),
|
|
src_size,
|
|
)
|
|
};
|
|
if ERR_isError(header_size) || table_log > max_log {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
unsafe {
|
|
build_fse_table_body(
|
|
table_space,
|
|
norm.as_ptr(),
|
|
max_symbol,
|
|
base.as_ptr(),
|
|
bits.as_ptr(),
|
|
table_log,
|
|
workspace,
|
|
workspace_size,
|
|
);
|
|
*table_ptr = table_space;
|
|
}
|
|
let _ = bmi2;
|
|
header_size
|
|
}
|
|
_ => ERROR(ZstdErrorCode::Generic),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_decodeSeqHeaders(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
nb_seq_ptr: *mut c_int,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
) -> usize {
|
|
if src_size < MIN_SEQUENCES_SIZE {
|
|
return ERROR(ZstdErrorCode::SrcSizeWrong);
|
|
}
|
|
let start = src.cast::<u8>();
|
|
let end = unsafe { start.add(src_size) };
|
|
let mut ip = start;
|
|
let mut nb_seq = unsafe { *ip } as usize;
|
|
ip = unsafe { ip.add(1) };
|
|
if nb_seq > 0x7f {
|
|
if nb_seq == 0xff {
|
|
if (ip as usize).wrapping_add(2) > end as usize {
|
|
return ERROR(ZstdErrorCode::SrcSizeWrong);
|
|
}
|
|
nb_seq = unsafe { MEM_readLE16(ip.cast()) as usize } + LONG_NB_SEQ;
|
|
ip = unsafe { ip.add(2) };
|
|
} else {
|
|
if ip >= end {
|
|
return ERROR(ZstdErrorCode::SrcSizeWrong);
|
|
}
|
|
nb_seq = ((nb_seq - 0x80) << 8) + unsafe { *ip } as usize;
|
|
ip = unsafe { ip.add(1) };
|
|
}
|
|
}
|
|
unsafe { *nb_seq_ptr = nb_seq as c_int };
|
|
if nb_seq == 0 {
|
|
return if ip == end {
|
|
unsafe { ip.offset_from(start) as usize }
|
|
} else {
|
|
ERROR(ZstdErrorCode::CorruptionDetected)
|
|
};
|
|
}
|
|
if ip >= end || unsafe { *ip & 3 } != 0 {
|
|
return if ip >= end {
|
|
ERROR(ZstdErrorCode::SrcSizeWrong)
|
|
} else {
|
|
ERROR(ZstdErrorCode::CorruptionDetected)
|
|
};
|
|
}
|
|
let descriptor = unsafe { *ip };
|
|
ip = unsafe { ip.add(1) };
|
|
let ll_type = (descriptor >> 6) as c_int;
|
|
let of_type = ((descriptor >> 4) & 3) as c_int;
|
|
let ml_type = ((descriptor >> 2) & 3) as c_int;
|
|
let entropy = unsafe { entropy(ctx) };
|
|
let repeat = unsafe { *(*ctx).fse_entropy };
|
|
let cold = unsafe { *(*ctx).ddict_is_cold };
|
|
let ll_size = unsafe {
|
|
build_seq_table(
|
|
(*entropy).ll_table.as_mut_ptr(),
|
|
(*ctx).llt_ptr,
|
|
ll_type,
|
|
MAX_LL as u32,
|
|
LL_FSE_LOG,
|
|
ip,
|
|
address_distance(end, ip),
|
|
&LL_BASE,
|
|
&LL_BITS,
|
|
ll_default_table(),
|
|
repeat,
|
|
cold,
|
|
nb_seq as c_int,
|
|
(*ctx).workspace,
|
|
(*ctx).workspace_size,
|
|
(*ctx).bmi2,
|
|
)
|
|
};
|
|
if ERR_isError(ll_size) || ll_size > unsafe { address_distance(end, ip) } {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
ip = unsafe { ip.add(ll_size) };
|
|
let of_size = unsafe {
|
|
build_seq_table(
|
|
(*entropy).of_table.as_mut_ptr(),
|
|
(*ctx).oft_ptr,
|
|
of_type,
|
|
MAX_OFF as u32,
|
|
OFF_FSE_LOG,
|
|
ip,
|
|
address_distance(end, ip),
|
|
&OF_BASE,
|
|
&OF_BITS,
|
|
of_default_table(),
|
|
repeat,
|
|
cold,
|
|
nb_seq as c_int,
|
|
(*ctx).workspace,
|
|
(*ctx).workspace_size,
|
|
(*ctx).bmi2,
|
|
)
|
|
};
|
|
if ERR_isError(of_size) || of_size > unsafe { address_distance(end, ip) } {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
ip = unsafe { ip.add(of_size) };
|
|
let ml_size = unsafe {
|
|
build_seq_table(
|
|
(*entropy).ml_table.as_mut_ptr(),
|
|
(*ctx).mlt_ptr,
|
|
ml_type,
|
|
MAX_ML as u32,
|
|
ML_FSE_LOG,
|
|
ip,
|
|
address_distance(end, ip),
|
|
&ML_BASE,
|
|
&ML_BITS,
|
|
ml_default_table(),
|
|
repeat,
|
|
cold,
|
|
nb_seq as c_int,
|
|
(*ctx).workspace,
|
|
(*ctx).workspace_size,
|
|
(*ctx).bmi2,
|
|
)
|
|
};
|
|
if ERR_isError(ml_size) || ml_size > unsafe { address_distance(end, ip) } {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
ip = unsafe { ip.add(ml_size) };
|
|
unsafe { ip.offset_from(start) as usize }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn init_fse_state(
|
|
state: &mut ZSTD_fseState,
|
|
dstream: *mut BIT_DStream_t,
|
|
table: *const ZSTD_seqSymbol,
|
|
) -> Result<(), usize> {
|
|
if table.is_null() {
|
|
return Err(ERROR(ZstdErrorCode::CorruptionDetected));
|
|
}
|
|
let header = unsafe { seq_header(table) };
|
|
if header.table_log > MAX_FSE_LOG as u32 {
|
|
return Err(ERROR(ZstdErrorCode::CorruptionDetected));
|
|
}
|
|
state.state = unsafe { BIT_readBits(dstream, header.table_log) };
|
|
let _ = unsafe { BIT_reloadDStream(dstream) };
|
|
state.table = unsafe { table.add(1) };
|
|
Ok(())
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn update_fse_state(
|
|
state: &mut ZSTD_fseState,
|
|
dstream: *mut BIT_DStream_t,
|
|
next_state: u16,
|
|
nb_bits: u8,
|
|
) {
|
|
let low_bits = unsafe { BIT_readBits(dstream, nb_bits as u32) };
|
|
state.state = next_state as usize + low_bits;
|
|
}
|
|
|
|
unsafe fn decode_sequence(
|
|
state: &mut seq_state_t,
|
|
long_offsets: bool,
|
|
is_last_sequence: bool,
|
|
) -> ZSTD_rustSeq {
|
|
let ll_info = unsafe { state.state_ll.table.add(state.state_ll.state).read() };
|
|
let ml_info = unsafe { state.state_ml.table.add(state.state_ml.state).read() };
|
|
let of_info = unsafe { state.state_off.table.add(state.state_off.state).read() };
|
|
let ll_bits = ll_info.nb_additional_bits as u32;
|
|
let ml_bits = ml_info.nb_additional_bits as u32;
|
|
let of_bits = of_info.nb_additional_bits as u32;
|
|
let total_bits = ll_bits + ml_bits + of_bits;
|
|
let mut sequence = ZSTD_rustSeq {
|
|
lit_length: ll_info.base_value as usize,
|
|
match_length: ml_info.base_value as usize,
|
|
offset: 0,
|
|
};
|
|
|
|
let offset = if of_bits > 1 {
|
|
let value = if MEM_32bits() && long_offsets && of_bits >= 25 {
|
|
let upper = unsafe { BIT_readBitsFast(&mut state.dstream, of_bits - 5) } << 5;
|
|
let _ = unsafe { BIT_reloadDStream(&mut state.dstream) };
|
|
upper + unsafe { BIT_readBitsFast(&mut state.dstream, 5) }
|
|
} else {
|
|
let value = unsafe { BIT_readBitsFast(&mut state.dstream, of_bits) };
|
|
if MEM_32bits() {
|
|
let _ = unsafe { BIT_reloadDStream(&mut state.dstream) };
|
|
}
|
|
value
|
|
};
|
|
let value = (of_info.base_value as usize).wrapping_add(value);
|
|
state.prev_offset[2] = state.prev_offset[1];
|
|
state.prev_offset[1] = state.prev_offset[0];
|
|
state.prev_offset[0] = value;
|
|
value
|
|
} else {
|
|
let ll_zero = ll_info.base_value == 0;
|
|
if of_bits == 0 {
|
|
let value = state.prev_offset[usize::from(ll_zero)];
|
|
state.prev_offset[1] = state.prev_offset[usize::from(!ll_zero)];
|
|
state.prev_offset[0] = value;
|
|
value
|
|
} else {
|
|
let code = (of_info.base_value as usize)
|
|
.wrapping_add(usize::from(ll_zero))
|
|
.wrapping_add(unsafe { BIT_readBitsFast(&mut state.dstream, 1) });
|
|
let mut value = if code == 3 {
|
|
state.prev_offset[0].wrapping_sub(1)
|
|
} else if code < ZSTD_REP_NUM {
|
|
state.prev_offset[code]
|
|
} else {
|
|
usize::MAX
|
|
};
|
|
value = value.wrapping_sub(usize::from(value == 0));
|
|
if code != 1 {
|
|
state.prev_offset[2] = state.prev_offset[1];
|
|
}
|
|
state.prev_offset[1] = state.prev_offset[0];
|
|
state.prev_offset[0] = value;
|
|
value
|
|
}
|
|
};
|
|
sequence.offset = offset;
|
|
if ml_bits != 0 {
|
|
sequence.match_length = sequence
|
|
.match_length
|
|
.wrapping_add(unsafe { BIT_readBitsFast(&mut state.dstream, ml_bits) });
|
|
}
|
|
if MEM_32bits() && ml_bits + ll_bits >= 20 {
|
|
let _ = unsafe { BIT_reloadDStream(&mut state.dstream) };
|
|
}
|
|
if MEM_64bits() && total_bits >= 30 {
|
|
let _ = unsafe { BIT_reloadDStream(&mut state.dstream) };
|
|
}
|
|
if ll_bits != 0 {
|
|
sequence.lit_length = sequence
|
|
.lit_length
|
|
.wrapping_add(unsafe { BIT_readBitsFast(&mut state.dstream, ll_bits) });
|
|
}
|
|
if MEM_32bits() {
|
|
let _ = unsafe { BIT_reloadDStream(&mut state.dstream) };
|
|
}
|
|
if !is_last_sequence {
|
|
unsafe {
|
|
update_fse_state(
|
|
&mut state.state_ll,
|
|
&mut state.dstream,
|
|
ll_info.next_state,
|
|
ll_info.nb_bits,
|
|
);
|
|
update_fse_state(
|
|
&mut state.state_ml,
|
|
&mut state.dstream,
|
|
ml_info.next_state,
|
|
ml_info.nb_bits,
|
|
);
|
|
}
|
|
if MEM_32bits() {
|
|
let _ = unsafe { BIT_reloadDStream(&mut state.dstream) };
|
|
}
|
|
unsafe {
|
|
update_fse_state(
|
|
&mut state.state_off,
|
|
&mut state.dstream,
|
|
of_info.next_state,
|
|
of_info.nb_bits,
|
|
);
|
|
}
|
|
let _ = unsafe { BIT_reloadDStream(&mut state.dstream) };
|
|
}
|
|
sequence
|
|
}
|
|
|
|
unsafe fn exec_sequence(
|
|
op: *mut u8,
|
|
oend: *mut u8,
|
|
sequence: ZSTD_rustSeq,
|
|
lit_ptr: &mut *const u8,
|
|
lit_limit: *const u8,
|
|
prefix_start: *const u8,
|
|
virtual_start: *const u8,
|
|
dict_end: *const u8,
|
|
split_literals: bool,
|
|
) -> usize {
|
|
let sequence_length = match sequence.lit_length.checked_add(sequence.match_length) {
|
|
Some(length) => length,
|
|
None => return ERROR(ZstdErrorCode::DstSizeTooSmall),
|
|
};
|
|
let output_capacity = unsafe { address_distance(oend.cast_const(), op.cast_const()) };
|
|
if sequence_length > output_capacity {
|
|
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
|
}
|
|
let literal_available = unsafe { address_distance(lit_limit, *lit_ptr) };
|
|
if sequence.lit_length > literal_available {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
let lit_end = unsafe { ptr_add(op, sequence.lit_length) };
|
|
if split_literals
|
|
&& (op as usize) > (*lit_ptr as usize)
|
|
&& (op as usize) < (*lit_ptr as usize).wrapping_add(sequence.lit_length)
|
|
{
|
|
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
|
}
|
|
unsafe { copy_bytes(op, *lit_ptr, sequence.lit_length) };
|
|
*lit_ptr = unsafe { const_ptr_add(*lit_ptr, sequence.lit_length) };
|
|
if sequence.offset == 0 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
let prefix_history = unsafe { address_distance(lit_end.cast_const(), prefix_start) };
|
|
let mut output = lit_end;
|
|
let mut match_length = sequence.match_length;
|
|
if sequence.offset > prefix_history {
|
|
let virtual_history = unsafe { address_distance(lit_end.cast_const(), virtual_start) };
|
|
if sequence.offset > virtual_history || dict_end.is_null() {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
let before_prefix = sequence.offset - prefix_history;
|
|
let match_ptr = unsafe { dict_end.sub(before_prefix) };
|
|
let dict_available = unsafe { address_distance(dict_end, match_ptr) };
|
|
let first_length = min(match_length, dict_available);
|
|
unsafe { copy_bytes(output, match_ptr, first_length) };
|
|
output = unsafe { output.add(first_length) };
|
|
match_length -= first_length;
|
|
if match_length != 0 {
|
|
if prefix_start.is_null() {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
unsafe { copy_match(output, prefix_start, match_length) };
|
|
}
|
|
} else {
|
|
let match_ptr = unsafe { lit_end.sub(sequence.offset) };
|
|
if match_ptr.is_null() {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
unsafe { copy_match(output, match_ptr, match_length) };
|
|
}
|
|
sequence_length
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_execSequenceEnd(
|
|
op: *mut u8,
|
|
oend: *mut u8,
|
|
sequence: ZSTD_rustSeq,
|
|
lit_ptr: *mut *const u8,
|
|
lit_limit: *const u8,
|
|
prefix_start: *const u8,
|
|
virtual_start: *const u8,
|
|
dict_end: *const u8,
|
|
) -> usize {
|
|
unsafe {
|
|
exec_sequence(
|
|
op,
|
|
oend,
|
|
sequence,
|
|
&mut *lit_ptr,
|
|
lit_limit,
|
|
prefix_start,
|
|
virtual_start,
|
|
dict_end,
|
|
false,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_execSequenceEndSplitLitBuffer(
|
|
op: *mut u8,
|
|
oend: *mut u8,
|
|
_oend_w: *const u8,
|
|
sequence: ZSTD_rustSeq,
|
|
lit_ptr: *mut *const u8,
|
|
lit_limit: *const u8,
|
|
prefix_start: *const u8,
|
|
virtual_start: *const u8,
|
|
dict_end: *const u8,
|
|
) -> usize {
|
|
unsafe {
|
|
exec_sequence(
|
|
op,
|
|
oend,
|
|
sequence,
|
|
&mut *lit_ptr,
|
|
lit_limit,
|
|
prefix_start,
|
|
virtual_start,
|
|
dict_end,
|
|
true,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_execSequence(
|
|
op: *mut u8,
|
|
oend: *mut u8,
|
|
sequence: ZSTD_rustSeq,
|
|
lit_ptr: *mut *const u8,
|
|
lit_limit: *const u8,
|
|
prefix_start: *const u8,
|
|
virtual_start: *const u8,
|
|
dict_end: *const u8,
|
|
) -> usize {
|
|
unsafe {
|
|
exec_sequence(
|
|
op,
|
|
oend,
|
|
sequence,
|
|
&mut *lit_ptr,
|
|
lit_limit,
|
|
prefix_start,
|
|
virtual_start,
|
|
dict_end,
|
|
false,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_execSequenceSplitLitBuffer(
|
|
op: *mut u8,
|
|
oend: *mut u8,
|
|
_oend_w: *const u8,
|
|
sequence: ZSTD_rustSeq,
|
|
lit_ptr: *mut *const u8,
|
|
lit_limit: *const u8,
|
|
prefix_start: *const u8,
|
|
virtual_start: *const u8,
|
|
dict_end: *const u8,
|
|
) -> usize {
|
|
unsafe {
|
|
exec_sequence(
|
|
op,
|
|
oend,
|
|
sequence,
|
|
&mut *lit_ptr,
|
|
lit_limit,
|
|
prefix_start,
|
|
virtual_start,
|
|
dict_end,
|
|
true,
|
|
)
|
|
}
|
|
}
|
|
|
|
unsafe fn decompress_sequences(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
dst: *mut u8,
|
|
dst_capacity: usize,
|
|
sequence_start: *const u8,
|
|
sequence_size: usize,
|
|
nb_seq: c_int,
|
|
long_offsets: bool,
|
|
use_prefetch_decoder: bool,
|
|
) -> usize {
|
|
if nb_seq < 0 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
let mut location = unsafe { *(*ctx).lit_buffer_location };
|
|
let mut op = dst;
|
|
let mut oend = if location == ZSTD_NOT_IN_DST || location == ZSTD_SPLIT {
|
|
unsafe { ptr_add(dst, dst_capacity) }
|
|
} else {
|
|
unsafe { *(*ctx).lit_buffer }
|
|
};
|
|
let mut lit_ptr = unsafe { *(*ctx).lit_ptr };
|
|
let mut lit_limit = if location == ZSTD_SPLIT {
|
|
unsafe { *(*ctx).lit_buffer_end }
|
|
} else {
|
|
unsafe { const_ptr_add(lit_ptr, *(*ctx).lit_size) }
|
|
};
|
|
let prefix_start = unsafe { *(*ctx).prefix_start };
|
|
let virtual_start = unsafe { *(*ctx).virtual_start };
|
|
let dict_end = unsafe { *(*ctx).dict_end };
|
|
if nb_seq != 0 {
|
|
if dst.is_null() {
|
|
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
|
}
|
|
let mut state = seq_state_t {
|
|
dstream: unsafe { MaybeUninit::zeroed().assume_init() },
|
|
state_ll: ZSTD_fseState {
|
|
state: 0,
|
|
table: ptr::null(),
|
|
},
|
|
state_off: ZSTD_fseState {
|
|
state: 0,
|
|
table: ptr::null(),
|
|
},
|
|
state_ml: ZSTD_fseState {
|
|
state: 0,
|
|
table: ptr::null(),
|
|
},
|
|
prev_offset: unsafe { (*entropy(ctx)).rep.map(|value| value as usize) },
|
|
};
|
|
unsafe { *(*ctx).fse_entropy = 1 };
|
|
let init =
|
|
unsafe { BIT_initDStream(&mut state.dstream, sequence_start.cast(), sequence_size) };
|
|
if ERR_isError(init) {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
if let Err(error) =
|
|
unsafe { init_fse_state(&mut state.state_ll, &mut state.dstream, *(*ctx).llt_ptr) }
|
|
{
|
|
return error;
|
|
}
|
|
if let Err(error) =
|
|
unsafe { init_fse_state(&mut state.state_off, &mut state.dstream, *(*ctx).oft_ptr) }
|
|
{
|
|
return error;
|
|
}
|
|
if let Err(error) =
|
|
unsafe { init_fse_state(&mut state.state_ml, &mut state.dstream, *(*ctx).mlt_ptr) }
|
|
{
|
|
return error;
|
|
}
|
|
let mut prefetch_pos = 0usize;
|
|
for remaining in (1..=nb_seq as usize).rev() {
|
|
let mut sequence = unsafe { decode_sequence(&mut state, long_offsets, remaining == 1) };
|
|
if use_prefetch_decoder {
|
|
prefetch_pos =
|
|
unsafe { ZSTD_prefetchMatch(prefetch_pos, sequence, prefix_start, dict_end) };
|
|
}
|
|
if location == ZSTD_SPLIT
|
|
&& sequence.lit_length > unsafe { address_distance(lit_limit, lit_ptr) }
|
|
{
|
|
let leftover = unsafe { address_distance(lit_limit, lit_ptr) };
|
|
if leftover > unsafe { address_distance(oend.cast_const(), op.cast_const()) } {
|
|
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
|
}
|
|
unsafe { copy_bytes(op, lit_ptr, leftover) };
|
|
op = unsafe { op.add(leftover) };
|
|
sequence.lit_length -= leftover;
|
|
lit_ptr = unsafe { (*ctx).lit_extra_buffer.cast_const() };
|
|
lit_limit = unsafe {
|
|
const_ptr_add(
|
|
(*ctx).lit_extra_buffer.cast_const(),
|
|
(*ctx).lit_extra_buffer_size,
|
|
)
|
|
};
|
|
location = ZSTD_NOT_IN_DST;
|
|
unsafe { *(*ctx).lit_buffer_location = ZSTD_NOT_IN_DST };
|
|
}
|
|
let decoded = unsafe {
|
|
exec_sequence(
|
|
op,
|
|
oend,
|
|
sequence,
|
|
&mut lit_ptr,
|
|
lit_limit,
|
|
prefix_start,
|
|
virtual_start,
|
|
dict_end,
|
|
location == ZSTD_SPLIT,
|
|
)
|
|
};
|
|
if ERR_isError(decoded) {
|
|
return decoded;
|
|
}
|
|
op = unsafe { op.add(decoded) };
|
|
}
|
|
if unsafe { BIT_endOfDStream(&state.dstream) } == 0 {
|
|
return ERROR(ZstdErrorCode::CorruptionDetected);
|
|
}
|
|
unsafe { (*entropy(ctx)).rep = state.prev_offset.map(|value| value as u32) };
|
|
}
|
|
if location == ZSTD_SPLIT {
|
|
let first_size = unsafe { address_distance(lit_limit, lit_ptr) };
|
|
if first_size > unsafe { address_distance(oend.cast_const(), op.cast_const()) } {
|
|
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
|
}
|
|
unsafe { copy_bytes(op, lit_ptr, first_size) };
|
|
op = unsafe { op.add(first_size) };
|
|
lit_ptr = unsafe { (*ctx).lit_extra_buffer.cast_const() };
|
|
lit_limit = unsafe {
|
|
const_ptr_add(
|
|
(*ctx).lit_extra_buffer.cast_const(),
|
|
(*ctx).lit_extra_buffer_size,
|
|
)
|
|
};
|
|
unsafe { *(*ctx).lit_buffer_location = ZSTD_NOT_IN_DST };
|
|
oend = unsafe { ptr_add(dst, dst_capacity) };
|
|
}
|
|
let last_size = unsafe { address_distance(lit_limit, lit_ptr) };
|
|
if last_size > unsafe { address_distance(oend.cast_const(), op.cast_const()) } {
|
|
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
|
}
|
|
unsafe { copy_bytes(op, lit_ptr, last_size) };
|
|
op = unsafe { op.add(last_size) };
|
|
unsafe { address_distance(op.cast_const(), dst.cast_const()) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_prefetchMatch(
|
|
prefetch_pos: usize,
|
|
sequence: ZSTD_rustSeq,
|
|
_prefix_start: *const u8,
|
|
_dict_end: *const u8,
|
|
) -> usize {
|
|
prefetch_pos
|
|
.wrapping_add(sequence.lit_length)
|
|
.wrapping_add(sequence.match_length)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_decodeLiteralsBlock_wrapper(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
dst: *mut c_void,
|
|
dst_capacity: usize,
|
|
) -> usize {
|
|
unsafe {
|
|
*(*ctx).is_frame_decompression = 0;
|
|
decode_literals_block(
|
|
ctx,
|
|
src.cast(),
|
|
src_size,
|
|
dst.cast(),
|
|
dst_capacity,
|
|
NOT_STREAMING,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn max_short_offset() -> usize {
|
|
if MEM_64bits() {
|
|
usize::MAX
|
|
} else {
|
|
((1usize << 26) - 1).wrapping_sub(ZSTD_REP_NUM)
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_decompressBlock_internal(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
dst: *mut c_void,
|
|
dst_capacity: usize,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
streaming: c_int,
|
|
) -> usize {
|
|
let block_max = unsafe { block_size_max(ctx) };
|
|
if src_size > block_max {
|
|
return ERROR(ZstdErrorCode::SrcSizeWrong);
|
|
}
|
|
let mut input = src.cast::<u8>();
|
|
let lit_size =
|
|
unsafe { decode_literals_block(ctx, input, src_size, dst.cast(), dst_capacity, streaming) };
|
|
if ERR_isError(lit_size) || lit_size > src_size {
|
|
return lit_size;
|
|
}
|
|
input = unsafe { input.add(lit_size) };
|
|
let remaining = src_size - lit_size;
|
|
let history_size = unsafe { total_history_size(ctx, dst.cast(), dst_capacity) };
|
|
let potential_long_offsets = MEM_32bits() && history_size > max_short_offset();
|
|
let mut nb_seq = 0 as c_int;
|
|
let header_size =
|
|
unsafe { ZSTD_rust_decodeSeqHeaders(ctx, &mut nb_seq, input.cast(), remaining) };
|
|
if ERR_isError(header_size) || header_size > remaining {
|
|
return header_size;
|
|
}
|
|
input = unsafe { input.add(header_size) };
|
|
let sequence_size = remaining - header_size;
|
|
if (dst.is_null() || dst_capacity == 0) && nb_seq > 0 {
|
|
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
|
}
|
|
let offset_info = if potential_long_offsets
|
|
|| (unsafe { *(*ctx).ddict_is_cold } == 0
|
|
&& history_size > LONG_OFFSET_HISTORY_THRESHOLD
|
|
&& nb_seq > 8)
|
|
{
|
|
unsafe { get_offset_info(*(*ctx).oft_ptr, nb_seq) }
|
|
} else {
|
|
ZSTD_offsetInfo::default()
|
|
};
|
|
let mut long_offsets = potential_long_offsets;
|
|
if long_offsets && offset_info.max_nb_additional_bits <= stream_accumulator_min() as u8 {
|
|
long_offsets = false;
|
|
}
|
|
let decoder = choose_sequence_decoder(
|
|
unsafe { (*ctx).sequence_decoder_mode },
|
|
unsafe { *(*ctx).ddict_is_cold },
|
|
potential_long_offsets,
|
|
history_size,
|
|
nb_seq,
|
|
offset_info,
|
|
);
|
|
unsafe { *(*ctx).ddict_is_cold = 0 };
|
|
unsafe {
|
|
decompress_sequences(
|
|
ctx,
|
|
dst.cast(),
|
|
dst_capacity,
|
|
input,
|
|
sequence_size,
|
|
nb_seq,
|
|
long_offsets,
|
|
decoder == SequenceDecoder::Long,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_checkContinuity(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
dst: *const c_void,
|
|
dst_size: usize,
|
|
) {
|
|
unsafe {
|
|
if dst.cast::<u8>() != *(*ctx).previous_dst_end && dst_size != 0 {
|
|
*(*ctx).dict_end = *(*ctx).previous_dst_end;
|
|
let prefix = *(*ctx).prefix_start;
|
|
let previous = *(*ctx).previous_dst_end;
|
|
let distance = address_distance(previous, prefix);
|
|
*(*ctx).virtual_start = dst.cast::<u8>().wrapping_sub(distance);
|
|
*(*ctx).prefix_start = dst.cast();
|
|
*(*ctx).previous_dst_end = dst.cast();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_decompressBlock_deprecated(
|
|
ctx: *mut ZSTD_rustBlockCtx,
|
|
dst: *mut c_void,
|
|
dst_capacity: usize,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
) -> usize {
|
|
unsafe {
|
|
*(*ctx).is_frame_decompression = 0;
|
|
ZSTD_rust_checkContinuity(ctx, dst.cast(), dst_capacity);
|
|
let result = ZSTD_rust_decompressBlock_internal(
|
|
ctx,
|
|
dst,
|
|
dst_capacity,
|
|
src,
|
|
src_size,
|
|
NOT_STREAMING,
|
|
);
|
|
if !ERR_isError(result) {
|
|
*(*ctx).previous_dst_end = ptr_add(dst.cast(), result).cast_const();
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/* The public and hidden block-decoder entrypoints retain their C ABI, but the
|
|
* wrapper control flow lives here with the decoder implementation. C only
|
|
* projects the configuration-dependent private fields of ZSTD_DCtx. */
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_decodeLiteralsBlock_wrapper(
|
|
dctx: *mut ZSTD_DCtx,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
dst: *mut c_void,
|
|
dst_capacity: usize,
|
|
) -> usize {
|
|
let mut ctx = unsafe { block_context(dctx) };
|
|
unsafe { ZSTD_rust_decodeLiteralsBlock_wrapper(&mut ctx, src, src_size, dst, dst_capacity) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_decodeSeqHeaders(
|
|
dctx: *mut ZSTD_DCtx,
|
|
nb_seq_ptr: *mut c_int,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
) -> usize {
|
|
let mut ctx = unsafe { block_context(dctx) };
|
|
unsafe { ZSTD_rust_decodeSeqHeaders(&mut ctx, nb_seq_ptr, src, src_size) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" 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 {
|
|
let mut ctx = unsafe { block_context(dctx) };
|
|
unsafe {
|
|
ZSTD_rust_decompressBlock_internal(&mut ctx, dst, dst_capacity, src, src_size, streaming)
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_checkContinuity(
|
|
dctx: *mut ZSTD_DCtx,
|
|
dst: *const c_void,
|
|
dst_size: usize,
|
|
) {
|
|
let mut ctx = unsafe { block_context(dctx) };
|
|
unsafe { ZSTD_rust_checkContinuity(&mut ctx, dst, dst_size) };
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_decompressBlock_deprecated(
|
|
dctx: *mut ZSTD_DCtx,
|
|
dst: *mut c_void,
|
|
dst_capacity: usize,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
) -> usize {
|
|
let mut ctx = unsafe { block_context(dctx) };
|
|
unsafe { ZSTD_rust_decompressBlock_deprecated(&mut ctx, dst, dst_capacity, src, src_size) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_decompressBlock(
|
|
dctx: *mut ZSTD_DCtx,
|
|
dst: *mut c_void,
|
|
dst_capacity: usize,
|
|
src: *const c_void,
|
|
src_size: usize,
|
|
) -> usize {
|
|
unsafe { ZSTD_decompressBlock_deprecated(dctx, dst, dst_capacity, src, src_size) }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn offset_code_22_and_23_keep_their_22_and_23_bit_bases() {
|
|
assert_eq!(OF_BASE[22], 0x3F_FFFD);
|
|
assert_eq!(OF_BASE[23], 0x7F_FFFD);
|
|
assert_eq!(OF_BASE[24], 0xFF_FFFD);
|
|
|
|
let mut normalized = [0i16; MAX_OFF + 1];
|
|
normalized[22] = 1 << 5;
|
|
let mut table = [ZSTD_seqSymbol::default(); 1 + (1 << 5)];
|
|
let mut workspace = [0u32; ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32];
|
|
unsafe {
|
|
build_fse_table_body(
|
|
table.as_mut_ptr(),
|
|
normalized.as_ptr(),
|
|
22,
|
|
OF_BASE.as_ptr(),
|
|
OF_BITS.as_ptr(),
|
|
5,
|
|
workspace.as_mut_ptr(),
|
|
std::mem::size_of_val(&workspace),
|
|
);
|
|
}
|
|
assert!(table[1..]
|
|
.iter()
|
|
.all(|symbol| symbol.base_value == 0x3F_FFFD));
|
|
}
|
|
|
|
#[test]
|
|
fn offset_info_scales_long_share_and_handles_invalid_tables() {
|
|
let mut table = [ZSTD_seqSymbol::default(); 1 + (1 << 5)];
|
|
unsafe {
|
|
set_seq_header(
|
|
table.as_mut_ptr(),
|
|
ZSTD_seqSymbol_header {
|
|
fast_mode: 0,
|
|
table_log: 5,
|
|
},
|
|
);
|
|
}
|
|
table[1].nb_additional_bits = 23;
|
|
table[2].nb_additional_bits = 31;
|
|
|
|
let info = unsafe { get_offset_info(table.as_ptr(), 1) };
|
|
assert_eq!(info.max_nb_additional_bits, 31);
|
|
assert_eq!(info.long_offset_share, 2 << (OFF_FSE_LOG - 5));
|
|
assert_eq!(
|
|
unsafe { get_offset_info(table.as_ptr(), 0) },
|
|
ZSTD_offsetInfo::default()
|
|
);
|
|
|
|
unsafe {
|
|
set_seq_header(
|
|
table.as_mut_ptr(),
|
|
ZSTD_seqSymbol_header {
|
|
fast_mode: 0,
|
|
table_log: OFF_FSE_LOG + 1,
|
|
},
|
|
);
|
|
}
|
|
assert_eq!(
|
|
unsafe { get_offset_info(table.as_ptr(), 1) },
|
|
ZSTD_offsetInfo::default()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn sequence_decoder_policy_preserves_forced_and_runtime_variants() {
|
|
let minimum_share = if MEM_32bits() {
|
|
LONG_OFFSET_MIN_SHARE_32
|
|
} else {
|
|
LONG_OFFSET_MIN_SHARE_64
|
|
};
|
|
let info = ZSTD_offsetInfo {
|
|
long_offset_share: minimum_share,
|
|
max_nb_additional_bits: 31,
|
|
};
|
|
|
|
assert_eq!(
|
|
choose_sequence_decoder(SEQUENCE_DECODER_FORCE_SHORT, 1, true, usize::MAX, 9, info,),
|
|
SequenceDecoder::Short
|
|
);
|
|
assert_eq!(
|
|
choose_sequence_decoder(
|
|
SEQUENCE_DECODER_FORCE_LONG,
|
|
0,
|
|
false,
|
|
0,
|
|
0,
|
|
ZSTD_offsetInfo::default(),
|
|
),
|
|
SequenceDecoder::Long
|
|
);
|
|
assert_eq!(
|
|
choose_sequence_decoder(
|
|
SEQUENCE_DECODER_RUNTIME,
|
|
1,
|
|
false,
|
|
0,
|
|
0,
|
|
ZSTD_offsetInfo::default(),
|
|
),
|
|
SequenceDecoder::Long
|
|
);
|
|
assert_eq!(
|
|
choose_sequence_decoder(
|
|
SEQUENCE_DECODER_RUNTIME,
|
|
0,
|
|
false,
|
|
LONG_OFFSET_HISTORY_THRESHOLD + 1,
|
|
9,
|
|
info,
|
|
),
|
|
SequenceDecoder::Long
|
|
);
|
|
assert_eq!(
|
|
choose_sequence_decoder(
|
|
SEQUENCE_DECODER_RUNTIME,
|
|
0,
|
|
false,
|
|
LONG_OFFSET_HISTORY_THRESHOLD,
|
|
8,
|
|
info,
|
|
),
|
|
SequenceDecoder::Short
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn block_header_edges_keep_rle_reserved_and_truncated_contracts() {
|
|
let mut properties = block_properties_t {
|
|
block_type: -1,
|
|
last_block: 99,
|
|
orig_size: 99,
|
|
};
|
|
let truncated = [0u8; 2];
|
|
let truncated_result = unsafe {
|
|
ZSTD_getcBlockSize(
|
|
truncated.as_ptr().cast(),
|
|
truncated.len(),
|
|
(&mut properties as *mut block_properties_t).cast(),
|
|
)
|
|
};
|
|
assert_eq!(
|
|
crate::errors::ERR_getErrorCode(truncated_result),
|
|
ZstdErrorCode::SrcSizeWrong as i32
|
|
);
|
|
assert_eq!(properties.block_type, -1);
|
|
|
|
let rle_header = (0x1234u32 << 3) | (1 << 1) | 1;
|
|
let rle = [
|
|
rle_header as u8,
|
|
(rle_header >> 8) as u8,
|
|
(rle_header >> 16) as u8,
|
|
];
|
|
let rle_result = unsafe {
|
|
ZSTD_getcBlockSize(
|
|
rle.as_ptr().cast(),
|
|
rle.len(),
|
|
(&mut properties as *mut block_properties_t).cast(),
|
|
)
|
|
};
|
|
assert_eq!(rle_result, 1);
|
|
assert_eq!(properties.block_type, SET_RLE);
|
|
assert_eq!(properties.last_block, 1);
|
|
assert_eq!(properties.orig_size, 0x1234);
|
|
|
|
let reserved_header = 3u32 << 1;
|
|
let reserved = [
|
|
reserved_header as u8,
|
|
(reserved_header >> 8) as u8,
|
|
(reserved_header >> 16) as u8,
|
|
];
|
|
let reserved_result = unsafe {
|
|
ZSTD_getcBlockSize(
|
|
reserved.as_ptr().cast(),
|
|
reserved.len(),
|
|
(&mut properties as *mut block_properties_t).cast(),
|
|
)
|
|
};
|
|
assert_eq!(
|
|
crate::errors::ERR_getErrorCode(reserved_result),
|
|
ZstdErrorCode::CorruptionDetected as i32
|
|
);
|
|
assert_eq!(properties.block_type, 3);
|
|
}
|
|
}
|