feat(decompress): move legacy dispatch into Rust

Legacy detection, frame-size queries, one-shot decode, and buffered streaming
were still routed through the C header dispatcher even though every historical
v0.1 through v0.7 decoder is already ported under Rust feature gates.  The C
implementation also duplicated the support-level boundary independently from
the Rust archive selection.

Move the dispatcher and stream lifecycle to Rust using the existing DCtx field
projection.  Rust now selects the version, preserves the null-buffer and
position semantics, initializes and frees buffered legacy contexts, and calls
the frozen per-version modules.  C retains only the configuration leaf that
reports ZSTD_LEGACY_SUPPORT plus the private DCtx layout and trace helpers, so
library, program, and test builds can still choose different support levels.

Test Plan:
- `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression,decompression,dict-builder,legacy-v01,legacy-v02,legacy-v03,legacy-v04,legacy-v05,legacy-v06,legacy-v07 --all-targets -- --test-threads=1` -- 528 passed.
- Native `test-legacy`, `test-invalidDictionaries`, `test-decodecorpus`, and `test-zstd` -- passed.
- Strict C syntax checks with legacy support 0, 4, 5, and 7 -- passed.
- `cargo clippy` default, benches, and tests for library and CLI -- passed.
This commit is contained in:
2026-07-18 22:41:17 +02:00
parent 0e0d87ddf5
commit 4304c0af40
2 changed files with 600 additions and 114 deletions
+9 -95
View File
@@ -16,10 +16,6 @@
#include "zstd_decompress_internal.h"
#include "zstd_ddict.h"
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
# include "../legacy/zstd_legacy.h"
#endif
/* Keep the three public build-time tuning knobs in the C configuration domain.
* Rust queries them through the small leaves below rather than baking a second
* set of defaults into its source. */
@@ -125,18 +121,7 @@ void ZSTD_rust_dctx_copy_prefix(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx);
void ZSTD_rust_dctx_trace_begin(ZSTD_DCtx* dctx);
void ZSTD_rust_dctx_trace_end(ZSTD_DCtx* dctx, U64 uncompressedSize,
U64 compressedSize, int streaming);
unsigned ZSTD_rust_legacy_is(const void* src, size_t srcSize);
unsigned long long ZSTD_rust_legacy_get_decompressed_size(const void* src,
size_t srcSize);
size_t ZSTD_rust_legacy_find_compressed_size(const void* src, size_t srcSize);
size_t ZSTD_rust_legacy_frame_size_info(const void* src, size_t srcSize,
size_t* compressedSize,
unsigned long long* decompressedBound,
size_t* nbBlocks);
size_t ZSTD_rust_legacy_decompress(void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
const void* dict, size_t dictSize);
void ZSTD_rust_legacy_free_stream(ZSTD_DCtx* dctx);
unsigned ZSTD_rust_legacy_support(void);
void ZSTD_rust_dctx_view(ZSTD_DCtx* dctx, ZSTD_rustDctxView* out)
{
@@ -319,87 +304,16 @@ void ZSTD_rust_dctx_trace_end(ZSTD_DCtx* dctx, U64 uncompressedSize,
#endif
}
unsigned ZSTD_rust_legacy_is(const void* src, size_t srcSize)
/* The Rust dispatcher has all version-specific legacy implementations. Keep
* the active C configuration as a leaf so test builds, which compile every
* Rust legacy module but vary this C macro per target, retain the historical
* support boundary. */
unsigned ZSTD_rust_legacy_support(void)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
return ZSTD_isLegacy(src, srcSize);
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) \
&& (ZSTD_LEGACY_SUPPORT <= 7)
return ZSTD_LEGACY_SUPPORT;
#else
(void)src;
(void)srcSize;
return 0;
#endif
}
unsigned long long ZSTD_rust_legacy_get_decompressed_size(const void* src, size_t srcSize)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
return ZSTD_getDecompressedSize_legacy(src, srcSize);
#else
(void)src;
(void)srcSize;
return 0;
#endif
}
size_t ZSTD_rust_legacy_find_compressed_size(const void* src, size_t srcSize)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
return ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
#else
(void)src;
(void)srcSize;
return ERROR(prefix_unknown);
#endif
}
size_t ZSTD_rust_legacy_frame_size_info(const void* src, size_t srcSize,
size_t* compressedSize,
unsigned long long* decompressedBound,
size_t* nbBlocks)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
ZSTD_frameSizeInfo const info = ZSTD_findFrameSizeInfoLegacy(src, srcSize);
*compressedSize = info.compressedSize;
*decompressedBound = info.decompressedBound;
*nbBlocks = info.decompressedBound == ZSTD_CONTENTSIZE_ERROR
? 0 : (size_t)(info.decompressedBound / ZSTD_BLOCKSIZE_MAX);
return ZSTD_isError(info.compressedSize) ? info.compressedSize : 0;
#else
(void)src;
(void)srcSize;
*compressedSize = ERROR(prefix_unknown);
*decompressedBound = ZSTD_CONTENTSIZE_ERROR;
*nbBlocks = 0;
return ERROR(prefix_unknown);
#endif
}
size_t ZSTD_rust_legacy_decompress(void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
const void* dict, size_t dictSize)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, dict, dictSize);
#else
(void)dst;
(void)dstCapacity;
(void)src;
(void)srcSize;
(void)dict;
(void)dictSize;
return ERROR(prefix_unknown);
#endif
}
void ZSTD_rust_legacy_free_stream(ZSTD_DCtx* dctx)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
if (dctx->legacyContext) {
ZSTD_freeLegacyStreamContext(dctx->legacyContext,
dctx->previousLegacyVersion);
dctx->legacyContext = NULL;
}
#else
(void)dctx;
#endif
}
+591 -19
View File
@@ -327,25 +327,19 @@ unsafe extern "C" {
);
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_free_stream(dctx: *mut ZSTD_DCtx);
#[cfg(all(
not(test),
any(
feature = "legacy-v01",
feature = "legacy-v02",
feature = "legacy-v03",
feature = "legacy-v04",
feature = "legacy-v05",
feature = "legacy-v06",
feature = "legacy-v07"
)
))]
fn ZSTD_rust_legacy_support() -> c_uint;
fn ZSTD_decompressBlock_internal(
dctx: *mut ZSTD_DCtx,
dst: *mut c_void,
@@ -388,6 +382,9 @@ fn legacy_empty_mut_ptr() -> *mut c_void {
* C helper's historical behavior is to leave that slot untouched while an
* initialization attempt replaces it only after successful creation. */
unsafe fn free_legacy_stream_context(_legacy_context: *mut c_void, version: u32) {
if _legacy_context.is_null() {
return;
}
match version {
#[cfg(feature = "legacy-v04")]
4 => {
@@ -417,6 +414,395 @@ unsafe fn free_legacy_stream_context(_legacy_context: *mut c_void, version: u32)
}
}
/* The Rust archive used by the C test harness deliberately contains all
* ported legacy modules, even when a particular test target lowers
* ZSTD_LEGACY_SUPPORT. Query the active C setting instead of deriving the
* dispatch boundary from Cargo features alone. Standalone Rust tests have no
* C adapter to query; enabling all configured modules there gives the helper
* tests a deterministic support boundary without changing production builds. */
#[inline]
unsafe fn configured_legacy_support() -> u32 {
#[cfg(test)]
{
1
}
#[cfg(not(test))]
{
#[cfg(any(
feature = "legacy-v01",
feature = "legacy-v02",
feature = "legacy-v03",
feature = "legacy-v04",
feature = "legacy-v05",
feature = "legacy-v06",
feature = "legacy-v07"
))]
{
unsafe { ZSTD_rust_legacy_support() }
}
#[cfg(not(any(
feature = "legacy-v01",
feature = "legacy-v02",
feature = "legacy-v03",
feature = "legacy-v04",
feature = "legacy-v05",
feature = "legacy-v06",
feature = "legacy-v07"
)))]
{
0
}
}
}
/* This is the Rust equivalent of ZSTD_isLegacy(). The v0.1 magic is the one
* historical exception: its bytes are checked as the little-endian constant
* 0x1EB52FFD, while v0.2 through v0.7 use the usual 0xFD2FB52N values. */
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_is(src: *const c_void, src_size: usize) -> c_uint {
if src.is_null() || src_size < ZSTD_FRAMEIDSIZE {
return 0;
}
let support = unsafe { configured_legacy_support() };
if support == 0 {
return 0;
}
let _magic = unsafe { MEM_readLE32(src) };
#[cfg(feature = "legacy-v01")]
if support <= 1 && _magic == 0x1EB5_2FFD {
return 1;
}
#[cfg(feature = "legacy-v02")]
if support <= 2 && _magic == 0xFD2F_B522 {
return 2;
}
#[cfg(feature = "legacy-v03")]
if support <= 3 && _magic == 0xFD2F_B523 {
return 3;
}
#[cfg(feature = "legacy-v04")]
if support <= 4 && _magic == 0xFD2F_B524 {
return 4;
}
#[cfg(feature = "legacy-v05")]
if support <= 5 && _magic == 0xFD2F_B525 {
return 5;
}
#[cfg(feature = "legacy-v06")]
if support <= 6 && _magic == 0xFD2F_B526 {
return 6;
}
#[cfg(feature = "legacy-v07")]
if support <= 7 && _magic == 0xFD2F_B527 {
return 7;
}
0
}
#[inline]
unsafe fn legacy_frame_size_info_values(
src: *const c_void,
src_size: usize,
) -> (usize, u64, usize) {
let version = unsafe { ZSTD_rust_legacy_is(src, src_size) };
let mut compressed_size = ERROR(ZstdErrorCode::PrefixUnknown);
let mut decompressed_bound = ZSTD_CONTENTSIZE_ERROR;
match version {
#[cfg(feature = "legacy-v01")]
1 => unsafe {
crate::legacy::zstd_v01::ZSTDv01_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v02")]
2 => unsafe {
crate::legacy::zstd_v02::ZSTDv02_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v03")]
3 => unsafe {
crate::legacy::zstd_v03::ZSTDv03_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v04")]
4 => unsafe {
crate::legacy::zstd_v04::ZSTDv04_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v05")]
5 => unsafe {
crate::legacy::zstd_v05::ZSTDv05_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v06")]
6 => unsafe {
crate::legacy::zstd_v06::ZSTDv06_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
#[cfg(feature = "legacy-v07")]
7 => unsafe {
crate::legacy::zstd_v07::ZSTDv07_findFrameSizeInfoLegacy(
src,
src_size,
&mut compressed_size,
&mut decompressed_bound,
);
},
_ => {}
}
if !ERR_isError(compressed_size) && compressed_size > src_size {
compressed_size = ERROR(ZstdErrorCode::SrcSizeWrong);
decompressed_bound = ZSTD_CONTENTSIZE_ERROR;
}
let nb_blocks = if decompressed_bound == ZSTD_CONTENTSIZE_ERROR {
0
} else {
debug_assert_eq!(decompressed_bound & (ZSTD_BLOCKSIZE_MAX as u64 - 1), 0);
(decompressed_bound / ZSTD_BLOCKSIZE_MAX as u64) as usize
};
(compressed_size, decompressed_bound, nb_blocks)
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_get_decompressed_size(
src: *const c_void,
src_size: usize,
) -> u64 {
let version = unsafe { ZSTD_rust_legacy_is(src, src_size) };
match version {
#[cfg(feature = "legacy-v05")]
5 => {
/* ZstdParameters is repr(C), and src_size is its first field. The
* field is private inside the frozen module because it is not part
* of that module's public Rust API, so read the documented C ABI
* prefix only after the version-specific parser succeeds. */
let mut params = MaybeUninit::<crate::legacy::zstd_v05::ZstdParameters>::zeroed();
let result = unsafe {
crate::legacy::zstd_v05::ZSTDv05_getFrameParams(params.as_mut_ptr(), src, src_size)
};
if result == 0 {
unsafe { params.as_ptr().cast::<u64>().read() }
} else {
0
}
}
#[cfg(feature = "legacy-v06")]
6 => {
let mut params = crate::legacy::zstd_v06::ZSTDv06_frameParams {
frame_content_size: 0,
window_log: 0,
};
let result = unsafe {
crate::legacy::zstd_v06::ZSTDv06_getFrameParams(&mut params, src, src_size)
};
if result == 0 {
params.frame_content_size
} else {
0
}
}
#[cfg(feature = "legacy-v07")]
7 => {
let mut params = crate::legacy::zstd_v07::ZSTDv07_frameParams {
frameContentSize: 0,
windowSize: 0,
dictID: 0,
checksumFlag: 0,
};
let result = unsafe {
crate::legacy::zstd_v07::ZSTDv07_getFrameParams(&mut params, src, src_size)
};
if result == 0 {
params.frameContentSize
} else {
0
}
}
_ => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_find_compressed_size(
src: *const c_void,
src_size: usize,
) -> usize {
unsafe { legacy_frame_size_info_values(src, src_size).0 }
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_frame_size_info(
src: *const c_void,
src_size: usize,
compressed_size: *mut usize,
decompressed_bound: *mut u64,
nb_blocks: *mut usize,
) -> usize {
let (c_size, d_bound, blocks) = unsafe { legacy_frame_size_info_values(src, src_size) };
unsafe {
compressed_size.write(c_size);
decompressed_bound.write(d_bound);
nb_blocks.write(blocks);
}
if ERR_isError(c_size) {
c_size
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_decompress(
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
dict: *const c_void,
dict_size: usize,
) -> usize {
let version = unsafe { ZSTD_rust_legacy_is(src, src_size) };
let _dst = if dst.is_null() {
debug_assert_eq!(dst_capacity, 0);
legacy_empty_mut_ptr()
} else {
dst
};
let _src = if src.is_null() {
debug_assert_eq!(src_size, 0);
legacy_empty_const_ptr()
} else {
src
};
let _dict = if dict.is_null() {
debug_assert_eq!(dict_size, 0);
legacy_empty_const_ptr()
} else {
dict
};
match version {
#[cfg(feature = "legacy-v01")]
1 => unsafe {
crate::legacy::zstd_v01::ZSTDv01_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v02")]
2 => unsafe {
crate::legacy::zstd_v02::ZSTDv02_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v03")]
3 => unsafe {
crate::legacy::zstd_v03::ZSTDv03_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v04")]
4 => unsafe {
crate::legacy::zstd_v04::ZSTDv04_decompress(_dst, dst_capacity, _src, src_size)
},
#[cfg(feature = "legacy-v05")]
5 => {
let dctx = unsafe { crate::legacy::zstd_v05::ZSTDv05_createDCtx() };
if dctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe {
crate::legacy::zstd_v05::ZSTDv05_decompress_usingDict(
dctx,
_dst,
dst_capacity,
_src,
src_size,
_dict,
dict_size,
)
};
unsafe { crate::legacy::zstd_v05::ZSTDv05_freeDCtx(dctx) };
result
}
#[cfg(feature = "legacy-v06")]
6 => {
let dctx = unsafe { crate::legacy::zstd_v06::ZSTDv06_createDCtx() };
if dctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe {
crate::legacy::zstd_v06::ZSTDv06_decompress_usingDict(
dctx,
_dst,
dst_capacity,
_src,
src_size,
_dict,
dict_size,
)
};
unsafe { crate::legacy::zstd_v06::ZSTDv06_freeDCtx(dctx) };
result
}
#[cfg(feature = "legacy-v07")]
7 => {
let dctx = unsafe { crate::legacy::zstd_v07::ZSTDv07_createDCtx() };
if dctx.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
let result = unsafe {
crate::legacy::zstd_v07::ZSTDv07_decompress_usingDict(
dctx,
_dst,
dst_capacity,
_src,
src_size,
_dict,
dict_size,
)
};
unsafe { crate::legacy::zstd_v07::ZSTDv07_freeDCtx(dctx) };
result
}
_ => ERROR(ZstdErrorCode::PrefixUnknown),
}
}
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_legacy_free_stream(dctx: *mut ZSTD_DCtx) {
if dctx.is_null() {
return;
}
let view = unsafe { dctx_view(dctx) };
if view.legacy_context.is_null() {
return;
}
let legacy_context = unsafe { field::<*mut c_void>(view.legacy_context) };
let previous_version = unsafe { field::<u32>(view.previous_legacy_version) };
unsafe { free_legacy_stream_context(legacy_context, previous_version) };
unsafe { set_field(view.legacy_context, ptr::null_mut::<c_void>()) };
}
/* This is the narrow Rust equivalent of ZSTD_initLegacyStream(). It only
* touches the three legacy projections in ZSTD_rustDctxView; the enclosing
* decoder context and every version-specific stream context remain opaque to
@@ -795,6 +1181,192 @@ mod legacy_stream_dispatch_tests {
}
}
#[cfg(test)]
mod legacy_dispatch_helper_tests {
use super::*;
#[test]
fn legacy_detection_rejects_short_null_and_unknown_input() {
assert_eq!(unsafe { ZSTD_rust_legacy_is(ptr::null(), 0) }, 0);
let short = [0x27, 0xB5, 0x2F];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(short.as_ptr().cast(), short.len()) },
0
);
let unknown = [0u8; ZSTD_FRAMEIDSIZE];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(unknown.as_ptr().cast(), unknown.len()) },
0
);
}
#[cfg(feature = "legacy-v01")]
#[test]
fn detects_v01_magic() {
let magic: [u8; 4] = [0xFD, 0x2F, 0xB5, 0x1E];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
1
);
}
#[cfg(feature = "legacy-v02")]
#[test]
fn detects_v02_magic() {
let magic: [u8; 4] = [0x22, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
2
);
}
#[cfg(feature = "legacy-v03")]
#[test]
fn detects_v03_magic() {
let magic: [u8; 4] = [0x23, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
3
);
}
#[cfg(feature = "legacy-v04")]
#[test]
fn detects_v04_magic() {
let magic: [u8; 4] = [0x24, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
4
);
}
#[cfg(feature = "legacy-v05")]
#[test]
fn detects_v05_magic() {
let magic: [u8; 4] = [0x25, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
5
);
}
#[cfg(feature = "legacy-v06")]
#[test]
fn detects_v06_magic() {
let magic: [u8; 4] = [0x26, 0xB5, 0x2F, 0xFD];
assert_eq!(
unsafe { ZSTD_rust_legacy_is(magic.as_ptr().cast(), magic.len()) },
6
);
}
#[cfg(feature = "legacy-v07")]
#[test]
fn legacy_v07_size_and_one_shot_helpers_match_frame() {
let payload = b"legacy-dispatch";
let mut frame = Vec::with_capacity(6 + 3 + payload.len() + 3);
frame.extend_from_slice(&[0x27, 0xB5, 0x2F, 0xFD]);
frame.extend_from_slice(&[0x20, payload.len() as u8]);
frame.extend_from_slice(&[
0x40 | (((payload.len() >> 16) & 7) as u8),
(payload.len() >> 8) as u8,
payload.len() as u8,
]);
frame.extend_from_slice(payload);
frame.extend_from_slice(&[0xC0, 0, 0]);
let mut compressed_size = 0;
let mut decompressed_bound = 0;
let mut nb_blocks = 0;
let status = unsafe {
ZSTD_rust_legacy_frame_size_info(
frame.as_ptr().cast(),
frame.len(),
&mut compressed_size,
&mut decompressed_bound,
&mut nb_blocks,
)
};
assert_eq!(status, 0);
assert_eq!(compressed_size, frame.len());
assert_eq!(
decompressed_bound, ZSTD_BLOCKSIZE_MAX as u64,
"legacy frame bounds are reported in whole legacy blocks"
);
assert_eq!(nb_blocks, 1);
assert_eq!(
unsafe { ZSTD_rust_legacy_find_compressed_size(frame.as_ptr().cast(), frame.len()) },
frame.len()
);
assert_eq!(
unsafe { ZSTD_rust_legacy_get_decompressed_size(frame.as_ptr().cast(), frame.len()) },
payload.len() as u64
);
let mut output = vec![0u8; payload.len()];
let decoded = unsafe {
ZSTD_rust_legacy_decompress(
output.as_mut_ptr().cast(),
output.len(),
frame.as_ptr().cast(),
frame.len(),
ptr::null(),
0,
)
};
assert_eq!(decoded, payload.len());
assert_eq!(output, payload);
}
#[cfg(feature = "legacy-v07")]
#[test]
fn legacy_size_and_decompress_helpers_report_truncation() {
let mut truncated = vec![0x27, 0xB5, 0x2F, 0xFD, 0x20, 4, 0x40, 0, 4, b't'];
let mut compressed_size = 0;
let mut decompressed_bound = 0;
let mut nb_blocks = 0;
let status = unsafe {
ZSTD_rust_legacy_frame_size_info(
truncated.as_ptr().cast(),
truncated.len(),
&mut compressed_size,
&mut decompressed_bound,
&mut nb_blocks,
)
};
assert_eq!(status, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!(compressed_size, status);
assert_eq!(decompressed_bound, ZSTD_CONTENTSIZE_ERROR);
assert_eq!(nb_blocks, 0);
assert_eq!(
unsafe {
ZSTD_rust_legacy_find_compressed_size(truncated.as_ptr().cast(), truncated.len())
},
status
);
let mut output = [0u8; 4];
let decoded = unsafe {
ZSTD_rust_legacy_decompress(
output.as_mut_ptr().cast(),
output.len(),
truncated.as_ptr().cast(),
truncated.len(),
ptr::null(),
0,
)
};
assert!(ERR_isError(decoded));
truncated[0] ^= 1;
assert_eq!(
unsafe {
ZSTD_rust_legacy_get_decompressed_size(truncated.as_ptr().cast(), truncated.len())
},
0
);
}
}
#[inline]
fn frame_header_prefix(format: c_int) -> usize {
if format == ZSTD_F_ZSTD1 {