contrib/python-zstandard/zstd/compress/zbuff_compress.c
changeset 30822 b54a2984cdd4
parent 30821 7005c03f7387
child 30823 806a830e6612
equal deleted inserted replaced
30821:7005c03f7387 30822:b54a2984cdd4
     1 /**
       
     2  * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
       
     3  * All rights reserved.
       
     4  *
       
     5  * This source code is licensed under the BSD-style license found in the
       
     6  * LICENSE file in the root directory of this source tree. An additional grant
       
     7  * of patent rights can be found in the PATENTS file in the same directory.
       
     8  */
       
     9 
       
    10 
       
    11 
       
    12 /* *************************************
       
    13 *  Dependencies
       
    14 ***************************************/
       
    15 #include <stdlib.h>
       
    16 #include "error_private.h"
       
    17 #include "zstd_internal.h"  /* MIN, ZSTD_BLOCKHEADERSIZE, defaultCustomMem */
       
    18 #define ZBUFF_STATIC_LINKING_ONLY
       
    19 #include "zbuff.h"
       
    20 
       
    21 
       
    22 /* *************************************
       
    23 *  Constants
       
    24 ***************************************/
       
    25 static size_t const ZBUFF_endFrameSize = ZSTD_BLOCKHEADERSIZE;
       
    26 
       
    27 
       
    28 /*-***********************************************************
       
    29 *  Streaming compression
       
    30 *
       
    31 *  A ZBUFF_CCtx object is required to track streaming operation.
       
    32 *  Use ZBUFF_createCCtx() and ZBUFF_freeCCtx() to create/release resources.
       
    33 *  Use ZBUFF_compressInit() to start a new compression operation.
       
    34 *  ZBUFF_CCtx objects can be reused multiple times.
       
    35 *
       
    36 *  Use ZBUFF_compressContinue() repetitively to consume your input.
       
    37 *  *srcSizePtr and *dstCapacityPtr can be any size.
       
    38 *  The function will report how many bytes were read or written by modifying *srcSizePtr and *dstCapacityPtr.
       
    39 *  Note that it may not consume the entire input, in which case it's up to the caller to call again the function with remaining input.
       
    40 *  The content of dst will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters or change dst .
       
    41 *  @return : a hint to preferred nb of bytes to use as input for next function call (it's only a hint, to improve latency)
       
    42 *            or an error code, which can be tested using ZBUFF_isError().
       
    43 *
       
    44 *  ZBUFF_compressFlush() can be used to instruct ZBUFF to compress and output whatever remains within its buffer.
       
    45 *  Note that it will not output more than *dstCapacityPtr.
       
    46 *  Therefore, some content might still be left into its internal buffer if dst buffer is too small.
       
    47 *  @return : nb of bytes still present into internal buffer (0 if it's empty)
       
    48 *            or an error code, which can be tested using ZBUFF_isError().
       
    49 *
       
    50 *  ZBUFF_compressEnd() instructs to finish a frame.
       
    51 *  It will perform a flush and write frame epilogue.
       
    52 *  Similar to ZBUFF_compressFlush(), it may not be able to output the entire internal buffer content if *dstCapacityPtr is too small.
       
    53 *  @return : nb of bytes still present into internal buffer (0 if it's empty)
       
    54 *            or an error code, which can be tested using ZBUFF_isError().
       
    55 *
       
    56 *  Hint : recommended buffer sizes (not compulsory)
       
    57 *  input : ZSTD_BLOCKSIZE_MAX (128 KB), internal unit size, it improves latency to use this value.
       
    58 *  output : ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + ZBUFF_endFrameSize : ensures it's always possible to write/flush/end a full block at best speed.
       
    59 * ***********************************************************/
       
    60 
       
    61 typedef enum { ZBUFFcs_init, ZBUFFcs_load, ZBUFFcs_flush, ZBUFFcs_final } ZBUFF_cStage;
       
    62 
       
    63 /* *** Resources *** */
       
    64 struct ZBUFF_CCtx_s {
       
    65     ZSTD_CCtx* zc;
       
    66     char*  inBuff;
       
    67     size_t inBuffSize;
       
    68     size_t inToCompress;
       
    69     size_t inBuffPos;
       
    70     size_t inBuffTarget;
       
    71     size_t blockSize;
       
    72     char*  outBuff;
       
    73     size_t outBuffSize;
       
    74     size_t outBuffContentSize;
       
    75     size_t outBuffFlushedSize;
       
    76     ZBUFF_cStage stage;
       
    77     U32    checksum;
       
    78     U32    frameEnded;
       
    79     ZSTD_customMem customMem;
       
    80 };   /* typedef'd tp ZBUFF_CCtx within "zbuff.h" */
       
    81 
       
    82 ZBUFF_CCtx* ZBUFF_createCCtx(void)
       
    83 {
       
    84     return ZBUFF_createCCtx_advanced(defaultCustomMem);
       
    85 }
       
    86 
       
    87 ZBUFF_CCtx* ZBUFF_createCCtx_advanced(ZSTD_customMem customMem)
       
    88 {
       
    89     ZBUFF_CCtx* zbc;
       
    90 
       
    91     if (!customMem.customAlloc && !customMem.customFree)
       
    92         customMem = defaultCustomMem;
       
    93 
       
    94     if (!customMem.customAlloc || !customMem.customFree)
       
    95         return NULL;
       
    96 
       
    97     zbc = (ZBUFF_CCtx*)customMem.customAlloc(customMem.opaque, sizeof(ZBUFF_CCtx));
       
    98     if (zbc==NULL) return NULL;
       
    99     memset(zbc, 0, sizeof(ZBUFF_CCtx));
       
   100     memcpy(&zbc->customMem, &customMem, sizeof(ZSTD_customMem));
       
   101     zbc->zc = ZSTD_createCCtx_advanced(customMem);
       
   102     if (zbc->zc == NULL) { ZBUFF_freeCCtx(zbc); return NULL; }
       
   103     return zbc;
       
   104 }
       
   105 
       
   106 size_t ZBUFF_freeCCtx(ZBUFF_CCtx* zbc)
       
   107 {
       
   108     if (zbc==NULL) return 0;   /* support free on NULL */
       
   109     ZSTD_freeCCtx(zbc->zc);
       
   110     if (zbc->inBuff) zbc->customMem.customFree(zbc->customMem.opaque, zbc->inBuff);
       
   111     if (zbc->outBuff) zbc->customMem.customFree(zbc->customMem.opaque, zbc->outBuff);
       
   112     zbc->customMem.customFree(zbc->customMem.opaque, zbc);
       
   113     return 0;
       
   114 }
       
   115 
       
   116 
       
   117 /* ======   Initialization   ====== */
       
   118 
       
   119 size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc,
       
   120                                    const void* dict, size_t dictSize,
       
   121                                    ZSTD_parameters params, unsigned long long pledgedSrcSize)
       
   122 {
       
   123     /* allocate buffers */
       
   124     {   size_t const neededInBuffSize = (size_t)1 << params.cParams.windowLog;
       
   125         if (zbc->inBuffSize < neededInBuffSize) {
       
   126             zbc->inBuffSize = neededInBuffSize;
       
   127             zbc->customMem.customFree(zbc->customMem.opaque, zbc->inBuff);   /* should not be necessary */
       
   128             zbc->inBuff = (char*)zbc->customMem.customAlloc(zbc->customMem.opaque, neededInBuffSize);
       
   129             if (zbc->inBuff == NULL) return ERROR(memory_allocation);
       
   130         }
       
   131         zbc->blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, neededInBuffSize);
       
   132     }
       
   133     if (zbc->outBuffSize < ZSTD_compressBound(zbc->blockSize)+1) {
       
   134         zbc->outBuffSize = ZSTD_compressBound(zbc->blockSize)+1;
       
   135         zbc->customMem.customFree(zbc->customMem.opaque, zbc->outBuff);   /* should not be necessary */
       
   136         zbc->outBuff = (char*)zbc->customMem.customAlloc(zbc->customMem.opaque, zbc->outBuffSize);
       
   137         if (zbc->outBuff == NULL) return ERROR(memory_allocation);
       
   138     }
       
   139 
       
   140     { size_t const errorCode = ZSTD_compressBegin_advanced(zbc->zc, dict, dictSize, params, pledgedSrcSize);
       
   141       if (ZSTD_isError(errorCode)) return errorCode; }
       
   142 
       
   143     zbc->inToCompress = 0;
       
   144     zbc->inBuffPos = 0;
       
   145     zbc->inBuffTarget = zbc->blockSize;
       
   146     zbc->outBuffContentSize = zbc->outBuffFlushedSize = 0;
       
   147     zbc->stage = ZBUFFcs_load;
       
   148     zbc->checksum = params.fParams.checksumFlag > 0;
       
   149     zbc->frameEnded = 0;
       
   150     return 0;   /* ready to go */
       
   151 }
       
   152 
       
   153 
       
   154 size_t ZBUFF_compressInitDictionary(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, int compressionLevel)
       
   155 {
       
   156     ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, dictSize);
       
   157     return ZBUFF_compressInit_advanced(zbc, dict, dictSize, params, 0);
       
   158 }
       
   159 
       
   160 size_t ZBUFF_compressInit(ZBUFF_CCtx* zbc, int compressionLevel)
       
   161 {
       
   162     return ZBUFF_compressInitDictionary(zbc, NULL, 0, compressionLevel);
       
   163 }
       
   164 
       
   165 
       
   166 /* internal util function */
       
   167 MEM_STATIC size_t ZBUFF_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
       
   168 {
       
   169     size_t const length = MIN(dstCapacity, srcSize);
       
   170     memcpy(dst, src, length);
       
   171     return length;
       
   172 }
       
   173 
       
   174 
       
   175 /* ======   Compression   ====== */
       
   176 
       
   177 typedef enum { zbf_gather, zbf_flush, zbf_end } ZBUFF_flush_e;
       
   178 
       
   179 static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc,
       
   180                               void* dst, size_t* dstCapacityPtr,
       
   181                         const void* src, size_t* srcSizePtr,
       
   182                               ZBUFF_flush_e const flush)
       
   183 {
       
   184     U32 someMoreWork = 1;
       
   185     const char* const istart = (const char*)src;
       
   186     const char* const iend = istart + *srcSizePtr;
       
   187     const char* ip = istart;
       
   188     char* const ostart = (char*)dst;
       
   189     char* const oend = ostart + *dstCapacityPtr;
       
   190     char* op = ostart;
       
   191 
       
   192     while (someMoreWork) {
       
   193         switch(zbc->stage)
       
   194         {
       
   195         case ZBUFFcs_init: return ERROR(init_missing);   /* call ZBUFF_compressInit() first ! */
       
   196 
       
   197         case ZBUFFcs_load:
       
   198             /* complete inBuffer */
       
   199             {   size_t const toLoad = zbc->inBuffTarget - zbc->inBuffPos;
       
   200                 size_t const loaded = ZBUFF_limitCopy(zbc->inBuff + zbc->inBuffPos, toLoad, ip, iend-ip);
       
   201                 zbc->inBuffPos += loaded;
       
   202                 ip += loaded;
       
   203                 if ( (zbc->inBuffPos==zbc->inToCompress) || (!flush && (toLoad != loaded)) ) {
       
   204                     someMoreWork = 0; break;  /* not enough input to get a full block : stop there, wait for more */
       
   205             }   }
       
   206             /* compress current block (note : this stage cannot be stopped in the middle) */
       
   207             {   void* cDst;
       
   208                 size_t cSize;
       
   209                 size_t const iSize = zbc->inBuffPos - zbc->inToCompress;
       
   210                 size_t oSize = oend-op;
       
   211                 if (oSize >= ZSTD_compressBound(iSize))
       
   212                     cDst = op;   /* compress directly into output buffer (avoid flush stage) */
       
   213                 else
       
   214                     cDst = zbc->outBuff, oSize = zbc->outBuffSize;
       
   215                 cSize = (flush == zbf_end) ?
       
   216                         ZSTD_compressEnd(zbc->zc, cDst, oSize, zbc->inBuff + zbc->inToCompress, iSize) :
       
   217                         ZSTD_compressContinue(zbc->zc, cDst, oSize, zbc->inBuff + zbc->inToCompress, iSize);
       
   218                 if (ZSTD_isError(cSize)) return cSize;
       
   219                 if (flush == zbf_end) zbc->frameEnded = 1;
       
   220                 /* prepare next block */
       
   221                 zbc->inBuffTarget = zbc->inBuffPos + zbc->blockSize;
       
   222                 if (zbc->inBuffTarget > zbc->inBuffSize)
       
   223                     zbc->inBuffPos = 0, zbc->inBuffTarget = zbc->blockSize;   /* note : inBuffSize >= blockSize */
       
   224                 zbc->inToCompress = zbc->inBuffPos;
       
   225                 if (cDst == op) { op += cSize; break; }   /* no need to flush */
       
   226                 zbc->outBuffContentSize = cSize;
       
   227                 zbc->outBuffFlushedSize = 0;
       
   228                 zbc->stage = ZBUFFcs_flush;   /* continue to flush stage */
       
   229             }
       
   230 
       
   231         case ZBUFFcs_flush:
       
   232             {   size_t const toFlush = zbc->outBuffContentSize - zbc->outBuffFlushedSize;
       
   233                 size_t const flushed = ZBUFF_limitCopy(op, oend-op, zbc->outBuff + zbc->outBuffFlushedSize, toFlush);
       
   234                 op += flushed;
       
   235                 zbc->outBuffFlushedSize += flushed;
       
   236                 if (toFlush!=flushed) { someMoreWork = 0; break; } /* dst too small to store flushed data : stop there */
       
   237                 zbc->outBuffContentSize = zbc->outBuffFlushedSize = 0;
       
   238                 zbc->stage = ZBUFFcs_load;
       
   239                 break;
       
   240             }
       
   241 
       
   242         case ZBUFFcs_final:
       
   243             someMoreWork = 0;   /* do nothing */
       
   244             break;
       
   245 
       
   246         default:
       
   247             return ERROR(GENERIC);   /* impossible */
       
   248         }
       
   249     }
       
   250 
       
   251     *srcSizePtr = ip - istart;
       
   252     *dstCapacityPtr = op - ostart;
       
   253     if (zbc->frameEnded) return 0;
       
   254     {   size_t hintInSize = zbc->inBuffTarget - zbc->inBuffPos;
       
   255         if (hintInSize==0) hintInSize = zbc->blockSize;
       
   256         return hintInSize;
       
   257     }
       
   258 }
       
   259 
       
   260 size_t ZBUFF_compressContinue(ZBUFF_CCtx* zbc,
       
   261                               void* dst, size_t* dstCapacityPtr,
       
   262                         const void* src, size_t* srcSizePtr)
       
   263 {
       
   264     return ZBUFF_compressContinue_generic(zbc, dst, dstCapacityPtr, src, srcSizePtr, zbf_gather);
       
   265 }
       
   266 
       
   267 
       
   268 
       
   269 /* ======   Finalize   ====== */
       
   270 
       
   271 size_t ZBUFF_compressFlush(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr)
       
   272 {
       
   273     size_t srcSize = 0;
       
   274     ZBUFF_compressContinue_generic(zbc, dst, dstCapacityPtr, &srcSize, &srcSize, zbf_flush);  /* use a valid src address instead of NULL */
       
   275     return zbc->outBuffContentSize - zbc->outBuffFlushedSize;
       
   276 }
       
   277 
       
   278 
       
   279 size_t ZBUFF_compressEnd(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr)
       
   280 {
       
   281     BYTE* const ostart = (BYTE*)dst;
       
   282     BYTE* const oend = ostart + *dstCapacityPtr;
       
   283     BYTE* op = ostart;
       
   284 
       
   285     if (zbc->stage != ZBUFFcs_final) {
       
   286         /* flush whatever remains */
       
   287         size_t outSize = *dstCapacityPtr;
       
   288         size_t srcSize = 0;
       
   289         size_t const notEnded = ZBUFF_compressContinue_generic(zbc, dst, &outSize, &srcSize, &srcSize, zbf_end);  /* use a valid address instead of NULL */
       
   290         size_t const remainingToFlush = zbc->outBuffContentSize - zbc->outBuffFlushedSize;
       
   291         op += outSize;
       
   292         if (remainingToFlush) {
       
   293             *dstCapacityPtr = op-ostart;
       
   294             return remainingToFlush + ZBUFF_endFrameSize + (zbc->checksum * 4);
       
   295         }
       
   296         /* create epilogue */
       
   297         zbc->stage = ZBUFFcs_final;
       
   298         zbc->outBuffContentSize = !notEnded ? 0 :
       
   299             ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize, NULL, 0);  /* write epilogue into outBuff */
       
   300     }
       
   301 
       
   302     /* flush epilogue */
       
   303     {   size_t const toFlush = zbc->outBuffContentSize - zbc->outBuffFlushedSize;
       
   304         size_t const flushed = ZBUFF_limitCopy(op, oend-op, zbc->outBuff + zbc->outBuffFlushedSize, toFlush);
       
   305         op += flushed;
       
   306         zbc->outBuffFlushedSize += flushed;
       
   307         *dstCapacityPtr = op-ostart;
       
   308         if (toFlush==flushed) zbc->stage = ZBUFFcs_init;  /* end reached */
       
   309         return toFlush - flushed;
       
   310     }
       
   311 }
       
   312 
       
   313 
       
   314 
       
   315 /* *************************************
       
   316 *  Tool functions
       
   317 ***************************************/
       
   318 size_t ZBUFF_recommendedCInSize(void)  { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; }
       
   319 size_t ZBUFF_recommendedCOutSize(void) { return ZSTD_compressBound(ZSTD_BLOCKSIZE_ABSOLUTEMAX) + ZSTD_blockHeaderSize + ZBUFF_endFrameSize; }