feat(decompress): move legacy stream dispatch to Rust
Move the legacy buffered-stream dispatcher out of the C decompression adapter while keeping ZSTD_DCtx_s private and C-owned. Rust now consumes the existing ZSTD_rustDctxView projection, detects the legacy version at input.pos, preserves static-context rejection and stream-stage reset behavior, and handles legacy context replacement through the version-gated v0.4-v0.7 Rust stream APIs. The Rust boundary retains the C helper's NULL normalization, dictionary lifetime sentinel, previous-version cleanup semantics, input/output position updates, and version-unsupported and no-legacy-support fallbacks. The C legacy free helper remains unchanged; only the dispatcher declaration and body were removed from the adapter. Focused tests cover unsupported versions, stage reset, context reuse/switching, and partial v0.4 input/output progress. Test Plan: - `cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression` -- passed - Same clippy command with `--benches` and `--tests` -- passed - `cargo +nightly fmt --manifest-path rust/Cargo.toml`, followed by all three clippy commands -- passed - Decompression/legacy clippy with `-D warnings` -- passed - All 107 Rust tests with v0.4-v0.7 features -- passed - Focused legacy dispatcher tests: 4 passed - `make -C lib -j2 lib-mt` and `make -C lib -j2 lib-nomt` -- passed - `make -C tests -j2 test-legacy` -- built, then failed in the existing simple one-shot path with one v0.8 output byte mismatch (`e0` vs expected `e2`) before the streaming check; the new dispatcher symbol was not called on that failing path
This commit is contained in:
@@ -143,10 +143,6 @@ size_t ZSTD_rust_legacy_frame_size_info(const void* src, size_t srcSize,
|
|||||||
size_t ZSTD_rust_legacy_decompress(void* dst, size_t dstCapacity,
|
size_t ZSTD_rust_legacy_decompress(void* dst, size_t dstCapacity,
|
||||||
const void* src, size_t srcSize,
|
const void* src, size_t srcSize,
|
||||||
const void* dict, size_t dictSize);
|
const void* dict, size_t dictSize);
|
||||||
size_t ZSTD_rust_legacy_decompress_stream(ZSTD_DCtx* dctx,
|
|
||||||
ZSTD_outBuffer* output,
|
|
||||||
ZSTD_inBuffer* input,
|
|
||||||
const void* dict, size_t dictSize);
|
|
||||||
void ZSTD_rust_legacy_free_stream(ZSTD_DCtx* dctx);
|
void ZSTD_rust_legacy_free_stream(ZSTD_DCtx* dctx);
|
||||||
|
|
||||||
void ZSTD_rust_dctx_view(ZSTD_DCtx* dctx, ZSTD_rustDctxView* out)
|
void ZSTD_rust_dctx_view(ZSTD_DCtx* dctx, ZSTD_rustDctxView* out)
|
||||||
@@ -428,46 +424,6 @@ size_t ZSTD_rust_legacy_decompress(void* dst, size_t dstCapacity,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ZSTD_rust_legacy_decompress_stream(ZSTD_DCtx* dctx,
|
|
||||||
ZSTD_outBuffer* output,
|
|
||||||
ZSTD_inBuffer* input,
|
|
||||||
const void* dict, size_t dictSize)
|
|
||||||
{
|
|
||||||
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
|
||||||
size_t hint;
|
|
||||||
if (dctx->legacyVersion) {
|
|
||||||
hint = ZSTD_decompressLegacyStream(dctx->legacyContext,
|
|
||||||
dctx->legacyVersion, output, input);
|
|
||||||
if (hint == 0) dctx->streamStage = zdss_init;
|
|
||||||
return hint;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const char* const istart = input->pos != 0
|
|
||||||
? (const char*)input->src + input->pos
|
|
||||||
: (const char*)input->src;
|
|
||||||
size_t const inputSize = input->size - input->pos;
|
|
||||||
U32 const legacyVersion = ZSTD_isLegacy(istart, inputSize);
|
|
||||||
if (!legacyVersion) return ERROR(prefix_unknown);
|
|
||||||
if (dctx->staticSize) return ERROR(memory_allocation);
|
|
||||||
FORWARD_IF_ERROR(ZSTD_initLegacyStream(&dctx->legacyContext,
|
|
||||||
dctx->previousLegacyVersion,
|
|
||||||
legacyVersion, dict, dictSize), "");
|
|
||||||
dctx->legacyVersion = dctx->previousLegacyVersion = legacyVersion;
|
|
||||||
hint = ZSTD_decompressLegacyStream(dctx->legacyContext, legacyVersion,
|
|
||||||
output, input);
|
|
||||||
if (hint == 0) dctx->streamStage = zdss_init;
|
|
||||||
return hint;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
(void)dctx;
|
|
||||||
(void)output;
|
|
||||||
(void)input;
|
|
||||||
(void)dict;
|
|
||||||
(void)dictSize;
|
|
||||||
return ERROR(prefix_unknown);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ZSTD_rust_legacy_free_stream(ZSTD_DCtx* dctx)
|
void ZSTD_rust_legacy_free_stream(ZSTD_DCtx* dctx)
|
||||||
{
|
{
|
||||||
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
||||||
|
|||||||
+428
-7
@@ -76,6 +76,12 @@ const ZSTD_USE_INDEFINITELY: c_int = -1;
|
|||||||
const ZSTD_DONT_USE: c_int = 0;
|
const ZSTD_DONT_USE: c_int = 0;
|
||||||
const ZSTD_USE_ONCE: c_int = 1;
|
const ZSTD_USE_ONCE: c_int = 1;
|
||||||
|
|
||||||
|
/* The C legacy dispatcher uses a static byte when a zero-sized input,
|
||||||
|
* output, or dictionary buffer is represented by NULL. Keep the same
|
||||||
|
* lifetime and mutability without making the private ZSTD_DCtx layout part
|
||||||
|
* of the Rust ABI. */
|
||||||
|
static mut LEGACY_EMPTY_BYTE: u8 = 0;
|
||||||
|
|
||||||
const ZSTDDS_GET_FRAME_HEADER_SIZE: c_int = 0;
|
const ZSTDDS_GET_FRAME_HEADER_SIZE: c_int = 0;
|
||||||
const ZSTDDS_DECODE_FRAME_HEADER: c_int = 1;
|
const ZSTDDS_DECODE_FRAME_HEADER: c_int = 1;
|
||||||
const ZSTDDS_DECODE_BLOCK_HEADER: c_int = 2;
|
const ZSTDDS_DECODE_BLOCK_HEADER: c_int = 2;
|
||||||
@@ -343,13 +349,6 @@ unsafe extern "C" {
|
|||||||
dict: *const c_void,
|
dict: *const c_void,
|
||||||
dict_size: usize,
|
dict_size: usize,
|
||||||
) -> 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_rust_legacy_free_stream(dctx: *mut ZSTD_DCtx);
|
||||||
fn ZSTD_decompressBlock_internal(
|
fn ZSTD_decompressBlock_internal(
|
||||||
dctx: *mut ZSTD_DCtx,
|
dctx: *mut ZSTD_DCtx,
|
||||||
@@ -378,6 +377,428 @@ unsafe fn dctx_view(dctx: *mut ZSTD_DCtx) -> ZSTD_rustDctxView {
|
|||||||
unsafe { view.assume_init() }
|
unsafe { view.assume_init() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn legacy_empty_const_ptr() -> *const c_void {
|
||||||
|
ptr::addr_of!(LEGACY_EMPTY_BYTE).cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn legacy_empty_mut_ptr() -> *mut c_void {
|
||||||
|
ptr::addr_of_mut!(LEGACY_EMPTY_BYTE).cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This mirrors ZSTD_freeLegacyStreamContext(), including its deliberately
|
||||||
|
* ignored error result. The caller owns the projected pointer slot and the
|
||||||
|
* C helper's historical behavior is to leave that slot untouched while an
|
||||||
|
* initialization attempt replaces it only after successful creation. */
|
||||||
|
unsafe fn free_legacy_stream_context(legacy_context: *mut c_void, version: u32) {
|
||||||
|
match version {
|
||||||
|
#[cfg(feature = "legacy-v04")]
|
||||||
|
4 => {
|
||||||
|
let _ = crate::legacy::zstd_v04::ZBUFFv04_freeDCtx(
|
||||||
|
legacy_context.cast::<crate::legacy::zstd_v04::ZBUFFv04_DCtx>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v05")]
|
||||||
|
5 => {
|
||||||
|
let _ = crate::legacy::zstd_v05::ZBUFFv05_freeDCtx(
|
||||||
|
legacy_context.cast::<crate::legacy::zstd_v05::ZBUFFv05_DCtx>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v06")]
|
||||||
|
6 => {
|
||||||
|
let _ = crate::legacy::zstd_v06::ZBUFFv06_freeDCtx(
|
||||||
|
legacy_context.cast::<crate::legacy::zstd_v06::ZBUFFv06_DCtx>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v07")]
|
||||||
|
7 => {
|
||||||
|
let _ = crate::legacy::zstd_v07::ZBUFFv07_freeDCtx(
|
||||||
|
legacy_context.cast::<crate::legacy::zstd_v07::ZBUFFv07_DCtx>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the narrow Rust equivalent of ZSTD_initLegacyStream(). It only
|
||||||
|
* touches the three legacy projections in ZSTD_rustDctxView; the enclosing
|
||||||
|
* decoder context and every version-specific stream context remain opaque to
|
||||||
|
* Rust callers outside this function. */
|
||||||
|
unsafe fn init_legacy_stream(
|
||||||
|
legacy_context_slot: *mut c_void,
|
||||||
|
previous_version: u32,
|
||||||
|
new_version: u32,
|
||||||
|
dict: *const c_void,
|
||||||
|
dict_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let dict = if dict.is_null() {
|
||||||
|
debug_assert_eq!(dict_size, 0);
|
||||||
|
legacy_empty_const_ptr()
|
||||||
|
} else {
|
||||||
|
dict
|
||||||
|
};
|
||||||
|
|
||||||
|
if previous_version != new_version {
|
||||||
|
let legacy_context = field::<*mut c_void>(legacy_context_slot);
|
||||||
|
free_legacy_stream_context(legacy_context, previous_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
match new_version {
|
||||||
|
/* The old formats can be recognized by ZSTD_isLegacy(), but their
|
||||||
|
* buffered streaming API was never supported by this dispatcher. */
|
||||||
|
1..=3 => 0,
|
||||||
|
#[cfg(feature = "legacy-v04")]
|
||||||
|
4 => {
|
||||||
|
let context = if previous_version == new_version {
|
||||||
|
field::<*mut c_void>(legacy_context_slot)
|
||||||
|
.cast::<crate::legacy::zstd_v04::ZBUFFv04_DCtx>()
|
||||||
|
} else {
|
||||||
|
crate::legacy::zstd_v04::ZBUFFv04_createDCtx()
|
||||||
|
};
|
||||||
|
if context.is_null() {
|
||||||
|
return ERROR(ZstdErrorCode::MemoryAllocation);
|
||||||
|
}
|
||||||
|
let _ = crate::legacy::zstd_v04::ZBUFFv04_decompressInit(context);
|
||||||
|
let _ = crate::legacy::zstd_v04::ZBUFFv04_decompressWithDictionary(
|
||||||
|
context, dict, dict_size,
|
||||||
|
);
|
||||||
|
set_field(legacy_context_slot, context.cast::<c_void>());
|
||||||
|
0
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v05")]
|
||||||
|
5 => {
|
||||||
|
let context = if previous_version == new_version {
|
||||||
|
field::<*mut c_void>(legacy_context_slot)
|
||||||
|
.cast::<crate::legacy::zstd_v05::ZBUFFv05_DCtx>()
|
||||||
|
} else {
|
||||||
|
crate::legacy::zstd_v05::ZBUFFv05_createDCtx()
|
||||||
|
};
|
||||||
|
if context.is_null() {
|
||||||
|
return ERROR(ZstdErrorCode::MemoryAllocation);
|
||||||
|
}
|
||||||
|
let _ = crate::legacy::zstd_v05::ZBUFFv05_decompressInitDictionary(
|
||||||
|
context, dict, dict_size,
|
||||||
|
);
|
||||||
|
set_field(legacy_context_slot, context.cast::<c_void>());
|
||||||
|
0
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v06")]
|
||||||
|
6 => {
|
||||||
|
let context = if previous_version == new_version {
|
||||||
|
field::<*mut c_void>(legacy_context_slot)
|
||||||
|
.cast::<crate::legacy::zstd_v06::ZBUFFv06_DCtx>()
|
||||||
|
} else {
|
||||||
|
crate::legacy::zstd_v06::ZBUFFv06_createDCtx()
|
||||||
|
};
|
||||||
|
if context.is_null() {
|
||||||
|
return ERROR(ZstdErrorCode::MemoryAllocation);
|
||||||
|
}
|
||||||
|
let _ = crate::legacy::zstd_v06::ZBUFFv06_decompressInitDictionary(
|
||||||
|
context, dict, dict_size,
|
||||||
|
);
|
||||||
|
set_field(legacy_context_slot, context.cast::<c_void>());
|
||||||
|
0
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v07")]
|
||||||
|
7 => {
|
||||||
|
let context = if previous_version == new_version {
|
||||||
|
field::<*mut c_void>(legacy_context_slot)
|
||||||
|
.cast::<crate::legacy::zstd_v07::ZBUFFv07_DCtx>()
|
||||||
|
} else {
|
||||||
|
crate::legacy::zstd_v07::ZBUFFv07_createDCtx()
|
||||||
|
};
|
||||||
|
if context.is_null() {
|
||||||
|
return ERROR(ZstdErrorCode::MemoryAllocation);
|
||||||
|
}
|
||||||
|
let _ = crate::legacy::zstd_v07::ZBUFFv07_decompressInitDictionary(
|
||||||
|
context, dict, dict_size,
|
||||||
|
);
|
||||||
|
set_field(legacy_context_slot, context.cast::<c_void>());
|
||||||
|
0
|
||||||
|
}
|
||||||
|
/* Keep the C helper's default behavior: initialization itself is
|
||||||
|
* successful, and the subsequent stream dispatch reports the
|
||||||
|
* unsupported version. */
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This mirrors ZSTD_decompressLegacyStream(). In particular, legacy
|
||||||
|
* buffered decoders receive only the remaining input/output slices and then
|
||||||
|
* return the amount they consumed/produced through the public buffer
|
||||||
|
* positions. */
|
||||||
|
unsafe fn decompress_legacy_stream(
|
||||||
|
legacy_context: *mut c_void,
|
||||||
|
version: u32,
|
||||||
|
output: *mut ZSTD_outBuffer,
|
||||||
|
input: *mut ZSTD_inBuffer,
|
||||||
|
) -> usize {
|
||||||
|
if (*output).dst.is_null() {
|
||||||
|
debug_assert_eq!((*output).size, 0);
|
||||||
|
(*output).dst = legacy_empty_mut_ptr();
|
||||||
|
}
|
||||||
|
if (*input).src.is_null() {
|
||||||
|
debug_assert_eq!((*input).size, 0);
|
||||||
|
(*input).src = legacy_empty_const_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
match version {
|
||||||
|
#[cfg(feature = "legacy-v04")]
|
||||||
|
4 => {
|
||||||
|
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
|
||||||
|
let mut read_size = (*input).size.wrapping_sub((*input).pos);
|
||||||
|
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
|
||||||
|
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
|
||||||
|
let hint = crate::legacy::zstd_v04::ZBUFFv04_decompressContinue(
|
||||||
|
legacy_context.cast(),
|
||||||
|
dst.cast(),
|
||||||
|
&mut decoded_size,
|
||||||
|
src.cast(),
|
||||||
|
&mut read_size,
|
||||||
|
);
|
||||||
|
(*output).pos = (*output).pos.wrapping_add(decoded_size);
|
||||||
|
(*input).pos = (*input).pos.wrapping_add(read_size);
|
||||||
|
hint
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v05")]
|
||||||
|
5 => {
|
||||||
|
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
|
||||||
|
let mut read_size = (*input).size.wrapping_sub((*input).pos);
|
||||||
|
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
|
||||||
|
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
|
||||||
|
let hint = crate::legacy::zstd_v05::ZBUFFv05_decompressContinue(
|
||||||
|
legacy_context.cast(),
|
||||||
|
dst.cast(),
|
||||||
|
&mut decoded_size,
|
||||||
|
src.cast(),
|
||||||
|
&mut read_size,
|
||||||
|
);
|
||||||
|
(*output).pos = (*output).pos.wrapping_add(decoded_size);
|
||||||
|
(*input).pos = (*input).pos.wrapping_add(read_size);
|
||||||
|
hint
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v06")]
|
||||||
|
6 => {
|
||||||
|
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
|
||||||
|
let mut read_size = (*input).size.wrapping_sub((*input).pos);
|
||||||
|
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
|
||||||
|
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
|
||||||
|
let hint = crate::legacy::zstd_v06::ZBUFFv06_decompressContinue(
|
||||||
|
legacy_context.cast(),
|
||||||
|
dst.cast(),
|
||||||
|
&mut decoded_size,
|
||||||
|
src.cast(),
|
||||||
|
&mut read_size,
|
||||||
|
);
|
||||||
|
(*output).pos = (*output).pos.wrapping_add(decoded_size);
|
||||||
|
(*input).pos = (*input).pos.wrapping_add(read_size);
|
||||||
|
hint
|
||||||
|
}
|
||||||
|
#[cfg(feature = "legacy-v07")]
|
||||||
|
7 => {
|
||||||
|
let src = (*input).src.cast::<u8>().wrapping_add((*input).pos);
|
||||||
|
let mut read_size = (*input).size.wrapping_sub((*input).pos);
|
||||||
|
let dst = (*output).dst.cast::<u8>().wrapping_add((*output).pos);
|
||||||
|
let mut decoded_size = (*output).size.wrapping_sub((*output).pos);
|
||||||
|
let hint = crate::legacy::zstd_v07::ZBUFFv07_decompressContinue(
|
||||||
|
legacy_context.cast(),
|
||||||
|
dst.cast(),
|
||||||
|
&mut decoded_size,
|
||||||
|
src.cast(),
|
||||||
|
&mut read_size,
|
||||||
|
);
|
||||||
|
(*output).pos = (*output).pos.wrapping_add(decoded_size);
|
||||||
|
(*input).pos = (*input).pos.wrapping_add(read_size);
|
||||||
|
hint
|
||||||
|
}
|
||||||
|
_ => ERROR(ZstdErrorCode::VersionUnsupported),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn reset_legacy_stage_if_done(stream_stage: *mut c_void, hint: usize) -> usize {
|
||||||
|
if hint == 0 {
|
||||||
|
set_field(stream_stage, ZDSS_INIT);
|
||||||
|
}
|
||||||
|
hint
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_legacy_decompress_stream(
|
||||||
|
dctx: *mut ZSTD_DCtx,
|
||||||
|
output: *mut ZSTD_outBuffer,
|
||||||
|
input: *mut ZSTD_inBuffer,
|
||||||
|
dict: *const c_void,
|
||||||
|
dict_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let view = dctx_view(dctx);
|
||||||
|
/* With ZSTD_LEGACY_SUPPORT disabled, the C projection deliberately leaves
|
||||||
|
* these slots null. This is the exact no-legacy-support fallback. */
|
||||||
|
if view.legacy_version.is_null() {
|
||||||
|
return ERROR(ZstdErrorCode::PrefixUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_version = field::<u32>(view.legacy_version);
|
||||||
|
if current_version != 0 {
|
||||||
|
let hint = decompress_legacy_stream(
|
||||||
|
field::<*mut c_void>(view.legacy_context),
|
||||||
|
current_version,
|
||||||
|
output,
|
||||||
|
input,
|
||||||
|
);
|
||||||
|
return reset_legacy_stage_if_done(view.stream_stage, hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_ref = &*input;
|
||||||
|
let istart = if input_ref.pos != 0 {
|
||||||
|
input_ref
|
||||||
|
.src
|
||||||
|
.cast::<u8>()
|
||||||
|
.wrapping_add(input_ref.pos)
|
||||||
|
.cast()
|
||||||
|
} else {
|
||||||
|
input_ref.src
|
||||||
|
};
|
||||||
|
let input_size = input_ref.size.wrapping_sub(input_ref.pos);
|
||||||
|
let legacy_version = ZSTD_rust_legacy_is(istart, input_size);
|
||||||
|
if legacy_version == 0 {
|
||||||
|
return ERROR(ZstdErrorCode::PrefixUnknown);
|
||||||
|
}
|
||||||
|
if field::<usize>(view.static_size) != 0 {
|
||||||
|
return ERROR(ZstdErrorCode::MemoryAllocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
let previous_version = field::<u32>(view.previous_legacy_version);
|
||||||
|
let init = init_legacy_stream(
|
||||||
|
view.legacy_context,
|
||||||
|
previous_version,
|
||||||
|
legacy_version,
|
||||||
|
dict,
|
||||||
|
dict_size,
|
||||||
|
);
|
||||||
|
if ERR_isError(init) {
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
set_field(view.legacy_version, legacy_version);
|
||||||
|
set_field(view.previous_legacy_version, legacy_version);
|
||||||
|
|
||||||
|
let hint = decompress_legacy_stream(
|
||||||
|
field::<*mut c_void>(view.legacy_context),
|
||||||
|
legacy_version,
|
||||||
|
output,
|
||||||
|
input,
|
||||||
|
);
|
||||||
|
reset_legacy_stage_if_done(view.stream_stage, hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod legacy_stream_dispatch_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_unsupported_versions_after_normalizing_empty_buffers() {
|
||||||
|
let mut output = ZSTD_outBuffer {
|
||||||
|
dst: ptr::null_mut(),
|
||||||
|
size: 0,
|
||||||
|
pos: 0,
|
||||||
|
};
|
||||||
|
let mut input = ZSTD_inBuffer {
|
||||||
|
src: ptr::null(),
|
||||||
|
size: 0,
|
||||||
|
pos: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result =
|
||||||
|
unsafe { decompress_legacy_stream(ptr::null_mut(), 3, &mut output, &mut input) };
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::VersionUnsupported));
|
||||||
|
assert!(!output.dst.is_null());
|
||||||
|
assert!(!input.src.is_null());
|
||||||
|
assert_eq!(output.pos, 0);
|
||||||
|
assert_eq!(input.pos, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resets_stage_only_for_a_completed_legacy_call() {
|
||||||
|
let mut stage = ZDSS_READ;
|
||||||
|
let stage_slot = (&mut stage as *mut c_int).cast::<c_void>();
|
||||||
|
|
||||||
|
assert_eq!(unsafe { reset_legacy_stage_if_done(stage_slot, 0) }, 0);
|
||||||
|
assert_eq!(stage, ZDSS_INIT);
|
||||||
|
|
||||||
|
stage = ZDSS_READ;
|
||||||
|
assert_eq!(unsafe { reset_legacy_stage_if_done(stage_slot, 7) }, 7);
|
||||||
|
assert_eq!(stage, ZDSS_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "legacy-v04", feature = "legacy-v05"))]
|
||||||
|
#[test]
|
||||||
|
fn switches_and_reuses_versioned_stream_contexts() {
|
||||||
|
let mut context = ptr::null_mut::<c_void>();
|
||||||
|
let context_slot = (&mut context as *mut *mut c_void).cast::<c_void>();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(init_legacy_stream(context_slot, 0, 4, ptr::null(), 0), 0);
|
||||||
|
let v04_context = context;
|
||||||
|
assert!(!v04_context.is_null());
|
||||||
|
|
||||||
|
assert_eq!(init_legacy_stream(context_slot, 4, 4, ptr::null(), 0), 0);
|
||||||
|
assert_eq!(context, v04_context);
|
||||||
|
|
||||||
|
assert_eq!(init_legacy_stream(context_slot, 4, 5, ptr::null(), 0), 0);
|
||||||
|
assert!(!context.is_null());
|
||||||
|
free_legacy_stream_context(context, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "legacy-v04")]
|
||||||
|
#[test]
|
||||||
|
fn tracks_partial_input_and_output_positions() {
|
||||||
|
const FRAME: &[u8] = &[
|
||||||
|
0x24, 0xB5, 0x2F, 0xFD, 0x00, 0x40, 0x00, 0x0B, b'r', b'a', b'w', b' ', b'v', b'0',
|
||||||
|
b'.', b'4', b'!', b'!', b'!', 0xC0, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
let expected = b"raw v0.4!!!";
|
||||||
|
let mut context = ptr::null_mut::<c_void>();
|
||||||
|
let context_slot = (&mut context as *mut *mut c_void).cast::<c_void>();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(init_legacy_stream(context_slot, 0, 4, ptr::null(), 0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut input_pos = 0;
|
||||||
|
let mut decoded = Vec::new();
|
||||||
|
for _ in 0..128 {
|
||||||
|
let input_end = min(FRAME.len(), input_pos + 1);
|
||||||
|
let mut input = ZSTD_inBuffer {
|
||||||
|
src: FRAME.as_ptr().cast(),
|
||||||
|
size: input_end,
|
||||||
|
pos: input_pos,
|
||||||
|
};
|
||||||
|
let mut chunk = [0u8; 2];
|
||||||
|
let mut output = ZSTD_outBuffer {
|
||||||
|
dst: chunk.as_mut_ptr().cast(),
|
||||||
|
size: chunk.len(),
|
||||||
|
pos: 0,
|
||||||
|
};
|
||||||
|
let old_input_pos = input.pos;
|
||||||
|
let hint = unsafe { decompress_legacy_stream(context, 4, &mut output, &mut input) };
|
||||||
|
assert!(!ERR_isError(hint));
|
||||||
|
assert!(input.pos > old_input_pos || output.pos != 0 || input_end == FRAME.len());
|
||||||
|
input_pos = input.pos;
|
||||||
|
decoded.extend_from_slice(&chunk[..output.pos]);
|
||||||
|
|
||||||
|
if input_pos == FRAME.len() && decoded == expected {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(input_pos, FRAME.len());
|
||||||
|
assert_eq!(decoded, expected);
|
||||||
|
unsafe { free_legacy_stream_context(context, 4) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn frame_header_prefix(format: c_int) -> usize {
|
fn frame_header_prefix(format: c_int) -> usize {
|
||||||
if format == ZSTD_F_ZSTD1 {
|
if format == ZSTD_F_ZSTD1 {
|
||||||
|
|||||||
Reference in New Issue
Block a user