1 1.1 christos /* 2 1.1 christos * Copyright (c) Meta Platforms, Inc. and affiliates. 3 1.1 christos * All rights reserved. 4 1.1 christos * 5 1.1 christos * This source code is licensed under both the BSD-style license (found in the 6 1.1 christos * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 1.1 christos * in the COPYING file in the root directory of this source tree). 8 1.1 christos * You may select, at your option, one of the above-listed licenses. 9 1.1 christos */ 10 1.1 christos 11 1.1 christos /*-************************************* 12 1.1 christos * Dependencies 13 1.1 christos ***************************************/ 14 1.1 christos #include "zstd_compress_superblock.h" 15 1.1 christos 16 1.1 christos #include "../common/zstd_internal.h" /* ZSTD_getSequenceLength */ 17 1.1 christos #include "hist.h" /* HIST_countFast_wksp */ 18 1.1 christos #include "zstd_compress_internal.h" /* ZSTD_[huf|fse|entropy]CTablesMetadata_t */ 19 1.1 christos #include "zstd_compress_sequences.h" 20 1.1 christos #include "zstd_compress_literals.h" 21 1.1 christos 22 1.1 christos /** ZSTD_compressSubBlock_literal() : 23 1.1 christos * Compresses literals section for a sub-block. 24 1.1 christos * When we have to write the Huffman table we will sometimes choose a header 25 1.1 christos * size larger than necessary. This is because we have to pick the header size 26 1.1 christos * before we know the table size + compressed size, so we have a bound on the 27 1.1 christos * table size. If we guessed incorrectly, we fall back to uncompressed literals. 28 1.1 christos * 29 1.1 christos * We write the header when writeEntropy=1 and set entropyWritten=1 when we succeeded 30 1.1 christos * in writing the header, otherwise it is set to 0. 31 1.1 christos * 32 1.1 christos * hufMetadata->hType has literals block type info. 33 1.1 christos * If it is set_basic, all sub-blocks literals section will be Raw_Literals_Block. 34 1.1 christos * If it is set_rle, all sub-blocks literals section will be RLE_Literals_Block. 35 1.1 christos * If it is set_compressed, first sub-block's literals section will be Compressed_Literals_Block 36 1.1 christos * If it is set_compressed, first sub-block's literals section will be Treeless_Literals_Block 37 1.1 christos * and the following sub-blocks' literals sections will be Treeless_Literals_Block. 38 1.1 christos * @return : compressed size of literals section of a sub-block 39 1.1 christos * Or 0 if unable to compress. 40 1.1 christos * Or error code */ 41 1.1 christos static size_t 42 1.1 christos ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, 43 1.1 christos const ZSTD_hufCTablesMetadata_t* hufMetadata, 44 1.1 christos const BYTE* literals, size_t litSize, 45 1.1 christos void* dst, size_t dstSize, 46 1.1 christos const int bmi2, int writeEntropy, int* entropyWritten) 47 1.1 christos { 48 1.1 christos size_t const header = writeEntropy ? 200 : 0; 49 1.1 christos size_t const lhSize = 3 + (litSize >= (1 KB - header)) + (litSize >= (16 KB - header)); 50 1.1 christos BYTE* const ostart = (BYTE*)dst; 51 1.1 christos BYTE* const oend = ostart + dstSize; 52 1.1 christos BYTE* op = ostart + lhSize; 53 1.1 christos U32 const singleStream = lhSize == 3; 54 1.1 christos symbolEncodingType_e hType = writeEntropy ? hufMetadata->hType : set_repeat; 55 1.1 christos size_t cLitSize = 0; 56 1.1 christos 57 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_literal (litSize=%zu, lhSize=%zu, writeEntropy=%d)", litSize, lhSize, writeEntropy); 58 1.1 christos 59 1.1 christos *entropyWritten = 0; 60 1.1 christos if (litSize == 0 || hufMetadata->hType == set_basic) { 61 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_literal using raw literal"); 62 1.1 christos return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); 63 1.1 christos } else if (hufMetadata->hType == set_rle) { 64 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_literal using rle literal"); 65 1.1 christos return ZSTD_compressRleLiteralsBlock(dst, dstSize, literals, litSize); 66 1.1 christos } 67 1.1 christos 68 1.1 christos assert(litSize > 0); 69 1.1 christos assert(hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat); 70 1.1 christos 71 1.1 christos if (writeEntropy && hufMetadata->hType == set_compressed) { 72 1.1 christos ZSTD_memcpy(op, hufMetadata->hufDesBuffer, hufMetadata->hufDesSize); 73 1.1 christos op += hufMetadata->hufDesSize; 74 1.1 christos cLitSize += hufMetadata->hufDesSize; 75 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_literal (hSize=%zu)", hufMetadata->hufDesSize); 76 1.1 christos } 77 1.1 christos 78 1.1 christos { int const flags = bmi2 ? HUF_flags_bmi2 : 0; 79 1.1 christos const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, (size_t)(oend-op), literals, litSize, hufTable, flags) 80 1.1 christos : HUF_compress4X_usingCTable(op, (size_t)(oend-op), literals, litSize, hufTable, flags); 81 1.1 christos op += cSize; 82 1.1 christos cLitSize += cSize; 83 1.1 christos if (cSize == 0 || ERR_isError(cSize)) { 84 1.1 christos DEBUGLOG(5, "Failed to write entropy tables %s", ZSTD_getErrorName(cSize)); 85 1.1 christos return 0; 86 1.1 christos } 87 1.1 christos /* If we expand and we aren't writing a header then emit uncompressed */ 88 1.1 christos if (!writeEntropy && cLitSize >= litSize) { 89 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_literal using raw literal because uncompressible"); 90 1.1 christos return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); 91 1.1 christos } 92 1.1 christos /* If we are writing headers then allow expansion that doesn't change our header size. */ 93 1.1 christos if (lhSize < (size_t)(3 + (cLitSize >= 1 KB) + (cLitSize >= 16 KB))) { 94 1.1 christos assert(cLitSize > litSize); 95 1.1 christos DEBUGLOG(5, "Literals expanded beyond allowed header size"); 96 1.1 christos return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); 97 1.1 christos } 98 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_literal (cSize=%zu)", cSize); 99 1.1 christos } 100 1.1 christos 101 1.1 christos /* Build header */ 102 1.1 christos switch(lhSize) 103 1.1 christos { 104 1.1 christos case 3: /* 2 - 2 - 10 - 10 */ 105 1.1 christos { U32 const lhc = hType + ((U32)(!singleStream) << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<14); 106 1.1 christos MEM_writeLE24(ostart, lhc); 107 1.1 christos break; 108 1.1 christos } 109 1.1 christos case 4: /* 2 - 2 - 14 - 14 */ 110 1.1 christos { U32 const lhc = hType + (2 << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<18); 111 1.1 christos MEM_writeLE32(ostart, lhc); 112 1.1 christos break; 113 1.1 christos } 114 1.1 christos case 5: /* 2 - 2 - 18 - 18 */ 115 1.1 christos { U32 const lhc = hType + (3 << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<22); 116 1.1 christos MEM_writeLE32(ostart, lhc); 117 1.1 christos ostart[4] = (BYTE)(cLitSize >> 10); 118 1.1 christos break; 119 1.1 christos } 120 1.1 christos default: /* not possible : lhSize is {3,4,5} */ 121 1.1 christos assert(0); 122 1.1 christos } 123 1.1 christos *entropyWritten = 1; 124 1.1 christos DEBUGLOG(5, "Compressed literals: %u -> %u", (U32)litSize, (U32)(op-ostart)); 125 1.1 christos return (size_t)(op-ostart); 126 1.1 christos } 127 1.1 christos 128 1.1 christos static size_t 129 1.1 christos ZSTD_seqDecompressedSize(seqStore_t const* seqStore, 130 1.1 christos const seqDef* sequences, size_t nbSeqs, 131 1.1 christos size_t litSize, int lastSubBlock) 132 1.1 christos { 133 1.1 christos size_t matchLengthSum = 0; 134 1.1 christos size_t litLengthSum = 0; 135 1.1 christos size_t n; 136 1.1 christos for (n=0; n<nbSeqs; n++) { 137 1.1 christos const ZSTD_sequenceLength seqLen = ZSTD_getSequenceLength(seqStore, sequences+n); 138 1.1 christos litLengthSum += seqLen.litLength; 139 1.1 christos matchLengthSum += seqLen.matchLength; 140 1.1 christos } 141 1.1 christos DEBUGLOG(5, "ZSTD_seqDecompressedSize: %u sequences from %p: %u literals + %u matchlength", 142 1.1 christos (unsigned)nbSeqs, (const void*)sequences, 143 1.1 christos (unsigned)litLengthSum, (unsigned)matchLengthSum); 144 1.1 christos if (!lastSubBlock) 145 1.1 christos assert(litLengthSum == litSize); 146 1.1 christos else 147 1.1 christos assert(litLengthSum <= litSize); 148 1.1 christos (void)litLengthSum; 149 1.1 christos return matchLengthSum + litSize; 150 1.1 christos } 151 1.1 christos 152 1.1 christos /** ZSTD_compressSubBlock_sequences() : 153 1.1 christos * Compresses sequences section for a sub-block. 154 1.1 christos * fseMetadata->llType, fseMetadata->ofType, and fseMetadata->mlType have 155 1.1 christos * symbol compression modes for the super-block. 156 1.1 christos * The first successfully compressed block will have these in its header. 157 1.1 christos * We set entropyWritten=1 when we succeed in compressing the sequences. 158 1.1 christos * The following sub-blocks will always have repeat mode. 159 1.1 christos * @return : compressed size of sequences section of a sub-block 160 1.1 christos * Or 0 if it is unable to compress 161 1.1 christos * Or error code. */ 162 1.1 christos static size_t 163 1.1 christos ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables, 164 1.1 christos const ZSTD_fseCTablesMetadata_t* fseMetadata, 165 1.1 christos const seqDef* sequences, size_t nbSeq, 166 1.1 christos const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, 167 1.1 christos const ZSTD_CCtx_params* cctxParams, 168 1.1 christos void* dst, size_t dstCapacity, 169 1.1 christos const int bmi2, int writeEntropy, int* entropyWritten) 170 1.1 christos { 171 1.1 christos const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; 172 1.1 christos BYTE* const ostart = (BYTE*)dst; 173 1.1 christos BYTE* const oend = ostart + dstCapacity; 174 1.1 christos BYTE* op = ostart; 175 1.1 christos BYTE* seqHead; 176 1.1 christos 177 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (nbSeq=%zu, writeEntropy=%d, longOffsets=%d)", nbSeq, writeEntropy, longOffsets); 178 1.1 christos 179 1.1 christos *entropyWritten = 0; 180 1.1 christos /* Sequences Header */ 181 1.1 christos RETURN_ERROR_IF((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/, 182 1.1 christos dstSize_tooSmall, ""); 183 1.1 christos if (nbSeq < 128) 184 1.1 christos *op++ = (BYTE)nbSeq; 185 1.1 christos else if (nbSeq < LONGNBSEQ) 186 1.1 christos op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2; 187 1.1 christos else 188 1.1 christos op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; 189 1.1 christos if (nbSeq==0) { 190 1.1 christos return (size_t)(op - ostart); 191 1.1 christos } 192 1.1 christos 193 1.1 christos /* seqHead : flags for FSE encoding type */ 194 1.1 christos seqHead = op++; 195 1.1 christos 196 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (seqHeadSize=%u)", (unsigned)(op-ostart)); 197 1.1 christos 198 1.1 christos if (writeEntropy) { 199 1.1 christos const U32 LLtype = fseMetadata->llType; 200 1.1 christos const U32 Offtype = fseMetadata->ofType; 201 1.1 christos const U32 MLtype = fseMetadata->mlType; 202 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (fseTablesSize=%zu)", fseMetadata->fseTablesSize); 203 1.1 christos *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); 204 1.1 christos ZSTD_memcpy(op, fseMetadata->fseTablesBuffer, fseMetadata->fseTablesSize); 205 1.1 christos op += fseMetadata->fseTablesSize; 206 1.1 christos } else { 207 1.1 christos const U32 repeat = set_repeat; 208 1.1 christos *seqHead = (BYTE)((repeat<<6) + (repeat<<4) + (repeat<<2)); 209 1.1 christos } 210 1.1 christos 211 1.1 christos { size_t const bitstreamSize = ZSTD_encodeSequences( 212 1.1 christos op, (size_t)(oend - op), 213 1.1 christos fseTables->matchlengthCTable, mlCode, 214 1.1 christos fseTables->offcodeCTable, ofCode, 215 1.1 christos fseTables->litlengthCTable, llCode, 216 1.1 christos sequences, nbSeq, 217 1.1 christos longOffsets, bmi2); 218 1.1 christos FORWARD_IF_ERROR(bitstreamSize, "ZSTD_encodeSequences failed"); 219 1.1 christos op += bitstreamSize; 220 1.1 christos /* zstd versions <= 1.3.4 mistakenly report corruption when 221 1.1 christos * FSE_readNCount() receives a buffer < 4 bytes. 222 1.1 christos * Fixed by https://github.com/facebook/zstd/pull/1146. 223 1.1 christos * This can happen when the last set_compressed table present is 2 224 1.1 christos * bytes and the bitstream is only one byte. 225 1.1 christos * In this exceedingly rare case, we will simply emit an uncompressed 226 1.1 christos * block, since it isn't worth optimizing. 227 1.1 christos */ 228 1.1 christos #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 229 1.1 christos if (writeEntropy && fseMetadata->lastCountSize && fseMetadata->lastCountSize + bitstreamSize < 4) { 230 1.1 christos /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */ 231 1.1 christos assert(fseMetadata->lastCountSize + bitstreamSize == 3); 232 1.1 christos DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by " 233 1.1 christos "emitting an uncompressed block."); 234 1.1 christos return 0; 235 1.1 christos } 236 1.1 christos #endif 237 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (bitstreamSize=%zu)", bitstreamSize); 238 1.1 christos } 239 1.1 christos 240 1.1 christos /* zstd versions <= 1.4.0 mistakenly report error when 241 1.1 christos * sequences section body size is less than 3 bytes. 242 1.1 christos * Fixed by https://github.com/facebook/zstd/pull/1664. 243 1.1 christos * This can happen when the previous sequences section block is compressed 244 1.1 christos * with rle mode and the current block's sequences section is compressed 245 1.1 christos * with repeat mode where sequences section body size can be 1 byte. 246 1.1 christos */ 247 1.1 christos #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 248 1.1 christos if (op-seqHead < 4) { 249 1.1 christos DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.4.0 by emitting " 250 1.1 christos "an uncompressed block when sequences are < 4 bytes"); 251 1.1 christos return 0; 252 1.1 christos } 253 1.1 christos #endif 254 1.1 christos 255 1.1 christos *entropyWritten = 1; 256 1.1 christos return (size_t)(op - ostart); 257 1.1 christos } 258 1.1 christos 259 1.1 christos /** ZSTD_compressSubBlock() : 260 1.1 christos * Compresses a single sub-block. 261 1.1 christos * @return : compressed size of the sub-block 262 1.1 christos * Or 0 if it failed to compress. */ 263 1.1 christos static size_t ZSTD_compressSubBlock(const ZSTD_entropyCTables_t* entropy, 264 1.1 christos const ZSTD_entropyCTablesMetadata_t* entropyMetadata, 265 1.1 christos const seqDef* sequences, size_t nbSeq, 266 1.1 christos const BYTE* literals, size_t litSize, 267 1.1 christos const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, 268 1.1 christos const ZSTD_CCtx_params* cctxParams, 269 1.1 christos void* dst, size_t dstCapacity, 270 1.1 christos const int bmi2, 271 1.1 christos int writeLitEntropy, int writeSeqEntropy, 272 1.1 christos int* litEntropyWritten, int* seqEntropyWritten, 273 1.1 christos U32 lastBlock) 274 1.1 christos { 275 1.1 christos BYTE* const ostart = (BYTE*)dst; 276 1.1 christos BYTE* const oend = ostart + dstCapacity; 277 1.1 christos BYTE* op = ostart + ZSTD_blockHeaderSize; 278 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock (litSize=%zu, nbSeq=%zu, writeLitEntropy=%d, writeSeqEntropy=%d, lastBlock=%d)", 279 1.1 christos litSize, nbSeq, writeLitEntropy, writeSeqEntropy, lastBlock); 280 1.1 christos { size_t cLitSize = ZSTD_compressSubBlock_literal((const HUF_CElt*)entropy->huf.CTable, 281 1.1 christos &entropyMetadata->hufMetadata, literals, litSize, 282 1.1 christos op, (size_t)(oend-op), 283 1.1 christos bmi2, writeLitEntropy, litEntropyWritten); 284 1.1 christos FORWARD_IF_ERROR(cLitSize, "ZSTD_compressSubBlock_literal failed"); 285 1.1 christos if (cLitSize == 0) return 0; 286 1.1 christos op += cLitSize; 287 1.1 christos } 288 1.1 christos { size_t cSeqSize = ZSTD_compressSubBlock_sequences(&entropy->fse, 289 1.1 christos &entropyMetadata->fseMetadata, 290 1.1 christos sequences, nbSeq, 291 1.1 christos llCode, mlCode, ofCode, 292 1.1 christos cctxParams, 293 1.1 christos op, (size_t)(oend-op), 294 1.1 christos bmi2, writeSeqEntropy, seqEntropyWritten); 295 1.1 christos FORWARD_IF_ERROR(cSeqSize, "ZSTD_compressSubBlock_sequences failed"); 296 1.1 christos if (cSeqSize == 0) return 0; 297 1.1 christos op += cSeqSize; 298 1.1 christos } 299 1.1 christos /* Write block header */ 300 1.1 christos { size_t cSize = (size_t)(op-ostart) - ZSTD_blockHeaderSize; 301 1.1 christos U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); 302 1.1 christos MEM_writeLE24(ostart, cBlockHeader24); 303 1.1 christos } 304 1.1 christos return (size_t)(op-ostart); 305 1.1 christos } 306 1.1 christos 307 1.1 christos static size_t ZSTD_estimateSubBlockSize_literal(const BYTE* literals, size_t litSize, 308 1.1 christos const ZSTD_hufCTables_t* huf, 309 1.1 christos const ZSTD_hufCTablesMetadata_t* hufMetadata, 310 1.1 christos void* workspace, size_t wkspSize, 311 1.1 christos int writeEntropy) 312 1.1 christos { 313 1.1 christos unsigned* const countWksp = (unsigned*)workspace; 314 1.1 christos unsigned maxSymbolValue = 255; 315 1.1 christos size_t literalSectionHeaderSize = 3; /* Use hard coded size of 3 bytes */ 316 1.1 christos 317 1.1 christos if (hufMetadata->hType == set_basic) return litSize; 318 1.1 christos else if (hufMetadata->hType == set_rle) return 1; 319 1.1 christos else if (hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat) { 320 1.1 christos size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)literals, litSize, workspace, wkspSize); 321 1.1 christos if (ZSTD_isError(largest)) return litSize; 322 1.1 christos { size_t cLitSizeEstimate = HUF_estimateCompressedSize((const HUF_CElt*)huf->CTable, countWksp, maxSymbolValue); 323 1.1 christos if (writeEntropy) cLitSizeEstimate += hufMetadata->hufDesSize; 324 1.1 christos return cLitSizeEstimate + literalSectionHeaderSize; 325 1.1 christos } } 326 1.1 christos assert(0); /* impossible */ 327 1.1 christos return 0; 328 1.1 christos } 329 1.1 christos 330 1.1 christos static size_t ZSTD_estimateSubBlockSize_symbolType(symbolEncodingType_e type, 331 1.1 christos const BYTE* codeTable, unsigned maxCode, 332 1.1 christos size_t nbSeq, const FSE_CTable* fseCTable, 333 1.1 christos const U8* additionalBits, 334 1.1 christos short const* defaultNorm, U32 defaultNormLog, U32 defaultMax, 335 1.1 christos void* workspace, size_t wkspSize) 336 1.1 christos { 337 1.1 christos unsigned* const countWksp = (unsigned*)workspace; 338 1.1 christos const BYTE* ctp = codeTable; 339 1.1 christos const BYTE* const ctStart = ctp; 340 1.1 christos const BYTE* const ctEnd = ctStart + nbSeq; 341 1.1 christos size_t cSymbolTypeSizeEstimateInBits = 0; 342 1.1 christos unsigned max = maxCode; 343 1.1 christos 344 1.1 christos HIST_countFast_wksp(countWksp, &max, codeTable, nbSeq, workspace, wkspSize); /* can't fail */ 345 1.1 christos if (type == set_basic) { 346 1.1 christos /* We selected this encoding type, so it must be valid. */ 347 1.1 christos assert(max <= defaultMax); 348 1.1 christos cSymbolTypeSizeEstimateInBits = max <= defaultMax 349 1.1 christos ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, countWksp, max) 350 1.1 christos : ERROR(GENERIC); 351 1.1 christos } else if (type == set_rle) { 352 1.1 christos cSymbolTypeSizeEstimateInBits = 0; 353 1.1 christos } else if (type == set_compressed || type == set_repeat) { 354 1.1 christos cSymbolTypeSizeEstimateInBits = ZSTD_fseBitCost(fseCTable, countWksp, max); 355 1.1 christos } 356 1.1 christos if (ZSTD_isError(cSymbolTypeSizeEstimateInBits)) return nbSeq * 10; 357 1.1 christos while (ctp < ctEnd) { 358 1.1 christos if (additionalBits) cSymbolTypeSizeEstimateInBits += additionalBits[*ctp]; 359 1.1 christos else cSymbolTypeSizeEstimateInBits += *ctp; /* for offset, offset code is also the number of additional bits */ 360 1.1 christos ctp++; 361 1.1 christos } 362 1.1 christos return cSymbolTypeSizeEstimateInBits / 8; 363 1.1 christos } 364 1.1 christos 365 1.1 christos static size_t ZSTD_estimateSubBlockSize_sequences(const BYTE* ofCodeTable, 366 1.1 christos const BYTE* llCodeTable, 367 1.1 christos const BYTE* mlCodeTable, 368 1.1 christos size_t nbSeq, 369 1.1 christos const ZSTD_fseCTables_t* fseTables, 370 1.1 christos const ZSTD_fseCTablesMetadata_t* fseMetadata, 371 1.1 christos void* workspace, size_t wkspSize, 372 1.1 christos int writeEntropy) 373 1.1 christos { 374 1.1 christos size_t const sequencesSectionHeaderSize = 3; /* Use hard coded size of 3 bytes */ 375 1.1 christos size_t cSeqSizeEstimate = 0; 376 1.1 christos if (nbSeq == 0) return sequencesSectionHeaderSize; 377 1.1 christos cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, MaxOff, 378 1.1 christos nbSeq, fseTables->offcodeCTable, NULL, 379 1.1 christos OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, 380 1.1 christos workspace, wkspSize); 381 1.1 christos cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->llType, llCodeTable, MaxLL, 382 1.1 christos nbSeq, fseTables->litlengthCTable, LL_bits, 383 1.1 christos LL_defaultNorm, LL_defaultNormLog, MaxLL, 384 1.1 christos workspace, wkspSize); 385 1.1 christos cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, MaxML, 386 1.1 christos nbSeq, fseTables->matchlengthCTable, ML_bits, 387 1.1 christos ML_defaultNorm, ML_defaultNormLog, MaxML, 388 1.1 christos workspace, wkspSize); 389 1.1 christos if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize; 390 1.1 christos return cSeqSizeEstimate + sequencesSectionHeaderSize; 391 1.1 christos } 392 1.1 christos 393 1.1 christos typedef struct { 394 1.1 christos size_t estLitSize; 395 1.1 christos size_t estBlockSize; 396 1.1 christos } EstimatedBlockSize; 397 1.1 christos static EstimatedBlockSize ZSTD_estimateSubBlockSize(const BYTE* literals, size_t litSize, 398 1.1 christos const BYTE* ofCodeTable, 399 1.1 christos const BYTE* llCodeTable, 400 1.1 christos const BYTE* mlCodeTable, 401 1.1 christos size_t nbSeq, 402 1.1 christos const ZSTD_entropyCTables_t* entropy, 403 1.1 christos const ZSTD_entropyCTablesMetadata_t* entropyMetadata, 404 1.1 christos void* workspace, size_t wkspSize, 405 1.1 christos int writeLitEntropy, int writeSeqEntropy) 406 1.1 christos { 407 1.1 christos EstimatedBlockSize ebs; 408 1.1 christos ebs.estLitSize = ZSTD_estimateSubBlockSize_literal(literals, litSize, 409 1.1 christos &entropy->huf, &entropyMetadata->hufMetadata, 410 1.1 christos workspace, wkspSize, writeLitEntropy); 411 1.1 christos ebs.estBlockSize = ZSTD_estimateSubBlockSize_sequences(ofCodeTable, llCodeTable, mlCodeTable, 412 1.1 christos nbSeq, &entropy->fse, &entropyMetadata->fseMetadata, 413 1.1 christos workspace, wkspSize, writeSeqEntropy); 414 1.1 christos ebs.estBlockSize += ebs.estLitSize + ZSTD_blockHeaderSize; 415 1.1 christos return ebs; 416 1.1 christos } 417 1.1 christos 418 1.1 christos static int ZSTD_needSequenceEntropyTables(ZSTD_fseCTablesMetadata_t const* fseMetadata) 419 1.1 christos { 420 1.1 christos if (fseMetadata->llType == set_compressed || fseMetadata->llType == set_rle) 421 1.1 christos return 1; 422 1.1 christos if (fseMetadata->mlType == set_compressed || fseMetadata->mlType == set_rle) 423 1.1 christos return 1; 424 1.1 christos if (fseMetadata->ofType == set_compressed || fseMetadata->ofType == set_rle) 425 1.1 christos return 1; 426 1.1 christos return 0; 427 1.1 christos } 428 1.1 christos 429 1.1 christos static size_t countLiterals(seqStore_t const* seqStore, const seqDef* sp, size_t seqCount) 430 1.1 christos { 431 1.1 christos size_t n, total = 0; 432 1.1 christos assert(sp != NULL); 433 1.1 christos for (n=0; n<seqCount; n++) { 434 1.1 christos total += ZSTD_getSequenceLength(seqStore, sp+n).litLength; 435 1.1 christos } 436 1.1 christos DEBUGLOG(6, "countLiterals for %zu sequences from %p => %zu bytes", seqCount, (const void*)sp, total); 437 1.1 christos return total; 438 1.1 christos } 439 1.1 christos 440 1.1 christos #define BYTESCALE 256 441 1.1 christos 442 1.1 christos static size_t sizeBlockSequences(const seqDef* sp, size_t nbSeqs, 443 1.1 christos size_t targetBudget, size_t avgLitCost, size_t avgSeqCost, 444 1.1 christos int firstSubBlock) 445 1.1 christos { 446 1.1 christos size_t n, budget = 0, inSize=0; 447 1.1 christos /* entropy headers */ 448 1.1 christos size_t const headerSize = (size_t)firstSubBlock * 120 * BYTESCALE; /* generous estimate */ 449 1.1 christos assert(firstSubBlock==0 || firstSubBlock==1); 450 1.1 christos budget += headerSize; 451 1.1 christos 452 1.1 christos /* first sequence => at least one sequence*/ 453 1.1 christos budget += sp[0].litLength * avgLitCost + avgSeqCost; 454 1.1 christos if (budget > targetBudget) return 1; 455 1.1 christos inSize = sp[0].litLength + (sp[0].mlBase+MINMATCH); 456 1.1 christos 457 1.1 christos /* loop over sequences */ 458 1.1 christos for (n=1; n<nbSeqs; n++) { 459 1.1 christos size_t currentCost = sp[n].litLength * avgLitCost + avgSeqCost; 460 1.1 christos budget += currentCost; 461 1.1 christos inSize += sp[n].litLength + (sp[n].mlBase+MINMATCH); 462 1.1 christos /* stop when sub-block budget is reached */ 463 1.1 christos if ( (budget > targetBudget) 464 1.1 christos /* though continue to expand until the sub-block is deemed compressible */ 465 1.1 christos && (budget < inSize * BYTESCALE) ) 466 1.1 christos break; 467 1.1 christos } 468 1.1 christos 469 1.1 christos return n; 470 1.1 christos } 471 1.1 christos 472 1.1 christos /** ZSTD_compressSubBlock_multi() : 473 1.1 christos * Breaks super-block into multiple sub-blocks and compresses them. 474 1.1 christos * Entropy will be written into the first block. 475 1.1 christos * The following blocks use repeat_mode to compress. 476 1.1 christos * Sub-blocks are all compressed, except the last one when beneficial. 477 1.1 christos * @return : compressed size of the super block (which features multiple ZSTD blocks) 478 1.1 christos * or 0 if it failed to compress. */ 479 1.1 christos static size_t ZSTD_compressSubBlock_multi(const seqStore_t* seqStorePtr, 480 1.1 christos const ZSTD_compressedBlockState_t* prevCBlock, 481 1.1 christos ZSTD_compressedBlockState_t* nextCBlock, 482 1.1 christos const ZSTD_entropyCTablesMetadata_t* entropyMetadata, 483 1.1 christos const ZSTD_CCtx_params* cctxParams, 484 1.1 christos void* dst, size_t dstCapacity, 485 1.1 christos const void* src, size_t srcSize, 486 1.1 christos const int bmi2, U32 lastBlock, 487 1.1 christos void* workspace, size_t wkspSize) 488 1.1 christos { 489 1.1 christos const seqDef* const sstart = seqStorePtr->sequencesStart; 490 1.1 christos const seqDef* const send = seqStorePtr->sequences; 491 1.1 christos const seqDef* sp = sstart; /* tracks progresses within seqStorePtr->sequences */ 492 1.1 christos size_t const nbSeqs = (size_t)(send - sstart); 493 1.1 christos const BYTE* const lstart = seqStorePtr->litStart; 494 1.1 christos const BYTE* const lend = seqStorePtr->lit; 495 1.1 christos const BYTE* lp = lstart; 496 1.1 christos size_t const nbLiterals = (size_t)(lend - lstart); 497 1.1 christos BYTE const* ip = (BYTE const*)src; 498 1.1 christos BYTE const* const iend = ip + srcSize; 499 1.1 christos BYTE* const ostart = (BYTE*)dst; 500 1.1 christos BYTE* const oend = ostart + dstCapacity; 501 1.1 christos BYTE* op = ostart; 502 1.1 christos const BYTE* llCodePtr = seqStorePtr->llCode; 503 1.1 christos const BYTE* mlCodePtr = seqStorePtr->mlCode; 504 1.1 christos const BYTE* ofCodePtr = seqStorePtr->ofCode; 505 1.1 christos size_t const minTarget = ZSTD_TARGETCBLOCKSIZE_MIN; /* enforce minimum size, to reduce undesirable side effects */ 506 1.1 christos size_t const targetCBlockSize = MAX(minTarget, cctxParams->targetCBlockSize); 507 1.1 christos int writeLitEntropy = (entropyMetadata->hufMetadata.hType == set_compressed); 508 1.1 christos int writeSeqEntropy = 1; 509 1.1 christos 510 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_multi (srcSize=%u, litSize=%u, nbSeq=%u)", 511 1.1 christos (unsigned)srcSize, (unsigned)(lend-lstart), (unsigned)(send-sstart)); 512 1.1 christos 513 1.1 christos /* let's start by a general estimation for the full block */ 514 1.1 christos if (nbSeqs > 0) { 515 1.1 christos EstimatedBlockSize const ebs = 516 1.1 christos ZSTD_estimateSubBlockSize(lp, nbLiterals, 517 1.1 christos ofCodePtr, llCodePtr, mlCodePtr, nbSeqs, 518 1.1 christos &nextCBlock->entropy, entropyMetadata, 519 1.1 christos workspace, wkspSize, 520 1.1 christos writeLitEntropy, writeSeqEntropy); 521 1.1 christos /* quick estimation */ 522 1.1 christos size_t const avgLitCost = nbLiterals ? (ebs.estLitSize * BYTESCALE) / nbLiterals : BYTESCALE; 523 1.1 christos size_t const avgSeqCost = ((ebs.estBlockSize - ebs.estLitSize) * BYTESCALE) / nbSeqs; 524 1.1 christos const size_t nbSubBlocks = MAX((ebs.estBlockSize + (targetCBlockSize/2)) / targetCBlockSize, 1); 525 1.1 christos size_t n, avgBlockBudget, blockBudgetSupp=0; 526 1.1 christos avgBlockBudget = (ebs.estBlockSize * BYTESCALE) / nbSubBlocks; 527 1.1 christos DEBUGLOG(5, "estimated fullblock size=%u bytes ; avgLitCost=%.2f ; avgSeqCost=%.2f ; targetCBlockSize=%u, nbSubBlocks=%u ; avgBlockBudget=%.0f bytes", 528 1.1 christos (unsigned)ebs.estBlockSize, (double)avgLitCost/BYTESCALE, (double)avgSeqCost/BYTESCALE, 529 1.1 christos (unsigned)targetCBlockSize, (unsigned)nbSubBlocks, (double)avgBlockBudget/BYTESCALE); 530 1.1 christos /* simplification: if estimates states that the full superblock doesn't compress, just bail out immediately 531 1.1 christos * this will result in the production of a single uncompressed block covering @srcSize.*/ 532 1.1 christos if (ebs.estBlockSize > srcSize) return 0; 533 1.1 christos 534 1.1 christos /* compress and write sub-blocks */ 535 1.1 christos assert(nbSubBlocks>0); 536 1.1 christos for (n=0; n < nbSubBlocks-1; n++) { 537 1.1 christos /* determine nb of sequences for current sub-block + nbLiterals from next sequence */ 538 1.1 christos size_t const seqCount = sizeBlockSequences(sp, (size_t)(send-sp), 539 1.1 christos avgBlockBudget + blockBudgetSupp, avgLitCost, avgSeqCost, n==0); 540 1.1 christos /* if reached last sequence : break to last sub-block (simplification) */ 541 1.1 christos assert(seqCount <= (size_t)(send-sp)); 542 1.1 christos if (sp + seqCount == send) break; 543 1.1 christos assert(seqCount > 0); 544 1.1 christos /* compress sub-block */ 545 1.1 christos { int litEntropyWritten = 0; 546 1.1 christos int seqEntropyWritten = 0; 547 1.1 christos size_t litSize = countLiterals(seqStorePtr, sp, seqCount); 548 1.1 christos const size_t decompressedSize = 549 1.1 christos ZSTD_seqDecompressedSize(seqStorePtr, sp, seqCount, litSize, 0); 550 1.1 christos size_t const cSize = ZSTD_compressSubBlock(&nextCBlock->entropy, entropyMetadata, 551 1.1 christos sp, seqCount, 552 1.1 christos lp, litSize, 553 1.1 christos llCodePtr, mlCodePtr, ofCodePtr, 554 1.1 christos cctxParams, 555 1.1 christos op, (size_t)(oend-op), 556 1.1 christos bmi2, writeLitEntropy, writeSeqEntropy, 557 1.1 christos &litEntropyWritten, &seqEntropyWritten, 558 1.1 christos 0); 559 1.1 christos FORWARD_IF_ERROR(cSize, "ZSTD_compressSubBlock failed"); 560 1.1 christos 561 1.1 christos /* check compressibility, update state components */ 562 1.1 christos if (cSize > 0 && cSize < decompressedSize) { 563 1.1 christos DEBUGLOG(5, "Committed sub-block compressing %u bytes => %u bytes", 564 1.1 christos (unsigned)decompressedSize, (unsigned)cSize); 565 1.1 christos assert(ip + decompressedSize <= iend); 566 1.1 christos ip += decompressedSize; 567 1.1 christos lp += litSize; 568 1.1 christos op += cSize; 569 1.1 christos llCodePtr += seqCount; 570 1.1 christos mlCodePtr += seqCount; 571 1.1 christos ofCodePtr += seqCount; 572 1.1 christos /* Entropy only needs to be written once */ 573 1.1 christos if (litEntropyWritten) { 574 1.1 christos writeLitEntropy = 0; 575 1.1 christos } 576 1.1 christos if (seqEntropyWritten) { 577 1.1 christos writeSeqEntropy = 0; 578 1.1 christos } 579 1.1 christos sp += seqCount; 580 1.1 christos blockBudgetSupp = 0; 581 1.1 christos } } 582 1.1 christos /* otherwise : do not compress yet, coalesce current sub-block with following one */ 583 1.1 christos } 584 1.1 christos } /* if (nbSeqs > 0) */ 585 1.1 christos 586 1.1 christos /* write last block */ 587 1.1 christos DEBUGLOG(5, "Generate last sub-block: %u sequences remaining", (unsigned)(send - sp)); 588 1.1 christos { int litEntropyWritten = 0; 589 1.1 christos int seqEntropyWritten = 0; 590 1.1 christos size_t litSize = (size_t)(lend - lp); 591 1.1 christos size_t seqCount = (size_t)(send - sp); 592 1.1 christos const size_t decompressedSize = 593 1.1 christos ZSTD_seqDecompressedSize(seqStorePtr, sp, seqCount, litSize, 1); 594 1.1 christos size_t const cSize = ZSTD_compressSubBlock(&nextCBlock->entropy, entropyMetadata, 595 1.1 christos sp, seqCount, 596 1.1 christos lp, litSize, 597 1.1 christos llCodePtr, mlCodePtr, ofCodePtr, 598 1.1 christos cctxParams, 599 1.1 christos op, (size_t)(oend-op), 600 1.1 christos bmi2, writeLitEntropy, writeSeqEntropy, 601 1.1 christos &litEntropyWritten, &seqEntropyWritten, 602 1.1 christos lastBlock); 603 1.1 christos FORWARD_IF_ERROR(cSize, "ZSTD_compressSubBlock failed"); 604 1.1 christos 605 1.1 christos /* update pointers, the nb of literals borrowed from next sequence must be preserved */ 606 1.1 christos if (cSize > 0 && cSize < decompressedSize) { 607 1.1 christos DEBUGLOG(5, "Last sub-block compressed %u bytes => %u bytes", 608 1.1 christos (unsigned)decompressedSize, (unsigned)cSize); 609 1.1 christos assert(ip + decompressedSize <= iend); 610 1.1 christos ip += decompressedSize; 611 1.1 christos lp += litSize; 612 1.1 christos op += cSize; 613 1.1 christos llCodePtr += seqCount; 614 1.1 christos mlCodePtr += seqCount; 615 1.1 christos ofCodePtr += seqCount; 616 1.1 christos /* Entropy only needs to be written once */ 617 1.1 christos if (litEntropyWritten) { 618 1.1 christos writeLitEntropy = 0; 619 1.1 christos } 620 1.1 christos if (seqEntropyWritten) { 621 1.1 christos writeSeqEntropy = 0; 622 1.1 christos } 623 1.1 christos sp += seqCount; 624 1.1 christos } 625 1.1 christos } 626 1.1 christos 627 1.1 christos 628 1.1 christos if (writeLitEntropy) { 629 1.1 christos DEBUGLOG(5, "Literal entropy tables were never written"); 630 1.1 christos ZSTD_memcpy(&nextCBlock->entropy.huf, &prevCBlock->entropy.huf, sizeof(prevCBlock->entropy.huf)); 631 1.1 christos } 632 1.1 christos if (writeSeqEntropy && ZSTD_needSequenceEntropyTables(&entropyMetadata->fseMetadata)) { 633 1.1 christos /* If we haven't written our entropy tables, then we've violated our contract and 634 1.1 christos * must emit an uncompressed block. 635 1.1 christos */ 636 1.1 christos DEBUGLOG(5, "Sequence entropy tables were never written => cancel, emit an uncompressed block"); 637 1.1 christos return 0; 638 1.1 christos } 639 1.1 christos 640 1.1 christos if (ip < iend) { 641 1.1 christos /* some data left : last part of the block sent uncompressed */ 642 1.1 christos size_t const rSize = (size_t)((iend - ip)); 643 1.1 christos size_t const cSize = ZSTD_noCompressBlock(op, (size_t)(oend - op), ip, rSize, lastBlock); 644 1.1 christos DEBUGLOG(5, "Generate last uncompressed sub-block of %u bytes", (unsigned)(rSize)); 645 1.1 christos FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed"); 646 1.1 christos assert(cSize != 0); 647 1.1 christos op += cSize; 648 1.1 christos /* We have to regenerate the repcodes because we've skipped some sequences */ 649 1.1 christos if (sp < send) { 650 1.1 christos const seqDef* seq; 651 1.1 christos repcodes_t rep; 652 1.1 christos ZSTD_memcpy(&rep, prevCBlock->rep, sizeof(rep)); 653 1.1 christos for (seq = sstart; seq < sp; ++seq) { 654 1.1 christos ZSTD_updateRep(rep.rep, seq->offBase, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0); 655 1.1 christos } 656 1.1 christos ZSTD_memcpy(nextCBlock->rep, &rep, sizeof(rep)); 657 1.1 christos } 658 1.1 christos } 659 1.1 christos 660 1.1 christos DEBUGLOG(5, "ZSTD_compressSubBlock_multi compressed all subBlocks: total compressed size = %u", 661 1.1 christos (unsigned)(op-ostart)); 662 1.1 christos return (size_t)(op-ostart); 663 1.1 christos } 664 1.1 christos 665 1.1 christos size_t ZSTD_compressSuperBlock(ZSTD_CCtx* zc, 666 1.1 christos void* dst, size_t dstCapacity, 667 1.1 christos const void* src, size_t srcSize, 668 1.1 christos unsigned lastBlock) 669 1.1 christos { 670 1.1 christos ZSTD_entropyCTablesMetadata_t entropyMetadata; 671 1.1 christos 672 1.1 christos FORWARD_IF_ERROR(ZSTD_buildBlockEntropyStats(&zc->seqStore, 673 1.1 christos &zc->blockState.prevCBlock->entropy, 674 1.1 christos &zc->blockState.nextCBlock->entropy, 675 1.1 christos &zc->appliedParams, 676 1.1 christos &entropyMetadata, 677 1.1 christos zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */), ""); 678 1.1 christos 679 1.1 christos return ZSTD_compressSubBlock_multi(&zc->seqStore, 680 1.1 christos zc->blockState.prevCBlock, 681 1.1 christos zc->blockState.nextCBlock, 682 1.1 christos &entropyMetadata, 683 1.1 christos &zc->appliedParams, 684 1.1 christos dst, dstCapacity, 685 1.1 christos src, srcSize, 686 1.1 christos zc->bmi2, lastBlock, 687 1.1 christos zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */); 688 1.1 christos } 689