Line data Source code
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, ZSTD_BLOCKSIZE_MAX */
18 : #define ZBUFF_STATIC_LINKING_ONLY
19 : #include "zbuff.h"
20 :
21 :
22 : typedef enum { ZBUFFds_init, ZBUFFds_loadHeader,
23 : ZBUFFds_read, ZBUFFds_load, ZBUFFds_flush } ZBUFF_dStage;
24 :
25 : /* *** Resource management *** */
26 : struct ZBUFF_DCtx_s {
27 : ZSTD_DCtx* zd;
28 : ZSTD_frameParams fParams;
29 : ZBUFF_dStage stage;
30 : char* inBuff;
31 : size_t inBuffSize;
32 : size_t inPos;
33 : char* outBuff;
34 : size_t outBuffSize;
35 : size_t outStart;
36 : size_t outEnd;
37 : size_t blockSize;
38 : BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
39 : size_t lhSize;
40 : ZSTD_customMem customMem;
41 : }; /* typedef'd to ZBUFF_DCtx within "zbuff.h" */
42 :
43 :
44 0 : ZBUFF_DCtx* ZBUFF_createDCtx(void)
45 : {
46 0 : return ZBUFF_createDCtx_advanced(defaultCustomMem);
47 : }
48 :
49 0 : ZBUFF_DCtx* ZBUFF_createDCtx_advanced(ZSTD_customMem customMem)
50 : {
51 : ZBUFF_DCtx* zbd;
52 :
53 0 : if (!customMem.customAlloc && !customMem.customFree)
54 0 : customMem = defaultCustomMem;
55 :
56 0 : if (!customMem.customAlloc || !customMem.customFree)
57 0 : return NULL;
58 :
59 0 : zbd = (ZBUFF_DCtx*)customMem.customAlloc(customMem.opaque, sizeof(ZBUFF_DCtx));
60 0 : if (zbd==NULL) return NULL;
61 0 : memset(zbd, 0, sizeof(ZBUFF_DCtx));
62 0 : memcpy(&zbd->customMem, &customMem, sizeof(ZSTD_customMem));
63 0 : zbd->zd = ZSTD_createDCtx_advanced(customMem);
64 0 : if (zbd->zd == NULL) { ZBUFF_freeDCtx(zbd); return NULL; }
65 0 : zbd->stage = ZBUFFds_init;
66 0 : return zbd;
67 : }
68 :
69 0 : size_t ZBUFF_freeDCtx(ZBUFF_DCtx* zbd)
70 : {
71 0 : if (zbd==NULL) return 0; /* support free on null */
72 0 : ZSTD_freeDCtx(zbd->zd);
73 0 : if (zbd->inBuff) zbd->customMem.customFree(zbd->customMem.opaque, zbd->inBuff);
74 0 : if (zbd->outBuff) zbd->customMem.customFree(zbd->customMem.opaque, zbd->outBuff);
75 0 : zbd->customMem.customFree(zbd->customMem.opaque, zbd);
76 0 : return 0;
77 : }
78 :
79 :
80 : /* *** Initialization *** */
81 :
82 0 : size_t ZBUFF_decompressInitDictionary(ZBUFF_DCtx* zbd, const void* dict, size_t dictSize)
83 : {
84 0 : zbd->stage = ZBUFFds_loadHeader;
85 0 : zbd->lhSize = zbd->inPos = zbd->outStart = zbd->outEnd = 0;
86 0 : return ZSTD_decompressBegin_usingDict(zbd->zd, dict, dictSize);
87 : }
88 :
89 0 : size_t ZBUFF_decompressInit(ZBUFF_DCtx* zbd)
90 : {
91 0 : return ZBUFF_decompressInitDictionary(zbd, NULL, 0);
92 : }
93 :
94 :
95 : /* internal util function */
96 0 : MEM_STATIC size_t ZBUFF_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
97 : {
98 0 : size_t const length = MIN(dstCapacity, srcSize);
99 0 : memcpy(dst, src, length);
100 0 : return length;
101 : }
102 :
103 :
104 : /* *** Decompression *** */
105 :
106 0 : size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd,
107 : void* dst, size_t* dstCapacityPtr,
108 : const void* src, size_t* srcSizePtr)
109 : {
110 0 : const char* const istart = (const char*)src;
111 0 : const char* const iend = istart + *srcSizePtr;
112 0 : const char* ip = istart;
113 0 : char* const ostart = (char*)dst;
114 0 : char* const oend = ostart + *dstCapacityPtr;
115 0 : char* op = ostart;
116 0 : U32 someMoreWork = 1;
117 :
118 0 : while (someMoreWork) {
119 0 : switch(zbd->stage)
120 : {
121 : case ZBUFFds_init :
122 0 : return ERROR(init_missing);
123 :
124 : case ZBUFFds_loadHeader :
125 0 : { size_t const hSize = ZSTD_getFrameParams(&(zbd->fParams), zbd->headerBuffer, zbd->lhSize);
126 0 : if (ZSTD_isError(hSize)) return hSize;
127 0 : if (hSize != 0) { /* need more input */
128 0 : size_t const toLoad = hSize - zbd->lhSize; /* if hSize!=0, hSize > zbd->lhSize */
129 0 : if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */
130 0 : memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip);
131 0 : zbd->lhSize += iend-ip;
132 0 : *dstCapacityPtr = 0;
133 0 : return (hSize - zbd->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */
134 : }
135 0 : memcpy(zbd->headerBuffer + zbd->lhSize, ip, toLoad); zbd->lhSize = hSize; ip += toLoad;
136 0 : break;
137 : } }
138 :
139 : /* Consume header */
140 0 : { size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zbd->zd); /* == ZSTD_frameHeaderSize_min */
141 0 : size_t const h1Result = ZSTD_decompressContinue(zbd->zd, NULL, 0, zbd->headerBuffer, h1Size);
142 0 : if (ZSTD_isError(h1Result)) return h1Result; /* should not happen : already checked */
143 0 : if (h1Size < zbd->lhSize) { /* long header */
144 0 : size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zbd->zd);
145 0 : size_t const h2Result = ZSTD_decompressContinue(zbd->zd, NULL, 0, zbd->headerBuffer+h1Size, h2Size);
146 0 : if (ZSTD_isError(h2Result)) return h2Result;
147 : } }
148 :
149 0 : zbd->fParams.windowSize = MAX(zbd->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN);
150 :
151 : /* Frame header instruct buffer sizes */
152 0 : { size_t const blockSize = MIN(zbd->fParams.windowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX);
153 0 : size_t const neededOutSize = zbd->fParams.windowSize + blockSize;
154 0 : zbd->blockSize = blockSize;
155 0 : if (zbd->inBuffSize < blockSize) {
156 0 : zbd->customMem.customFree(zbd->customMem.opaque, zbd->inBuff);
157 0 : zbd->inBuffSize = blockSize;
158 0 : zbd->inBuff = (char*)zbd->customMem.customAlloc(zbd->customMem.opaque, blockSize);
159 0 : if (zbd->inBuff == NULL) return ERROR(memory_allocation);
160 : }
161 0 : if (zbd->outBuffSize < neededOutSize) {
162 0 : zbd->customMem.customFree(zbd->customMem.opaque, zbd->outBuff);
163 0 : zbd->outBuffSize = neededOutSize;
164 0 : zbd->outBuff = (char*)zbd->customMem.customAlloc(zbd->customMem.opaque, neededOutSize);
165 0 : if (zbd->outBuff == NULL) return ERROR(memory_allocation);
166 : } }
167 0 : zbd->stage = ZBUFFds_read;
168 : /* pass-through */
169 :
170 : case ZBUFFds_read:
171 0 : { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zbd->zd);
172 0 : if (neededInSize==0) { /* end of frame */
173 0 : zbd->stage = ZBUFFds_init;
174 0 : someMoreWork = 0;
175 0 : break;
176 : }
177 0 : if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */
178 0 : const int isSkipFrame = ZSTD_isSkipFrame(zbd->zd);
179 0 : size_t const decodedSize = ZSTD_decompressContinue(zbd->zd,
180 0 : zbd->outBuff + zbd->outStart, (isSkipFrame ? 0 : zbd->outBuffSize - zbd->outStart),
181 : ip, neededInSize);
182 0 : if (ZSTD_isError(decodedSize)) return decodedSize;
183 0 : ip += neededInSize;
184 0 : if (!decodedSize && !isSkipFrame) break; /* this was just a header */
185 0 : zbd->outEnd = zbd->outStart + decodedSize;
186 0 : zbd->stage = ZBUFFds_flush;
187 0 : break;
188 : }
189 0 : if (ip==iend) { someMoreWork = 0; break; } /* no more input */
190 0 : zbd->stage = ZBUFFds_load;
191 : /* pass-through */
192 : }
193 :
194 : case ZBUFFds_load:
195 0 : { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zbd->zd);
196 0 : size_t const toLoad = neededInSize - zbd->inPos; /* should always be <= remaining space within inBuff */
197 : size_t loadedSize;
198 0 : if (toLoad > zbd->inBuffSize - zbd->inPos) return ERROR(corruption_detected); /* should never happen */
199 0 : loadedSize = ZBUFF_limitCopy(zbd->inBuff + zbd->inPos, toLoad, ip, iend-ip);
200 0 : ip += loadedSize;
201 0 : zbd->inPos += loadedSize;
202 0 : if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */
203 :
204 : /* decode loaded input */
205 0 : { const int isSkipFrame = ZSTD_isSkipFrame(zbd->zd);
206 0 : size_t const decodedSize = ZSTD_decompressContinue(zbd->zd,
207 0 : zbd->outBuff + zbd->outStart, zbd->outBuffSize - zbd->outStart,
208 0 : zbd->inBuff, neededInSize);
209 0 : if (ZSTD_isError(decodedSize)) return decodedSize;
210 0 : zbd->inPos = 0; /* input is consumed */
211 0 : if (!decodedSize && !isSkipFrame) { zbd->stage = ZBUFFds_read; break; } /* this was just a header */
212 0 : zbd->outEnd = zbd->outStart + decodedSize;
213 0 : zbd->stage = ZBUFFds_flush;
214 : /* pass-through */
215 : } }
216 :
217 : case ZBUFFds_flush:
218 0 : { size_t const toFlushSize = zbd->outEnd - zbd->outStart;
219 0 : size_t const flushedSize = ZBUFF_limitCopy(op, oend-op, zbd->outBuff + zbd->outStart, toFlushSize);
220 0 : op += flushedSize;
221 0 : zbd->outStart += flushedSize;
222 0 : if (flushedSize == toFlushSize) { /* flush completed */
223 0 : zbd->stage = ZBUFFds_read;
224 0 : if (zbd->outStart + zbd->blockSize > zbd->outBuffSize)
225 0 : zbd->outStart = zbd->outEnd = 0;
226 0 : break;
227 : }
228 : /* cannot flush everything */
229 0 : someMoreWork = 0;
230 0 : break;
231 : }
232 0 : default: return ERROR(GENERIC); /* impossible */
233 : } }
234 :
235 : /* result */
236 0 : *srcSizePtr = ip-istart;
237 0 : *dstCapacityPtr = op-ostart;
238 0 : { size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zbd->zd);
239 0 : if (!nextSrcSizeHint) return (zbd->outEnd != zbd->outStart); /* return 0 only if fully flushed too */
240 0 : nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zbd->zd) == ZSTDnit_block);
241 0 : if (zbd->inPos > nextSrcSizeHint) return ERROR(GENERIC); /* should never happen */
242 0 : nextSrcSizeHint -= zbd->inPos; /* already loaded*/
243 0 : return nextSrcSizeHint;
244 : }
245 : }
246 :
247 :
248 : /* *************************************
249 : * Tool functions
250 : ***************************************/
251 0 : size_t ZBUFF_recommendedDInSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX + ZSTD_blockHeaderSize /* block header size*/ ; }
252 0 : size_t ZBUFF_recommendedDOutSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; }
|