[8ebc79b] | 1 | /* |
---|
| 2 | Buffered version of Zstd compression library |
---|
| 3 | Copyright (C) 2015-2016, Yann Collet. |
---|
| 4 | |
---|
| 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
---|
| 6 | |
---|
| 7 | Redistribution and use in source and binary forms, with or without |
---|
| 8 | modification, are permitted provided that the following conditions are |
---|
| 9 | met: |
---|
| 10 | * Redistributions of source code must retain the above copyright |
---|
| 11 | notice, this list of conditions and the following disclaimer. |
---|
| 12 | * Redistributions in binary form must reproduce the above |
---|
| 13 | copyright notice, this list of conditions and the following disclaimer |
---|
| 14 | in the documentation and/or other materials provided with the |
---|
| 15 | distribution. |
---|
| 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
---|
| 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
---|
| 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
---|
| 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
---|
| 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
---|
| 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
---|
| 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
---|
| 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
---|
| 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
---|
| 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
---|
| 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
| 27 | |
---|
| 28 | You can contact the author at : |
---|
| 29 | - zstd homepage : http://www.zstd.net/ |
---|
| 30 | */ |
---|
| 31 | |
---|
| 32 | |
---|
| 33 | /* ************************************* |
---|
| 34 | * Dependencies |
---|
| 35 | ***************************************/ |
---|
| 36 | #include <stdlib.h> |
---|
| 37 | #include "error_private.h" |
---|
| 38 | #include "zstd_internal.h" /* MIN, ZSTD_blockHeaderSize, ZSTD_BLOCKSIZE_MAX */ |
---|
| 39 | #define ZBUFF_STATIC_LINKING_ONLY |
---|
| 40 | #include "zbuff.h" |
---|
| 41 | |
---|
| 42 | |
---|
| 43 | /*-*************************************************************************** |
---|
| 44 | * Streaming decompression howto |
---|
| 45 | * |
---|
| 46 | * A ZBUFF_DCtx object is required to track streaming operations. |
---|
| 47 | * Use ZBUFF_createDCtx() and ZBUFF_freeDCtx() to create/release resources. |
---|
| 48 | * Use ZBUFF_decompressInit() to start a new decompression operation, |
---|
| 49 | * or ZBUFF_decompressInitDictionary() if decompression requires a dictionary. |
---|
| 50 | * Note that ZBUFF_DCtx objects can be re-init multiple times. |
---|
| 51 | * |
---|
| 52 | * Use ZBUFF_decompressContinue() repetitively to consume your input. |
---|
| 53 | * *srcSizePtr and *dstCapacityPtr can be any size. |
---|
| 54 | * The function will report how many bytes were read or written by modifying *srcSizePtr and *dstCapacityPtr. |
---|
| 55 | * Note that it may not consume the entire input, in which case it's up to the caller to present remaining input again. |
---|
| 56 | * The content of @dst will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters, or change @dst. |
---|
| 57 | * @return : a hint to preferred nb of bytes to use as input for next function call (it's only a hint, to help latency), |
---|
| 58 | * or 0 when a frame is completely decoded, |
---|
| 59 | * or an error code, which can be tested using ZBUFF_isError(). |
---|
| 60 | * |
---|
| 61 | * Hint : recommended buffer sizes (not compulsory) : ZBUFF_recommendedDInSize() and ZBUFF_recommendedDOutSize() |
---|
| 62 | * output : ZBUFF_recommendedDOutSize==128 KB block size is the internal unit, it ensures it's always possible to write a full block when decoded. |
---|
| 63 | * input : ZBUFF_recommendedDInSize == 128KB + 3; |
---|
| 64 | * just follow indications from ZBUFF_decompressContinue() to minimize latency. It should always be <= 128 KB + 3 . |
---|
| 65 | * *******************************************************************************/ |
---|
| 66 | |
---|
| 67 | typedef enum { ZBUFFds_init, ZBUFFds_loadHeader, |
---|
| 68 | ZBUFFds_read, ZBUFFds_load, ZBUFFds_flush } ZBUFF_dStage; |
---|
| 69 | |
---|
| 70 | /* *** Resource management *** */ |
---|
| 71 | struct ZBUFF_DCtx_s { |
---|
| 72 | ZSTD_DCtx* zd; |
---|
| 73 | ZSTD_frameParams fParams; |
---|
| 74 | ZBUFF_dStage stage; |
---|
| 75 | char* inBuff; |
---|
| 76 | size_t inBuffSize; |
---|
| 77 | size_t inPos; |
---|
| 78 | char* outBuff; |
---|
| 79 | size_t outBuffSize; |
---|
| 80 | size_t outStart; |
---|
| 81 | size_t outEnd; |
---|
| 82 | size_t blockSize; |
---|
| 83 | BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; |
---|
| 84 | size_t lhSize; |
---|
| 85 | ZSTD_customMem customMem; |
---|
| 86 | }; /* typedef'd to ZBUFF_DCtx within "zstd_buffered.h" */ |
---|
| 87 | |
---|
| 88 | |
---|
| 89 | ZBUFF_DCtx* ZBUFF_createDCtx(void) |
---|
| 90 | { |
---|
| 91 | return ZBUFF_createDCtx_advanced(defaultCustomMem); |
---|
| 92 | } |
---|
| 93 | |
---|
| 94 | ZBUFF_DCtx* ZBUFF_createDCtx_advanced(ZSTD_customMem customMem) |
---|
| 95 | { |
---|
| 96 | ZBUFF_DCtx* zbd; |
---|
| 97 | |
---|
| 98 | if (!customMem.customAlloc && !customMem.customFree) |
---|
| 99 | customMem = defaultCustomMem; |
---|
| 100 | |
---|
| 101 | if (!customMem.customAlloc || !customMem.customFree) |
---|
| 102 | return NULL; |
---|
| 103 | |
---|
| 104 | zbd = (ZBUFF_DCtx*)customMem.customAlloc(customMem.opaque, sizeof(ZBUFF_DCtx)); |
---|
| 105 | if (zbd==NULL) return NULL; |
---|
| 106 | memset(zbd, 0, sizeof(ZBUFF_DCtx)); |
---|
| 107 | memcpy(&zbd->customMem, &customMem, sizeof(ZSTD_customMem)); |
---|
| 108 | zbd->zd = ZSTD_createDCtx_advanced(customMem); |
---|
| 109 | if (zbd->zd == NULL) { ZBUFF_freeDCtx(zbd); return NULL; } |
---|
| 110 | zbd->stage = ZBUFFds_init; |
---|
| 111 | return zbd; |
---|
| 112 | } |
---|
| 113 | |
---|
| 114 | size_t ZBUFF_freeDCtx(ZBUFF_DCtx* zbd) |
---|
| 115 | { |
---|
| 116 | if (zbd==NULL) return 0; /* support free on null */ |
---|
| 117 | ZSTD_freeDCtx(zbd->zd); |
---|
| 118 | if (zbd->inBuff) zbd->customMem.customFree(zbd->customMem.opaque, zbd->inBuff); |
---|
| 119 | if (zbd->outBuff) zbd->customMem.customFree(zbd->customMem.opaque, zbd->outBuff); |
---|
| 120 | zbd->customMem.customFree(zbd->customMem.opaque, zbd); |
---|
| 121 | return 0; |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | |
---|
| 125 | /* *** Initialization *** */ |
---|
| 126 | |
---|
| 127 | size_t ZBUFF_decompressInitDictionary(ZBUFF_DCtx* zbd, const void* dict, size_t dictSize) |
---|
| 128 | { |
---|
| 129 | zbd->stage = ZBUFFds_loadHeader; |
---|
| 130 | zbd->lhSize = zbd->inPos = zbd->outStart = zbd->outEnd = 0; |
---|
| 131 | return ZSTD_decompressBegin_usingDict(zbd->zd, dict, dictSize); |
---|
| 132 | } |
---|
| 133 | |
---|
| 134 | size_t ZBUFF_decompressInit(ZBUFF_DCtx* zbd) |
---|
| 135 | { |
---|
| 136 | return ZBUFF_decompressInitDictionary(zbd, NULL, 0); |
---|
| 137 | } |
---|
| 138 | |
---|
| 139 | |
---|
| 140 | /* internal util function */ |
---|
| 141 | MEM_STATIC size_t ZBUFF_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize) |
---|
| 142 | { |
---|
| 143 | size_t const length = MIN(dstCapacity, srcSize); |
---|
| 144 | memcpy(dst, src, length); |
---|
| 145 | return length; |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | |
---|
| 149 | /* *** Decompression *** */ |
---|
| 150 | |
---|
| 151 | size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, |
---|
| 152 | void* dst, size_t* dstCapacityPtr, |
---|
| 153 | const void* src, size_t* srcSizePtr) |
---|
| 154 | { |
---|
| 155 | const char* const istart = (const char*)src; |
---|
| 156 | const char* const iend = istart + *srcSizePtr; |
---|
| 157 | const char* ip = istart; |
---|
| 158 | char* const ostart = (char*)dst; |
---|
| 159 | char* const oend = ostart + *dstCapacityPtr; |
---|
| 160 | char* op = ostart; |
---|
| 161 | U32 notDone = 1; |
---|
| 162 | |
---|
| 163 | while (notDone) { |
---|
| 164 | switch(zbd->stage) |
---|
| 165 | { |
---|
| 166 | case ZBUFFds_init : |
---|
| 167 | return ERROR(init_missing); |
---|
| 168 | |
---|
| 169 | case ZBUFFds_loadHeader : |
---|
| 170 | { size_t const hSize = ZSTD_getFrameParams(&(zbd->fParams), zbd->headerBuffer, zbd->lhSize); |
---|
| 171 | if (hSize != 0) { |
---|
| 172 | size_t const toLoad = hSize - zbd->lhSize; /* if hSize!=0, hSize > zbd->lhSize */ |
---|
| 173 | if (ZSTD_isError(hSize)) return hSize; |
---|
| 174 | if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ |
---|
| 175 | memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip); |
---|
| 176 | zbd->lhSize += iend-ip; |
---|
| 177 | *dstCapacityPtr = 0; |
---|
| 178 | return (hSize - zbd->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ |
---|
| 179 | } |
---|
| 180 | memcpy(zbd->headerBuffer + zbd->lhSize, ip, toLoad); zbd->lhSize = hSize; ip += toLoad; |
---|
| 181 | break; |
---|
| 182 | } } |
---|
| 183 | |
---|
| 184 | /* Consume header */ |
---|
| 185 | { size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zbd->zd); /* == ZSTD_frameHeaderSize_min */ |
---|
| 186 | size_t const h1Result = ZSTD_decompressContinue(zbd->zd, NULL, 0, zbd->headerBuffer, h1Size); |
---|
| 187 | if (ZSTD_isError(h1Result)) return h1Result; |
---|
| 188 | if (h1Size < zbd->lhSize) { /* long header */ |
---|
| 189 | size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zbd->zd); |
---|
| 190 | size_t const h2Result = ZSTD_decompressContinue(zbd->zd, NULL, 0, zbd->headerBuffer+h1Size, h2Size); |
---|
| 191 | if (ZSTD_isError(h2Result)) return h2Result; |
---|
| 192 | } } |
---|
| 193 | |
---|
| 194 | zbd->fParams.windowSize = MAX(zbd->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); |
---|
| 195 | |
---|
| 196 | /* Frame header instruct buffer sizes */ |
---|
| 197 | { size_t const blockSize = MIN(zbd->fParams.windowSize, ZSTD_BLOCKSIZE_MAX); |
---|
| 198 | zbd->blockSize = blockSize; |
---|
| 199 | if (zbd->inBuffSize < blockSize) { |
---|
| 200 | zbd->customMem.customFree(zbd->customMem.opaque, zbd->inBuff); |
---|
| 201 | zbd->inBuffSize = blockSize; |
---|
| 202 | zbd->inBuff = (char*)zbd->customMem.customAlloc(zbd->customMem.opaque, blockSize); |
---|
| 203 | if (zbd->inBuff == NULL) return ERROR(memory_allocation); |
---|
| 204 | } |
---|
| 205 | { size_t const neededOutSize = zbd->fParams.windowSize + blockSize; |
---|
| 206 | if (zbd->outBuffSize < neededOutSize) { |
---|
| 207 | zbd->customMem.customFree(zbd->customMem.opaque, zbd->outBuff); |
---|
| 208 | zbd->outBuffSize = neededOutSize; |
---|
| 209 | zbd->outBuff = (char*)zbd->customMem.customAlloc(zbd->customMem.opaque, neededOutSize); |
---|
| 210 | if (zbd->outBuff == NULL) return ERROR(memory_allocation); |
---|
| 211 | } } } |
---|
| 212 | zbd->stage = ZBUFFds_read; |
---|
| 213 | |
---|
| 214 | case ZBUFFds_read: |
---|
| 215 | { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zbd->zd); |
---|
| 216 | if (neededInSize==0) { /* end of frame */ |
---|
| 217 | zbd->stage = ZBUFFds_init; |
---|
| 218 | notDone = 0; |
---|
| 219 | break; |
---|
| 220 | } |
---|
| 221 | if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */ |
---|
| 222 | const int isSkipFrame = ZSTD_isSkipFrame(zbd->zd); |
---|
| 223 | size_t const decodedSize = ZSTD_decompressContinue(zbd->zd, |
---|
| 224 | zbd->outBuff + zbd->outStart, (isSkipFrame ? 0 : zbd->outBuffSize - zbd->outStart), |
---|
| 225 | ip, neededInSize); |
---|
| 226 | if (ZSTD_isError(decodedSize)) return decodedSize; |
---|
| 227 | ip += neededInSize; |
---|
| 228 | if (!decodedSize && !isSkipFrame) break; /* this was just a header */ |
---|
| 229 | zbd->outEnd = zbd->outStart + decodedSize; |
---|
| 230 | zbd->stage = ZBUFFds_flush; |
---|
| 231 | break; |
---|
| 232 | } |
---|
| 233 | if (ip==iend) { notDone = 0; break; } /* no more input */ |
---|
| 234 | zbd->stage = ZBUFFds_load; |
---|
| 235 | } |
---|
| 236 | |
---|
| 237 | case ZBUFFds_load: |
---|
| 238 | { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zbd->zd); |
---|
| 239 | size_t const toLoad = neededInSize - zbd->inPos; /* should always be <= remaining space within inBuff */ |
---|
| 240 | size_t loadedSize; |
---|
| 241 | if (toLoad > zbd->inBuffSize - zbd->inPos) return ERROR(corruption_detected); /* should never happen */ |
---|
| 242 | loadedSize = ZBUFF_limitCopy(zbd->inBuff + zbd->inPos, toLoad, ip, iend-ip); |
---|
| 243 | ip += loadedSize; |
---|
| 244 | zbd->inPos += loadedSize; |
---|
| 245 | if (loadedSize < toLoad) { notDone = 0; break; } /* not enough input, wait for more */ |
---|
| 246 | |
---|
| 247 | /* decode loaded input */ |
---|
| 248 | { const int isSkipFrame = ZSTD_isSkipFrame(zbd->zd); |
---|
| 249 | size_t const decodedSize = ZSTD_decompressContinue(zbd->zd, |
---|
| 250 | zbd->outBuff + zbd->outStart, zbd->outBuffSize - zbd->outStart, |
---|
| 251 | zbd->inBuff, neededInSize); |
---|
| 252 | if (ZSTD_isError(decodedSize)) return decodedSize; |
---|
| 253 | zbd->inPos = 0; /* input is consumed */ |
---|
| 254 | if (!decodedSize && !isSkipFrame) { zbd->stage = ZBUFFds_read; break; } /* this was just a header */ |
---|
| 255 | zbd->outEnd = zbd->outStart + decodedSize; |
---|
| 256 | zbd->stage = ZBUFFds_flush; |
---|
| 257 | // break; /* ZBUFFds_flush follows */ |
---|
| 258 | } } |
---|
| 259 | |
---|
| 260 | case ZBUFFds_flush: |
---|
| 261 | { size_t const toFlushSize = zbd->outEnd - zbd->outStart; |
---|
| 262 | size_t const flushedSize = ZBUFF_limitCopy(op, oend-op, zbd->outBuff + zbd->outStart, toFlushSize); |
---|
| 263 | op += flushedSize; |
---|
| 264 | zbd->outStart += flushedSize; |
---|
| 265 | if (flushedSize == toFlushSize) { |
---|
| 266 | zbd->stage = ZBUFFds_read; |
---|
| 267 | if (zbd->outStart + zbd->blockSize > zbd->outBuffSize) |
---|
| 268 | zbd->outStart = zbd->outEnd = 0; |
---|
| 269 | break; |
---|
| 270 | } |
---|
| 271 | /* cannot flush everything */ |
---|
| 272 | notDone = 0; |
---|
| 273 | break; |
---|
| 274 | } |
---|
| 275 | default: return ERROR(GENERIC); /* impossible */ |
---|
| 276 | } } |
---|
| 277 | |
---|
| 278 | /* result */ |
---|
| 279 | *srcSizePtr = ip-istart; |
---|
| 280 | *dstCapacityPtr = op-ostart; |
---|
| 281 | { size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zbd->zd); |
---|
| 282 | // if (nextSrcSizeHint > ZSTD_blockHeaderSize) nextSrcSizeHint+= ZSTD_blockHeaderSize; /* get following block header too */ |
---|
| 283 | nextSrcSizeHint -= zbd->inPos; /* already loaded*/ |
---|
| 284 | return nextSrcSizeHint; |
---|
| 285 | } |
---|
| 286 | } |
---|
| 287 | |
---|
| 288 | |
---|
| 289 | |
---|
| 290 | /* ************************************* |
---|
| 291 | * Tool functions |
---|
| 292 | ***************************************/ |
---|
| 293 | size_t ZBUFF_recommendedDInSize(void) { return ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize /* block header size*/ ; } |
---|
| 294 | size_t ZBUFF_recommendedDOutSize(void) { return ZSTD_BLOCKSIZE_MAX; } |
---|