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; } |
---|