Files
zstd-rs/lib/decompress/zstd_decompress.c
T
ddidderr fe7e24c770 feat(rust): migrate high-level runtime paths
Move long-distance matching and high-level decompression from C shims into
Rust. The decoder now owns context, dictionary, parameter, one-shot, and
buffered streaming state while C retains allocation/configuration, legacy,
and trace leaves.

Move CLI parsing, safety policy, and dispatch into a separate Rust static
archive. Keeping it separate prevents library builds from retaining FIO
symbols, while C continues to own file opening, replacement, and I/O.
Program targets now select matching compression/decompression archives.

The remaining C boundary is intentional: high-level compression, optimal
parsing, dictionary building, legacy callbacks, and CLI file I/O still need
migration.

Test Plan:
- cargo test --all-targets (native and i686)
- cargo test --all-targets in rust/cli (native and i686)
- CLI crate compression-only and decompression-only feature tests
- native and i686 fuzzer/zstreamtest runs, plus legacy and dictionary tests
- ZSTD_C_PREDICT and ZSTD_HEAPMODE=0 fuzzer coverage
- library, dynamic-link, and program-target build/round-trip matrix

Refs: rust/README.md
2026-07-11 09:03:41 +02:00

483 lines
16 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, keeps allocation
* ownership in the C allocator domain, and retains the configuration-bound
* legacy and trace leaves.
*/
#define ZSTD_STATIC_LINKING_ONLY
#include "../common/zstd_deps.h"
#include "../common/allocations.h"
#include "../common/error_private.h"
#include "../common/mem.h"
#include "../common/zstd_internal.h"
#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. */
#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;
void ZSTD_rust_dctx_view(ZSTD_DCtx* dctx, ZSTD_rustDctxView* out);
size_t ZSTD_rust_dctx_sizeof(void);
ZSTD_DCtx* ZSTD_rust_dctx_alloc(ZSTD_customMem customMem);
void ZSTD_rust_dctx_free_storage(ZSTD_DCtx* dctx, ZSTD_customMem customMem);
void ZSTD_rust_dctx_init_platform(ZSTD_DCtx* dctx);
size_t ZSTD_rust_dctx_default_max_window_size(void);
int ZSTD_rust_no_forward_progress_max(void);
int ZSTD_rust_heapmode(void);
size_t ZSTD_rust_decompress_stack(void* dst, size_t dstCapacity,
const void* src, size_t srcSize);
void* ZSTD_rust_custom_malloc(size_t size, ZSTD_customMem customMem);
void* ZSTD_rust_custom_calloc(size_t size, ZSTD_customMem customMem);
void ZSTD_rust_custom_free(void* allocation, ZSTD_customMem customMem);
ZSTD_DDict* ZSTD_rust_create_ddict(const void* dict, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
ZSTD_dictContentType_e dictContentType,
ZSTD_customMem customMem);
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);
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)
{
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);
}
ZSTD_DCtx* ZSTD_rust_dctx_alloc(ZSTD_customMem customMem)
{
if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL;
return (ZSTD_DCtx*)ZSTD_customMalloc(sizeof(ZSTD_DCtx), customMem);
}
void ZSTD_rust_dctx_free_storage(ZSTD_DCtx* dctx, ZSTD_customMem customMem)
{
ZSTD_customFree(dctx, customMem);
}
void ZSTD_rust_dctx_init_platform(ZSTD_DCtx* dctx)
{
#if DYNAMIC_BMI2
dctx->bmi2 = ZSTD_cpuSupportsBmi2();
#else
(void)dctx;
#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)
{
#if ZSTD_HEAPMODE >= 1
(void)dst;
(void)dstCapacity;
(void)src;
(void)srcSize;
return ERROR(GENERIC);
#else
ZSTD_DCtx dctx;
ZSTD_DCtx* const initialized = ZSTD_initStaticDCtx(&dctx, sizeof(dctx));
if (initialized == NULL) return ERROR(memory_allocation);
/* This is a stack DCtx, not a user-provided static workspace. Keep the
* original heapmode=0 semantics so legacy decoding is permitted and no
* static-context allocation restrictions leak into the one-shot API. */
initialized->staticSize = 0;
return ZSTD_decompressDCtx(initialized, dst, dstCapacity, src, srcSize);
#endif
}
void* ZSTD_rust_custom_malloc(size_t size, ZSTD_customMem customMem)
{
return ZSTD_customMalloc(size, customMem);
}
void* ZSTD_rust_custom_calloc(size_t size, ZSTD_customMem customMem)
{
return ZSTD_customCalloc(size, customMem);
}
void ZSTD_rust_custom_free(void* allocation, ZSTD_customMem customMem)
{
ZSTD_customFree(allocation, customMem);
}
ZSTD_DDict* ZSTD_rust_create_ddict(const void* dict, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
ZSTD_dictContentType_e dictContentType,
ZSTD_customMem customMem)
{
return ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod,
dictContentType, customMem);
}
void ZSTD_rust_dctx_copy_prefix(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
{
size_t const toCopy = (size_t)((const char*)(&dstDCtx->inBuff) - (const char*)dstDCtx);
ZSTD_memcpy(dstDCtx, srcDCtx, toCopy);
}
void ZSTD_rust_dctx_trace_begin(ZSTD_DCtx* dctx)
{
#if ZSTD_TRACE
dctx->traceCtx = (ZSTD_trace_decompress_begin != NULL)
? ZSTD_trace_decompress_begin(dctx) : 0;
#else
(void)dctx;
#endif
}
void ZSTD_rust_dctx_trace_end(ZSTD_DCtx* dctx, U64 uncompressedSize,
U64 compressedSize, int streaming)
{
#if ZSTD_TRACE
if (dctx->traceCtx && ZSTD_trace_decompress_end != NULL) {
ZSTD_Trace trace;
ZSTD_memset(&trace, 0, sizeof(trace));
trace.version = ZSTD_VERSION_NUMBER;
trace.streaming = streaming;
if (dctx->ddict) {
trace.dictionaryID = ZSTD_getDictID_fromDDict(dctx->ddict);
trace.dictionarySize = ZSTD_DDict_dictSize(dctx->ddict);
trace.dictionaryIsCold = dctx->ddictIsCold;
}
trace.uncompressedSize = (size_t)uncompressedSize;
trace.compressedSize = (size_t)compressedSize;
trace.dctx = dctx;
ZSTD_trace_decompress_end(dctx->traceCtx, &trace);
}
#else
(void)dctx;
(void)uncompressedSize;
(void)compressedSize;
(void)streaming;
#endif
}
unsigned ZSTD_rust_legacy_is(const void* src, size_t srcSize)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
return ZSTD_isLegacy(src, srcSize);
#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
}
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)
if (dctx->legacyContext) {
ZSTD_freeLegacyStreamContext(dctx->legacyContext,
dctx->previousLegacyVersion);
dctx->legacyContext = NULL;
}
#else
(void)dctx;
#endif
}