The stack-backed one-shot decompression entry point used to make its own heap-mode rejection, static DCtx initialization, allocation-error mapping, and dispatch ordering in C. That kept scalar control flow beside the private DCtx layout even though Rust already owns the decompression policy and context leaf. Rust now consumes an ABI-checked scalar projection and callback set. It makes the branch and ordering decisions, maps a failed static initializer to the same memory-allocation error, and invokes the existing C-owned context leaf. The C shim retains the local stack object, private DCtx layout, and `ZSTD_initStaticDCtx` implementation. Recording-callback tests cover heap rejection, initializer failure, and successful initialization-to-dispatch ordering without fabricating the private context layout. Test Plan: - `git diff --cached --check` -- passed - `rustfmt --edition 2021 --check rust/src/zstd_decompress.rs` -- passed - Full capped Rust/native verification remains pending until the parallel compression seam is integrated.
367 lines
12 KiB
C
367 lines
12 KiB
C
/*
|
|
* Decoder orchestration is implemented in rust/src/zstd_decompress.rs.
|
|
*
|
|
* `ZSTD_DCtx_s` is intentionally still allocated and laid out by C: its
|
|
* optional members vary with the build configuration and the block decoder
|
|
* shares that object. This translation unit is therefore a deliberately
|
|
* narrow ABI adapter. It projects field addresses to Rust while retaining
|
|
* the private layout and configuration-bound legacy leaves. Weak trace-symbol
|
|
* discovery also stays here; Rust receives only a narrow trace projection.
|
|
*/
|
|
|
|
#define ZSTD_STATIC_LINKING_ONLY
|
|
#include "../common/zstd_deps.h"
|
|
#include "../common/error_private.h"
|
|
#include "../common/mem.h"
|
|
#include "../common/zstd_internal.h"
|
|
#include "zstd_decompress_internal.h"
|
|
#include "zstd_ddict.h"
|
|
|
|
/* 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. */
|
|
#ifndef ZSTD_HEAPMODE
|
|
# define ZSTD_HEAPMODE 1
|
|
#endif
|
|
|
|
#ifndef ZSTD_MAXWINDOWSIZE_DEFAULT
|
|
# define ZSTD_MAXWINDOWSIZE_DEFAULT (((U32)1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) + 1)
|
|
#endif
|
|
|
|
#ifndef ZSTD_NO_FORWARD_PROGRESS_MAX
|
|
# define ZSTD_NO_FORWARD_PROGRESS_MAX 16
|
|
#endif
|
|
|
|
/* Keep this in lock-step with ZSTD_rustDctxView in zstd_decompress.rs.
|
|
* Every pointer that names a scalar is the address of that scalar; array and
|
|
* embedded-object entries name their first byte. */
|
|
typedef struct {
|
|
void* dctx;
|
|
void* llt_ptr;
|
|
void* mlt_ptr;
|
|
void* oft_ptr;
|
|
void* huf_ptr;
|
|
void* entropy;
|
|
void* workspace;
|
|
size_t workspace_size;
|
|
void* previous_dst_end;
|
|
void* prefix_start;
|
|
void* virtual_start;
|
|
void* dict_end;
|
|
void* expected;
|
|
void* f_params;
|
|
void* processed_c_size;
|
|
void* decoded_size;
|
|
void* b_type;
|
|
void* stage;
|
|
void* lit_entropy;
|
|
void* fse_entropy;
|
|
void* xxh_state;
|
|
void* header_size;
|
|
void* format;
|
|
void* force_ignore_checksum;
|
|
void* validate_checksum;
|
|
void* lit_ptr;
|
|
void* custom_mem;
|
|
void* lit_size;
|
|
void* rle_size;
|
|
void* static_size;
|
|
void* is_frame_decompression;
|
|
void* ddict_local;
|
|
void* ddict;
|
|
void* dict_id;
|
|
void* ddict_is_cold;
|
|
void* dict_uses;
|
|
void* ddict_set;
|
|
void* ref_multiple_ddicts;
|
|
void* disable_huf_asm;
|
|
void* max_block_size_param;
|
|
void* stream_stage;
|
|
void* in_buff;
|
|
void* in_buff_size;
|
|
void* in_pos;
|
|
void* max_window_size;
|
|
void* out_buff;
|
|
void* out_buff_size;
|
|
void* out_start;
|
|
void* out_end;
|
|
void* lh_size;
|
|
void* legacy_context;
|
|
void* previous_legacy_version;
|
|
void* legacy_version;
|
|
void* hostage_byte;
|
|
void* no_forward_progress;
|
|
void* out_buffer_mode;
|
|
void* expected_out_buffer;
|
|
void* lit_buffer;
|
|
void* lit_buffer_end;
|
|
void* lit_buffer_location;
|
|
void* lit_extra_buffer;
|
|
size_t lit_extra_buffer_size;
|
|
void* header_buffer;
|
|
size_t header_buffer_size;
|
|
void* oversized_duration;
|
|
void* fuzz_begin;
|
|
void* fuzz_end;
|
|
size_t dctx_size;
|
|
} ZSTD_rustDctxView;
|
|
|
|
typedef char ZSTD_rust_dctx_view_layout[
|
|
(offsetof(ZSTD_rustDctxView, dctx) == 0
|
|
&& offsetof(ZSTD_rustDctxView, static_size) == 29 * sizeof(void*)
|
|
&& offsetof(ZSTD_rustDctxView, dctx_size) == 67 * sizeof(void*)
|
|
&& sizeof(ZSTD_rustDctxView) == 68 * sizeof(void*)) ? 1 : -1];
|
|
|
|
/* Rust exposes this as u64. Use the trace API's exact unsigned-long-long
|
|
* representation instead of U64, whose typedef may be a different C type on
|
|
* some targets even when it has the same width. */
|
|
typedef unsigned long long ZSTD_rustTraceCtx;
|
|
typedef ZSTD_rustTraceCtx (*ZSTD_rustTraceBeginFn)(const void* dctx);
|
|
typedef void (*ZSTD_rustTraceEndFn)(ZSTD_rustTraceCtx traceCtx,
|
|
const void* trace);
|
|
|
|
typedef struct {
|
|
ZSTD_rustTraceCtx* trace_ctx;
|
|
ZSTD_rustTraceBeginFn begin;
|
|
ZSTD_rustTraceEndFn end;
|
|
const void* dctx;
|
|
const void* ddict;
|
|
int dictionary_is_cold;
|
|
} ZSTD_rustDctxTraceView;
|
|
|
|
void ZSTD_rust_dctx_view(ZSTD_DCtx* dctx, ZSTD_rustDctxView* out);
|
|
void ZSTD_rust_dctx_trace_view(ZSTD_DCtx* dctx,
|
|
ZSTD_rustDctxTraceView* out);
|
|
size_t ZSTD_rust_dctx_sizeof(void);
|
|
void ZSTD_dctx_init_platform(ZSTD_DCtx* dctx);
|
|
void ZSTD_rust_dctx_init_platform(void* bmi2, int dynamic_bmi2);
|
|
size_t ZSTD_rust_dctx_default_max_window_size(void);
|
|
int ZSTD_rust_no_forward_progress_max(void);
|
|
size_t ZSTD_rust_decompress_stack(void* dst, size_t dstCapacity,
|
|
const void* src, size_t srcSize);
|
|
size_t ZSTD_rust_decompress_stack_context(ZSTD_DCtx* dctx,
|
|
void* dst, size_t dstCapacity,
|
|
const void* src, size_t srcSize);
|
|
|
|
typedef struct {
|
|
int heapmode;
|
|
void* workspace;
|
|
size_t workspace_size;
|
|
void* dst;
|
|
size_t dst_capacity;
|
|
const void* src;
|
|
size_t src_size;
|
|
} ZSTD_rustDecompressStackProjection;
|
|
typedef char ZSTD_rust_decompress_stack_projection_layout[
|
|
(offsetof(ZSTD_rustDecompressStackProjection, heapmode) == 0
|
|
&& offsetof(ZSTD_rustDecompressStackProjection, workspace) == sizeof(void*)
|
|
&& offsetof(ZSTD_rustDecompressStackProjection, workspace_size)
|
|
== 2 * sizeof(void*)
|
|
&& offsetof(ZSTD_rustDecompressStackProjection, dst)
|
|
== 3 * sizeof(void*)
|
|
&& offsetof(ZSTD_rustDecompressStackProjection, dst_capacity)
|
|
== 4 * sizeof(void*)
|
|
&& offsetof(ZSTD_rustDecompressStackProjection, src)
|
|
== 5 * sizeof(void*)
|
|
&& offsetof(ZSTD_rustDecompressStackProjection, src_size)
|
|
== 6 * sizeof(void*)
|
|
&& sizeof(ZSTD_rustDecompressStackProjection) == 7 * sizeof(void*))
|
|
? 1 : -1];
|
|
|
|
typedef ZSTD_DCtx* (*ZSTD_rustInitStaticDCtxFn)(void* workspace,
|
|
size_t workspace_size);
|
|
typedef size_t (*ZSTD_rustDecompressStackContextFn)(
|
|
ZSTD_DCtx* dctx, void* dst, size_t dstCapacity,
|
|
const void* src, size_t srcSize);
|
|
typedef struct {
|
|
ZSTD_rustInitStaticDCtxFn init_static_dctx;
|
|
ZSTD_rustDecompressStackContextFn decompress_context;
|
|
} ZSTD_rustDecompressStackCallbacks;
|
|
typedef char ZSTD_rust_decompress_stack_callbacks_layout[
|
|
(offsetof(ZSTD_rustDecompressStackCallbacks, init_static_dctx) == 0
|
|
&& offsetof(ZSTD_rustDecompressStackCallbacks, decompress_context)
|
|
== sizeof(void*)
|
|
&& sizeof(ZSTD_rustDecompressStackCallbacks) == 2 * sizeof(void*))
|
|
? 1 : -1];
|
|
|
|
size_t ZSTD_rust_decompress_stack_action(
|
|
const ZSTD_rustDecompressStackProjection* projection,
|
|
const ZSTD_rustDecompressStackCallbacks* callbacks);
|
|
unsigned ZSTD_rust_legacy_support(void);
|
|
|
|
#if ZSTD_TRACE
|
|
static ZSTD_rustTraceCtx ZSTD_rust_trace_begin_callback(const void* dctx)
|
|
{
|
|
return (ZSTD_trace_decompress_begin != NULL)
|
|
? ZSTD_trace_decompress_begin((const ZSTD_DCtx*)dctx) : 0;
|
|
}
|
|
|
|
static void ZSTD_rust_trace_end_callback(ZSTD_rustTraceCtx traceCtx,
|
|
const void* trace)
|
|
{
|
|
if (ZSTD_trace_decompress_end != NULL) {
|
|
ZSTD_trace_decompress_end((ZSTD_TraceCtx)traceCtx,
|
|
(const ZSTD_Trace*)trace);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void ZSTD_rust_dctx_view(ZSTD_DCtx* dctx, ZSTD_rustDctxView* out)
|
|
{
|
|
ZSTD_memset(out, 0, sizeof(*out));
|
|
if (dctx == NULL) return;
|
|
|
|
out->dctx = dctx;
|
|
out->llt_ptr = &dctx->LLTptr;
|
|
out->mlt_ptr = &dctx->MLTptr;
|
|
out->oft_ptr = &dctx->OFTptr;
|
|
out->huf_ptr = &dctx->HUFptr;
|
|
out->entropy = &dctx->entropy;
|
|
out->workspace = dctx->workspace;
|
|
out->workspace_size = sizeof(dctx->workspace);
|
|
out->previous_dst_end = &dctx->previousDstEnd;
|
|
out->prefix_start = &dctx->prefixStart;
|
|
out->virtual_start = &dctx->virtualStart;
|
|
out->dict_end = &dctx->dictEnd;
|
|
out->expected = &dctx->expected;
|
|
out->f_params = &dctx->fParams;
|
|
out->processed_c_size = &dctx->processedCSize;
|
|
out->decoded_size = &dctx->decodedSize;
|
|
out->b_type = &dctx->bType;
|
|
out->stage = &dctx->stage;
|
|
out->lit_entropy = &dctx->litEntropy;
|
|
out->fse_entropy = &dctx->fseEntropy;
|
|
out->xxh_state = &dctx->xxhState;
|
|
out->header_size = &dctx->headerSize;
|
|
out->format = &dctx->format;
|
|
out->force_ignore_checksum = &dctx->forceIgnoreChecksum;
|
|
out->validate_checksum = &dctx->validateChecksum;
|
|
out->lit_ptr = &dctx->litPtr;
|
|
out->custom_mem = &dctx->customMem;
|
|
out->lit_size = &dctx->litSize;
|
|
out->rle_size = &dctx->rleSize;
|
|
out->static_size = &dctx->staticSize;
|
|
out->is_frame_decompression = &dctx->isFrameDecompression;
|
|
out->ddict_local = &dctx->ddictLocal;
|
|
out->ddict = &dctx->ddict;
|
|
out->dict_id = &dctx->dictID;
|
|
out->ddict_is_cold = &dctx->ddictIsCold;
|
|
out->dict_uses = &dctx->dictUses;
|
|
out->ddict_set = &dctx->ddictSet;
|
|
out->ref_multiple_ddicts = &dctx->refMultipleDDicts;
|
|
out->disable_huf_asm = &dctx->disableHufAsm;
|
|
out->max_block_size_param = &dctx->maxBlockSizeParam;
|
|
out->stream_stage = &dctx->streamStage;
|
|
out->in_buff = &dctx->inBuff;
|
|
out->in_buff_size = &dctx->inBuffSize;
|
|
out->in_pos = &dctx->inPos;
|
|
out->max_window_size = &dctx->maxWindowSize;
|
|
out->out_buff = &dctx->outBuff;
|
|
out->out_buff_size = &dctx->outBuffSize;
|
|
out->out_start = &dctx->outStart;
|
|
out->out_end = &dctx->outEnd;
|
|
out->lh_size = &dctx->lhSize;
|
|
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
|
out->legacy_context = &dctx->legacyContext;
|
|
out->previous_legacy_version = &dctx->previousLegacyVersion;
|
|
out->legacy_version = &dctx->legacyVersion;
|
|
#endif
|
|
out->hostage_byte = &dctx->hostageByte;
|
|
out->no_forward_progress = &dctx->noForwardProgress;
|
|
out->out_buffer_mode = &dctx->outBufferMode;
|
|
out->expected_out_buffer = &dctx->expectedOutBuffer;
|
|
out->lit_buffer = &dctx->litBuffer;
|
|
out->lit_buffer_end = &dctx->litBufferEnd;
|
|
out->lit_buffer_location = &dctx->litBufferLocation;
|
|
out->lit_extra_buffer = dctx->litExtraBuffer;
|
|
out->lit_extra_buffer_size = sizeof(dctx->litExtraBuffer);
|
|
out->header_buffer = dctx->headerBuffer;
|
|
out->header_buffer_size = sizeof(dctx->headerBuffer);
|
|
out->oversized_duration = &dctx->oversizedDuration;
|
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
out->fuzz_begin = &dctx->dictContentBeginForFuzzing;
|
|
out->fuzz_end = &dctx->dictContentEndForFuzzing;
|
|
#endif
|
|
out->dctx_size = sizeof(*dctx);
|
|
}
|
|
|
|
size_t ZSTD_rust_dctx_sizeof(void)
|
|
{
|
|
return sizeof(ZSTD_DCtx);
|
|
}
|
|
|
|
void ZSTD_dctx_init_platform(ZSTD_DCtx* dctx)
|
|
{
|
|
#if DYNAMIC_BMI2
|
|
ZSTD_rust_dctx_init_platform(&dctx->bmi2, DYNAMIC_BMI2);
|
|
#else
|
|
(void)dctx;
|
|
ZSTD_rust_dctx_init_platform(NULL, DYNAMIC_BMI2);
|
|
#endif
|
|
}
|
|
|
|
size_t ZSTD_rust_dctx_default_max_window_size(void)
|
|
{
|
|
return ZSTD_MAXWINDOWSIZE_DEFAULT;
|
|
}
|
|
|
|
int ZSTD_rust_no_forward_progress_max(void)
|
|
{
|
|
return ZSTD_NO_FORWARD_PROGRESS_MAX;
|
|
}
|
|
|
|
int ZSTD_rust_heapmode(void)
|
|
{
|
|
return ZSTD_HEAPMODE;
|
|
}
|
|
|
|
size_t ZSTD_rust_decompress_stack(void* dst, size_t dstCapacity,
|
|
const void* src, size_t srcSize)
|
|
{
|
|
ZSTD_DCtx dctx;
|
|
ZSTD_rustDecompressStackProjection const projection = {
|
|
ZSTD_HEAPMODE,
|
|
&dctx,
|
|
sizeof(dctx),
|
|
dst,
|
|
dstCapacity,
|
|
src,
|
|
srcSize
|
|
};
|
|
ZSTD_rustDecompressStackCallbacks const callbacks = {
|
|
ZSTD_initStaticDCtx,
|
|
ZSTD_rust_decompress_stack_context
|
|
};
|
|
return ZSTD_rust_decompress_stack_action(&projection, &callbacks);
|
|
}
|
|
|
|
void ZSTD_rust_dctx_trace_view(ZSTD_DCtx* dctx,
|
|
ZSTD_rustDctxTraceView* out)
|
|
{
|
|
ZSTD_memset(out, 0, sizeof(*out));
|
|
out->dctx = dctx;
|
|
#if ZSTD_TRACE
|
|
out->trace_ctx = &dctx->traceCtx;
|
|
out->begin = (ZSTD_trace_decompress_begin != NULL)
|
|
? ZSTD_rust_trace_begin_callback : NULL;
|
|
out->end = (ZSTD_trace_decompress_end != NULL)
|
|
? ZSTD_rust_trace_end_callback : NULL;
|
|
out->ddict = dctx->ddict;
|
|
out->dictionary_is_cold = dctx->ddictIsCold;
|
|
#endif
|
|
}
|
|
|
|
/* 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) \
|
|
&& (ZSTD_LEGACY_SUPPORT <= 7)
|
|
return ZSTD_LEGACY_SUPPORT;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|