Fix super block compression and stream raw blocks in decompression (#1947)
Super blocks must never violate the zstd block bound of input_size + ZSTD_blockHeaderSize. The individual sub-blocks may, but not the super block. If the superblock violates the block bound we are liable to violate ZSTD_compressBound(), which we must not do. Whenever the super block violates the block bound we instead emit an uncompressed block. This means we increase the latency because of the single uncompressed block. I fix this by enabling streaming an uncompressed block, so the latency of an uncompressed block is 1 byte. This doesn't reduce the latency of the buffer-less API, but I don't think we really care. * I added a test case that verifies that the decompression has 1 byte latency. * I rely on existing zstreamtest / fuzzer / libfuzzer regression tests for correctness. During development I had several correctness bugs, and they easily caught them. * The added assert that the superblock doesn't violate the block bound will help us discover any missed conditions (though I think I got them all). Credit to OSS-Fuzz.
This commit is contained in:
@@ -658,7 +658,7 @@ static size_t ZSTD_compressSubBlock_multi(const seqStore_t* seqStorePtr,
|
||||
/* I think there is an optimization opportunity here.
|
||||
* Calling ZSTD_estimateSubBlockSize for every sequence can be wasteful
|
||||
* since it recalculates estimate from scratch.
|
||||
* For example, it would recount literal distribution and symbol codes everytime.
|
||||
* For example, it would recount literal distribution and symbol codes everytime.
|
||||
*/
|
||||
cBlockSizeEstimate = ZSTD_estimateSubBlockSize(lp, litSize, ofCodePtr, llCodePtr, mlCodePtr, seqCount,
|
||||
entropy, entropyMetadata,
|
||||
@@ -716,27 +716,3 @@ size_t ZSTD_compressSuperBlock(ZSTD_CCtx* zc,
|
||||
zc->bmi2, lastBlock,
|
||||
zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */);
|
||||
}
|
||||
|
||||
size_t ZSTD_noCompressSuperBlock(void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize,
|
||||
size_t targetCBlockSize,
|
||||
unsigned lastBlock) {
|
||||
const BYTE* const istart = (const BYTE*)src;
|
||||
const BYTE* const iend = istart + srcSize;
|
||||
const BYTE* ip = istart;
|
||||
BYTE* const ostart = (BYTE*)dst;
|
||||
BYTE* const oend = ostart + dstCapacity;
|
||||
BYTE* op = ostart;
|
||||
DEBUGLOG(5, "ZSTD_noCompressSuperBlock (dstCapacity=%zu, srcSize=%zu, targetCBlockSize=%zu)",
|
||||
dstCapacity, srcSize, targetCBlockSize);
|
||||
while (ip < iend) {
|
||||
size_t remaining = iend-ip;
|
||||
unsigned lastSubBlock = remaining <= targetCBlockSize;
|
||||
size_t blockSize = lastSubBlock ? remaining : targetCBlockSize;
|
||||
size_t cSize = ZSTD_noCompressBlock(op, oend-op, ip, blockSize, lastSubBlock && lastBlock);
|
||||
FORWARD_IF_ERROR(cSize);
|
||||
ip += blockSize;
|
||||
op += cSize;
|
||||
}
|
||||
return op-ostart;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user