feat(compress): move stream orchestration into Rust
The high-level single-thread stream path previously combined stable-input rewind, buffer fill, direct-versus-buffered output, pending flush, end-of-frame reset, and progress hints in C. The multithreaded path likewise owned input range selection, rsync and end-directive adjustments, job creation decisions, and outer flush return policy in C. Add explicit C/Rust projections and callbacks. Rust now drives the single-thread state machine and the MT scheduling and flush decision sequence, while C retains CCtx-sensitive block callbacks, reusable buffers, job descriptors, synchronization, and worker lifecycle. Focused tests cover stable and buffered stream behavior, errors, pending output, frame completion, and MT scheduler branches. Sequence-store and dictionary boundaries remain unchanged. Test Plan: - Rust lib and CLI checks, tests, formatting, and clippy passed. - Rust legacy feature matrix passed: 556 tests. - Native lib and CLI builds passed; CLI tests passed: 41 tests. - Native test-zstd, fuzzer, zstream, and decode-corpus gates passed.
This commit is contained in:
+115
-179
@@ -200,6 +200,78 @@ typedef char ZSTD_rust_compress_continue_state_layout[
|
||||
&& sizeof(ZSTD_rust_compressContinueState)
|
||||
== (sizeof(void*) == 8 ? 96 : 52))
|
||||
? 1 : -1];
|
||||
|
||||
/* Rust owns the single-threaded buffered/stable stream state machine. The
|
||||
* projection contains only stream bookkeeping and callback slots; operations
|
||||
* which still need the private CCtx layout remain C callbacks. */
|
||||
typedef size_t (*ZSTD_rust_compressStreamBlock_f)(
|
||||
void* context, void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize);
|
||||
typedef size_t (*ZSTD_rust_compressStreamReset_f)(void* context);
|
||||
typedef struct {
|
||||
void* callbackContext;
|
||||
int inBufferMode;
|
||||
int outBufferMode;
|
||||
ZSTD_cStreamStage* streamStage;
|
||||
size_t blockSizeMax;
|
||||
size_t* stableInNotConsumed;
|
||||
void* inBuff;
|
||||
size_t inBuffSize;
|
||||
size_t* inToCompress;
|
||||
size_t* inBuffPos;
|
||||
size_t* inBuffTarget;
|
||||
void* outBuff;
|
||||
size_t outBuffSize;
|
||||
size_t* outBuffContentSize;
|
||||
size_t* outBuffFlushedSize;
|
||||
U32* frameEnded;
|
||||
ZSTD_rust_compressStreamBlock_f compressContinue;
|
||||
ZSTD_rust_compressStreamBlock_f compressEnd;
|
||||
ZSTD_rust_compressStreamReset_f resetSession;
|
||||
} ZSTD_rust_compressStreamState;
|
||||
size_t ZSTD_rust_compressStreamGeneric(
|
||||
const ZSTD_rust_compressStreamState* state,
|
||||
ZSTD_outBuffer* output, ZSTD_inBuffer* input, int flushMode);
|
||||
typedef char ZSTD_rust_compress_stream_state_layout[
|
||||
(offsetof(ZSTD_rust_compressStreamState, callbackContext) == 0
|
||||
&& offsetof(ZSTD_rust_compressStreamState, inBufferMode) == sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, outBufferMode)
|
||||
== sizeof(void*) + sizeof(int)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, streamStage)
|
||||
== sizeof(void*) + 2 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, blockSizeMax)
|
||||
== 2 * sizeof(void*) + 2 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, stableInNotConsumed)
|
||||
== 3 * sizeof(void*) + 2 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, inBuff)
|
||||
== 4 * sizeof(void*) + 2 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, inBuffSize)
|
||||
== 5 * sizeof(void*) + 2 * sizeof(int)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, inToCompress)
|
||||
== 5 * sizeof(void*) + 2 * sizeof(int) + sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, inBuffPos)
|
||||
== 6 * sizeof(void*) + 2 * sizeof(int) + sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, inBuffTarget)
|
||||
== 7 * sizeof(void*) + 2 * sizeof(int) + sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, outBuff)
|
||||
== 8 * sizeof(void*) + 2 * sizeof(int) + sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, outBuffSize)
|
||||
== 9 * sizeof(void*) + 2 * sizeof(int) + sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, outBuffContentSize)
|
||||
== 9 * sizeof(void*) + 2 * sizeof(int) + 2 * sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, outBuffFlushedSize)
|
||||
== 10 * sizeof(void*) + 2 * sizeof(int) + 2 * sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, frameEnded)
|
||||
== 11 * sizeof(void*) + 2 * sizeof(int) + 2 * sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, compressContinue)
|
||||
== 12 * sizeof(void*) + 2 * sizeof(int) + 2 * sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, compressEnd)
|
||||
== 13 * sizeof(void*) + 2 * sizeof(int) + 2 * sizeof(size_t)
|
||||
&& offsetof(ZSTD_rust_compressStreamState, resetSession)
|
||||
== 14 * sizeof(void*) + 2 * sizeof(int) + 2 * sizeof(size_t)
|
||||
&& sizeof(ZSTD_rust_compressStreamState)
|
||||
== 15 * sizeof(void*) + 2 * sizeof(int) + 2 * sizeof(size_t))
|
||||
? 1 : -1];
|
||||
/* The target-sized block body only needs this narrow projection of ZSTD_CCtx.
|
||||
* Matchfinder/window state, sequence-store construction, and outer repeat-mode
|
||||
* cleanup remain in C. */
|
||||
@@ -4498,190 +4570,54 @@ static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx)
|
||||
/** ZSTD_compressStream_generic():
|
||||
* internal function for all *compressStream*() variants
|
||||
* @return : hint size for next input to complete ongoing block */
|
||||
static size_t ZSTD_rust_compressStream_continue(
|
||||
void* context, void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize)
|
||||
{
|
||||
return ZSTD_compressContinue_public(
|
||||
(ZSTD_CCtx*)context, dst, dstCapacity, src, srcSize);
|
||||
}
|
||||
|
||||
static size_t ZSTD_rust_compressStream_end(
|
||||
void* context, void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize)
|
||||
{
|
||||
return ZSTD_compressEnd_public(
|
||||
(ZSTD_CCtx*)context, dst, dstCapacity, src, srcSize);
|
||||
}
|
||||
|
||||
static size_t ZSTD_rust_compressStream_reset(void* context)
|
||||
{
|
||||
return ZSTD_CCtx_reset((ZSTD_CCtx*)context, ZSTD_reset_session_only);
|
||||
}
|
||||
|
||||
static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
|
||||
ZSTD_outBuffer* output,
|
||||
ZSTD_inBuffer* input,
|
||||
ZSTD_EndDirective const flushMode)
|
||||
{
|
||||
const char* const istart = (assert(input != NULL), (const char*)input->src);
|
||||
const char* const iend = (istart != NULL) ? istart + input->size : istart;
|
||||
const char* ip = (istart != NULL) ? istart + input->pos : istart;
|
||||
char* const ostart = (assert(output != NULL), (char*)output->dst);
|
||||
char* const oend = (ostart != NULL) ? ostart + output->size : ostart;
|
||||
char* op = (ostart != NULL) ? ostart + output->pos : ostart;
|
||||
U32 someMoreWork = 1;
|
||||
|
||||
/* check expectations */
|
||||
DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%i, srcSize = %zu", (int)flushMode, input->size - input->pos);
|
||||
assert(zcs != NULL);
|
||||
if (zcs->appliedParams.inBufferMode == ZSTD_bm_stable) {
|
||||
assert(input->pos >= zcs->stableIn_notConsumed);
|
||||
input->pos -= zcs->stableIn_notConsumed;
|
||||
if (ip) ip -= zcs->stableIn_notConsumed;
|
||||
zcs->stableIn_notConsumed = 0;
|
||||
}
|
||||
if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) {
|
||||
assert(zcs->inBuff != NULL);
|
||||
assert(zcs->inBuffSize > 0);
|
||||
}
|
||||
if (zcs->appliedParams.outBufferMode == ZSTD_bm_buffered) {
|
||||
assert(zcs->outBuff != NULL);
|
||||
assert(zcs->outBuffSize > 0);
|
||||
}
|
||||
if (input->src == NULL) assert(input->size == 0);
|
||||
assert(input->pos <= input->size);
|
||||
if (output->dst == NULL) assert(output->size == 0);
|
||||
assert(output->pos <= output->size);
|
||||
assert((U32)flushMode <= (U32)ZSTD_e_end);
|
||||
|
||||
while (someMoreWork) {
|
||||
switch(zcs->streamStage)
|
||||
{
|
||||
case zcss_init:
|
||||
RETURN_ERROR(init_missing, "call ZSTD_initCStream() first!");
|
||||
|
||||
case zcss_load:
|
||||
if ( (flushMode == ZSTD_e_end)
|
||||
&& ( (size_t)(oend-op) >= ZSTD_compressBound((size_t)(iend-ip)) /* Enough output space */
|
||||
|| zcs->appliedParams.outBufferMode == ZSTD_bm_stable) /* OR we are allowed to return dstSizeTooSmall */
|
||||
&& (zcs->inBuffPos == 0) ) {
|
||||
/* shortcut to compression pass directly into output buffer */
|
||||
size_t const cSize = ZSTD_compressEnd_public(zcs,
|
||||
op, (size_t)(oend-op),
|
||||
ip, (size_t)(iend-ip));
|
||||
DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize);
|
||||
FORWARD_IF_ERROR(cSize, "ZSTD_compressEnd failed");
|
||||
ip = iend;
|
||||
op += cSize;
|
||||
zcs->frameEnded = 1;
|
||||
ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
|
||||
someMoreWork = 0; break;
|
||||
}
|
||||
/* complete loading into inBuffer in buffered mode */
|
||||
if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) {
|
||||
size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos;
|
||||
size_t const loaded = ZSTD_limitCopy(
|
||||
zcs->inBuff + zcs->inBuffPos, toLoad,
|
||||
ip, (size_t)(iend-ip));
|
||||
zcs->inBuffPos += loaded;
|
||||
if (ip) ip += loaded;
|
||||
if ( (flushMode == ZSTD_e_continue)
|
||||
&& (zcs->inBuffPos < zcs->inBuffTarget) ) {
|
||||
/* not enough input to fill full block : stop here */
|
||||
someMoreWork = 0; break;
|
||||
}
|
||||
if ( (flushMode == ZSTD_e_flush)
|
||||
&& (zcs->inBuffPos == zcs->inToCompress) ) {
|
||||
/* empty */
|
||||
someMoreWork = 0; break;
|
||||
}
|
||||
} else {
|
||||
assert(zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
|
||||
if ( (flushMode == ZSTD_e_continue)
|
||||
&& ( (size_t)(iend - ip) < zcs->blockSizeMax) ) {
|
||||
/* can't compress a full block : stop here */
|
||||
zcs->stableIn_notConsumed = (size_t)(iend - ip);
|
||||
ip = iend; /* pretend to have consumed input */
|
||||
someMoreWork = 0; break;
|
||||
}
|
||||
if ( (flushMode == ZSTD_e_flush)
|
||||
&& (ip == iend) ) {
|
||||
/* empty */
|
||||
someMoreWork = 0; break;
|
||||
}
|
||||
}
|
||||
/* compress current block (note : this stage cannot be stopped in the middle) */
|
||||
DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode);
|
||||
{ int const inputBuffered = (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered);
|
||||
void* cDst;
|
||||
size_t cSize;
|
||||
size_t oSize = (size_t)(oend-op);
|
||||
size_t const iSize = inputBuffered ? zcs->inBuffPos - zcs->inToCompress
|
||||
: MIN((size_t)(iend - ip), zcs->blockSizeMax);
|
||||
if (oSize >= ZSTD_compressBound(iSize) || zcs->appliedParams.outBufferMode == ZSTD_bm_stable)
|
||||
cDst = op; /* compress into output buffer, to skip flush stage */
|
||||
else
|
||||
cDst = zcs->outBuff, oSize = zcs->outBuffSize;
|
||||
if (inputBuffered) {
|
||||
unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend);
|
||||
cSize = lastBlock ?
|
||||
ZSTD_compressEnd_public(zcs, cDst, oSize,
|
||||
zcs->inBuff + zcs->inToCompress, iSize) :
|
||||
ZSTD_compressContinue_public(zcs, cDst, oSize,
|
||||
zcs->inBuff + zcs->inToCompress, iSize);
|
||||
FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
|
||||
zcs->frameEnded = lastBlock;
|
||||
/* prepare next block */
|
||||
zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSizeMax;
|
||||
if (zcs->inBuffTarget > zcs->inBuffSize)
|
||||
zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSizeMax;
|
||||
DEBUGLOG(5, "inBuffTarget:%u / inBuffSize:%u",
|
||||
(unsigned)zcs->inBuffTarget, (unsigned)zcs->inBuffSize);
|
||||
if (!lastBlock)
|
||||
assert(zcs->inBuffTarget <= zcs->inBuffSize);
|
||||
zcs->inToCompress = zcs->inBuffPos;
|
||||
} else { /* !inputBuffered, hence ZSTD_bm_stable */
|
||||
unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip + iSize == iend);
|
||||
cSize = lastBlock ?
|
||||
ZSTD_compressEnd_public(zcs, cDst, oSize, ip, iSize) :
|
||||
ZSTD_compressContinue_public(zcs, cDst, oSize, ip, iSize);
|
||||
/* Consume the input prior to error checking to mirror buffered mode. */
|
||||
if (ip) ip += iSize;
|
||||
FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
|
||||
zcs->frameEnded = lastBlock;
|
||||
if (lastBlock) assert(ip == iend);
|
||||
}
|
||||
if (cDst == op) { /* no need to flush */
|
||||
op += cSize;
|
||||
if (zcs->frameEnded) {
|
||||
DEBUGLOG(5, "Frame completed directly in outBuffer");
|
||||
someMoreWork = 0;
|
||||
ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
|
||||
}
|
||||
break;
|
||||
}
|
||||
zcs->outBuffContentSize = cSize;
|
||||
zcs->outBuffFlushedSize = 0;
|
||||
zcs->streamStage = zcss_flush; /* pass-through to flush stage */
|
||||
}
|
||||
ZSTD_FALLTHROUGH;
|
||||
case zcss_flush:
|
||||
DEBUGLOG(5, "flush stage");
|
||||
assert(zcs->appliedParams.outBufferMode == ZSTD_bm_buffered);
|
||||
{ size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize;
|
||||
size_t const flushed = ZSTD_limitCopy(op, (size_t)(oend-op),
|
||||
zcs->outBuff + zcs->outBuffFlushedSize, toFlush);
|
||||
DEBUGLOG(5, "toFlush: %u into %u ==> flushed: %u",
|
||||
(unsigned)toFlush, (unsigned)(oend-op), (unsigned)flushed);
|
||||
if (flushed)
|
||||
op += flushed;
|
||||
zcs->outBuffFlushedSize += flushed;
|
||||
if (toFlush!=flushed) {
|
||||
/* flush not fully completed, presumably because dst is too small */
|
||||
assert(op==oend);
|
||||
someMoreWork = 0;
|
||||
break;
|
||||
}
|
||||
zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0;
|
||||
if (zcs->frameEnded) {
|
||||
DEBUGLOG(5, "Frame completed on flush");
|
||||
someMoreWork = 0;
|
||||
ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
|
||||
break;
|
||||
}
|
||||
zcs->streamStage = zcss_load;
|
||||
break;
|
||||
}
|
||||
|
||||
default: /* impossible */
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
input->pos = (size_t)(ip - istart);
|
||||
output->pos = (size_t)(op - ostart);
|
||||
if (zcs->frameEnded) return 0;
|
||||
return ZSTD_nextInputSizeHint(zcs);
|
||||
ZSTD_rust_compressStreamState state;
|
||||
state.callbackContext = zcs;
|
||||
state.inBufferMode = (int)zcs->appliedParams.inBufferMode;
|
||||
state.outBufferMode = (int)zcs->appliedParams.outBufferMode;
|
||||
state.streamStage = &zcs->streamStage;
|
||||
state.blockSizeMax = zcs->blockSizeMax;
|
||||
state.stableInNotConsumed = &zcs->stableIn_notConsumed;
|
||||
state.inBuff = zcs->inBuff;
|
||||
state.inBuffSize = zcs->inBuffSize;
|
||||
state.inToCompress = &zcs->inToCompress;
|
||||
state.inBuffPos = &zcs->inBuffPos;
|
||||
state.inBuffTarget = &zcs->inBuffTarget;
|
||||
state.outBuff = zcs->outBuff;
|
||||
state.outBuffSize = zcs->outBuffSize;
|
||||
state.outBuffContentSize = &zcs->outBuffContentSize;
|
||||
state.outBuffFlushedSize = &zcs->outBuffFlushedSize;
|
||||
state.frameEnded = &zcs->frameEnded;
|
||||
state.compressContinue = ZSTD_rust_compressStream_continue;
|
||||
state.compressEnd = ZSTD_rust_compressStream_end;
|
||||
state.resetSession = ZSTD_rust_compressStream_reset;
|
||||
return ZSTD_rust_compressStreamGeneric(
|
||||
&state, output, input, (int)flushMode);
|
||||
}
|
||||
|
||||
static size_t ZSTD_nextInputSizeHint_MTorST(const ZSTD_CCtx* cctx)
|
||||
|
||||
Reference in New Issue
Block a user