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,
|
||||
const void* src, size_t srcSize,
|
||||
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_dctx_view(ZSTD_DCtx* dctx, ZSTD_rustDctxView* out)
|
||||
@@ -428,46 +424,6 @@ size_t ZSTD_rust_legacy_decompress(void* dst, size_t dstCapacity,
|
||||
#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)
|
||||
{
|
||||
#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_USE_ONCE: c_int = 1;
|
||||
|
||||
/* The C legacy dispatcher uses a static byte when a zero-sized input,
|
||||
* output, or dictionary buffer is represented by NULL. Keep the same
|
||||
* lifetime and mutability without making the private ZSTD_DCtx layout part
|
||||
* of the Rust ABI. */
|
||||
static mut LEGACY_EMPTY_BYTE: u8 = 0;
|
||||
|
||||
const ZSTDDS_GET_FRAME_HEADER_SIZE: c_int = 0;
|
||||
const ZSTDDS_DECODE_FRAME_HEADER: c_int = 1;
|
||||
const ZSTDDS_DECODE_BLOCK_HEADER: c_int = 2;
|
||||
@@ -343,13 +349,6 @@ unsafe extern "C" {
|
||||
dict: *const c_void,
|
||||
dict_size: usize,
|
||||
) -> usize;
|
||||
fn ZSTD_rust_legacy_decompress_stream(
|
||||
dctx: *mut ZSTD_DCtx,
|
||||
output: *mut ZSTD_outBuffer,
|
||||
input: *mut ZSTD_inBuffer,
|
||||
dict: *const c_void,
|
||||
dict_size: usize,
|
||||
) -> usize;
|
||||
fn ZSTD_rust_legacy_free_stream(dctx: *mut ZSTD_DCtx);
|
||||
fn ZSTD_decompressBlock_internal(
|
||||
dctx: *mut ZSTD_DCtx,
|
||||
@@ -378,6 +377,428 @@ unsafe fn dctx_view(dctx: *mut ZSTD_DCtx) -> ZSTD_rustDctxView {
|
||||
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]
|
||||
fn frame_header_prefix(format: c_int) -> usize {
|
||||
if format == ZSTD_F_ZSTD1 {
|
||||
|
||||
Reference in New Issue
Block a user