Files
zstd-rs/lib/decompress/zstd_decompress.c
T
ddidderr cb770812fd refactor(decompress): move heap mode policy to Rust
`ZSTD_HEAPMODE` was still returned directly from C, and the stack entry
projection also copied the raw macro. That left the one-shot stack-versus-heap
classification in the C configuration shim even though Rust owns the public
decompression branch and stack action.

Keep the build-time override in C, but pass it through a one-field repr(C)
projection to Rust. Rust normalizes values below one to the stack mode and
values at or above one to the heap mode, preserving the existing behavior
while making the branch policy explicit. The C stack object, private DCtx
layout, platform initialization, and trace boundary remain C-owned; moving
those would either duplicate build configuration or cross the requested ABI
boundary. The stack projection now uses the same normalized result as the
one-shot branch.

ABI layout assertions cover the scalar projection, and focused Rust tests cover
negative, zero, positive, maximum, and missing-configuration inputs.

Test Plan:
- `git diff --cached --check` -- passed
- `rustfmt --edition 2021 --check rust/src/zstd_decompress.rs` -- passed
- Cargo, Make, native tests, and heavy commands intentionally not run per the
  task restriction; integration verification remains pending.
2026-07-21 09:12:42 +02:00

401 lines
14 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);
typedef struct {
int configured_max;
} ZSTD_rustNoForwardProgressProjection;
typedef char ZSTD_rust_no_forward_progress_projection_layout[
(offsetof(ZSTD_rustNoForwardProgressProjection, configured_max) == 0
&& sizeof(ZSTD_rustNoForwardProgressProjection) == sizeof(int))
? 1 : -1];
int ZSTD_rust_no_forward_progress_policy(
const ZSTD_rustNoForwardProgressProjection* projection);
typedef struct {
int configured_mode;
} ZSTD_rustHeapModeProjection;
typedef char ZSTD_rust_heapmode_projection_layout[
(offsetof(ZSTD_rustHeapModeProjection, configured_mode) == 0
&& sizeof(ZSTD_rustHeapModeProjection) == sizeof(int))
? 1 : -1];
int ZSTD_rust_heapmode_policy(const ZSTD_rustHeapModeProjection* projection);
typedef struct {
unsigned configured_level;
} ZSTD_rustLegacySupportProjection;
typedef char ZSTD_rust_legacy_support_projection_layout[
(offsetof(ZSTD_rustLegacySupportProjection, configured_level) == 0
&& sizeof(ZSTD_rustLegacySupportProjection) == sizeof(unsigned))
? 1 : -1];
unsigned ZSTD_rust_legacy_support_policy(
const ZSTD_rustLegacySupportProjection* projection);
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)
{
ZSTD_rustNoForwardProgressProjection const projection = {
ZSTD_NO_FORWARD_PROGRESS_MAX
};
return ZSTD_rust_no_forward_progress_policy(&projection);
}
int ZSTD_rust_heapmode(void)
{
ZSTD_rustHeapModeProjection const projection = {
ZSTD_HEAPMODE
};
return ZSTD_rust_heapmode_policy(&projection);
}
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_rust_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 scalar leaf so test builds, which compile
* every Rust legacy module but vary this C macro per target, retain the
* historical support boundary. Rust owns normalization of that scalar. */
unsigned ZSTD_rust_legacy_support(void)
{
ZSTD_rustLegacySupportProjection const projection = {
#if defined(ZSTD_LEGACY_SUPPORT)
ZSTD_LEGACY_SUPPORT
#else
0
#endif
};
return ZSTD_rust_legacy_support_policy(&projection);
}