1 /*
2 * LZ4 file library
3 * Copyright (C) 2022, Xiaomi Inc.
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 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following disclaimer
15 * in the documentation and/or other materials provided with the
16 * distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * You can contact the author at :
31 * - LZ4 homepage : http://www.lz4.org
32 * - LZ4 source repository : https://github.com/lz4/lz4
33 */
34 #include <stdlib.h> /* malloc, free */
35 #include <string.h>
36 #include <assert.h>
37 #include "lz4.h"
38 #include "lz4file.h"
39
returnErrorCode(LZ4F_errorCodes code)40 static LZ4F_errorCode_t returnErrorCode(LZ4F_errorCodes code)
41 {
42 return (LZ4F_errorCode_t)-(ptrdiff_t)code;
43 }
44 #undef RETURN_ERROR
45 #define RETURN_ERROR(e) return returnErrorCode(LZ4F_ERROR_ ## e)
46
47 /* ===== read API ===== */
48
49 struct LZ4_readFile_s {
50 LZ4F_dctx* dctxPtr;
51 FILE* fp;
52 LZ4_byte* srcBuf;
53 size_t srcBufNext;
54 size_t srcBufSize;
55 size_t srcBufMaxSize;
56 };
57
LZ4F_freeReadFile(LZ4_readFile_t * lz4fRead)58 static void LZ4F_freeReadFile(LZ4_readFile_t* lz4fRead)
59 {
60 if (lz4fRead==NULL) return;
61 LZ4F_freeDecompressionContext(lz4fRead->dctxPtr);
62 free(lz4fRead->srcBuf);
63 free(lz4fRead);
64 }
65
LZ4F_freeAndNullReadFile(LZ4_readFile_t ** statePtr)66 static void LZ4F_freeAndNullReadFile(LZ4_readFile_t** statePtr)
67 {
68 assert(statePtr != NULL);
69 LZ4F_freeReadFile(*statePtr);
70 *statePtr = NULL;
71 }
72
LZ4F_readOpen(LZ4_readFile_t ** lz4fRead,FILE * fp)73 LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp)
74 {
75 char buf[LZ4F_HEADER_SIZE_MAX];
76 size_t consumedSize;
77 LZ4F_errorCode_t ret;
78
79 if (fp == NULL || lz4fRead == NULL) {
80 RETURN_ERROR(parameter_null);
81 }
82
83 *lz4fRead = (LZ4_readFile_t*)calloc(1, sizeof(LZ4_readFile_t));
84 if (*lz4fRead == NULL) {
85 RETURN_ERROR(allocation_failed);
86 }
87
88 ret = LZ4F_createDecompressionContext(&(*lz4fRead)->dctxPtr, LZ4F_VERSION);
89 if (LZ4F_isError(ret)) {
90 LZ4F_freeAndNullReadFile(lz4fRead);
91 return ret;
92 }
93
94 (*lz4fRead)->fp = fp;
95 consumedSize = fread(buf, 1, sizeof(buf), (*lz4fRead)->fp);
96 if (consumedSize != sizeof(buf)) {
97 LZ4F_freeAndNullReadFile(lz4fRead);
98 RETURN_ERROR(io_read);
99 }
100
101 { LZ4F_frameInfo_t info;
102 LZ4F_errorCode_t const r = LZ4F_getFrameInfo((*lz4fRead)->dctxPtr, &info, buf, &consumedSize);
103 if (LZ4F_isError(r)) {
104 LZ4F_freeAndNullReadFile(lz4fRead);
105 return r;
106 }
107
108 switch (info.blockSizeID) {
109 case LZ4F_default :
110 case LZ4F_max64KB :
111 (*lz4fRead)->srcBufMaxSize = 64 * 1024;
112 break;
113 case LZ4F_max256KB:
114 (*lz4fRead)->srcBufMaxSize = 256 * 1024;
115 break;
116 case LZ4F_max1MB:
117 (*lz4fRead)->srcBufMaxSize = 1 * 1024 * 1024;
118 break;
119 case LZ4F_max4MB:
120 (*lz4fRead)->srcBufMaxSize = 4 * 1024 * 1024;
121 break;
122 default:
123 LZ4F_freeAndNullReadFile(lz4fRead);
124 RETURN_ERROR(maxBlockSize_invalid);
125 }
126 }
127
128 (*lz4fRead)->srcBuf = (LZ4_byte*)malloc((*lz4fRead)->srcBufMaxSize);
129 if ((*lz4fRead)->srcBuf == NULL) {
130 LZ4F_freeAndNullReadFile(lz4fRead);
131 RETURN_ERROR(allocation_failed);
132 }
133
134 (*lz4fRead)->srcBufSize = sizeof(buf) - consumedSize;
135 memcpy((*lz4fRead)->srcBuf, buf + consumedSize, (*lz4fRead)->srcBufSize);
136
137 return ret;
138 }
139
LZ4F_read(LZ4_readFile_t * lz4fRead,void * buf,size_t size)140 size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size)
141 {
142 LZ4_byte* p = (LZ4_byte*)buf;
143 size_t next = 0;
144
145 if (lz4fRead == NULL || buf == NULL)
146 RETURN_ERROR(parameter_null);
147
148 while (next < size) {
149 size_t srcsize = lz4fRead->srcBufSize - lz4fRead->srcBufNext;
150 size_t dstsize = size - next;
151 size_t ret;
152
153 if (srcsize == 0) {
154 ret = fread(lz4fRead->srcBuf, 1, lz4fRead->srcBufMaxSize, lz4fRead->fp);
155 if (ret > 0) {
156 lz4fRead->srcBufSize = ret;
157 srcsize = lz4fRead->srcBufSize;
158 lz4fRead->srcBufNext = 0;
159 } else if (ret == 0) {
160 break;
161 } else {
162 RETURN_ERROR(io_read);
163 }
164 }
165
166 ret = LZ4F_decompress(lz4fRead->dctxPtr,
167 p, &dstsize,
168 lz4fRead->srcBuf + lz4fRead->srcBufNext,
169 &srcsize,
170 NULL);
171 if (LZ4F_isError(ret)) {
172 return ret;
173 }
174
175 lz4fRead->srcBufNext += srcsize;
176 next += dstsize;
177 p += dstsize;
178 }
179
180 return next;
181 }
182
LZ4F_readClose(LZ4_readFile_t * lz4fRead)183 LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead)
184 {
185 if (lz4fRead == NULL)
186 RETURN_ERROR(parameter_null);
187 LZ4F_freeReadFile(lz4fRead);
188 return LZ4F_OK_NoError;
189 }
190
191 /* ===== write API ===== */
192
193 struct LZ4_writeFile_s {
194 LZ4F_cctx* cctxPtr;
195 FILE* fp;
196 LZ4_byte* dstBuf;
197 size_t maxWriteSize;
198 size_t dstBufMaxSize;
199 LZ4F_errorCode_t errCode;
200 };
201
LZ4F_freeWriteFile(LZ4_writeFile_t * state)202 static void LZ4F_freeWriteFile(LZ4_writeFile_t* state)
203 {
204 if (state == NULL) return;
205 LZ4F_freeCompressionContext(state->cctxPtr);
206 free(state->dstBuf);
207 free(state);
208 }
209
LZ4F_freeAndNullWriteFile(LZ4_writeFile_t ** statePtr)210 static void LZ4F_freeAndNullWriteFile(LZ4_writeFile_t** statePtr)
211 {
212 assert(statePtr != NULL);
213 LZ4F_freeWriteFile(*statePtr);
214 *statePtr = NULL;
215 }
216
LZ4F_writeOpen(LZ4_writeFile_t ** lz4fWrite,FILE * fp,const LZ4F_preferences_t * prefsPtr)217 LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr)
218 {
219 LZ4_byte buf[LZ4F_HEADER_SIZE_MAX];
220 size_t ret;
221
222 if (fp == NULL || lz4fWrite == NULL)
223 RETURN_ERROR(parameter_null);
224
225 *lz4fWrite = (LZ4_writeFile_t*)calloc(1, sizeof(LZ4_writeFile_t));
226 if (*lz4fWrite == NULL) {
227 RETURN_ERROR(allocation_failed);
228 }
229 if (prefsPtr != NULL) {
230 switch (prefsPtr->frameInfo.blockSizeID) {
231 case LZ4F_default :
232 case LZ4F_max64KB :
233 (*lz4fWrite)->maxWriteSize = 64 * 1024;
234 break;
235 case LZ4F_max256KB:
236 (*lz4fWrite)->maxWriteSize = 256 * 1024;
237 break;
238 case LZ4F_max1MB:
239 (*lz4fWrite)->maxWriteSize = 1 * 1024 * 1024;
240 break;
241 case LZ4F_max4MB:
242 (*lz4fWrite)->maxWriteSize = 4 * 1024 * 1024;
243 break;
244 default:
245 LZ4F_freeAndNullWriteFile(lz4fWrite);
246 RETURN_ERROR(maxBlockSize_invalid);
247 }
248 } else {
249 (*lz4fWrite)->maxWriteSize = 64 * 1024;
250 }
251
252 (*lz4fWrite)->dstBufMaxSize = LZ4F_compressBound((*lz4fWrite)->maxWriteSize, prefsPtr);
253 (*lz4fWrite)->dstBuf = (LZ4_byte*)malloc((*lz4fWrite)->dstBufMaxSize);
254 if ((*lz4fWrite)->dstBuf == NULL) {
255 LZ4F_freeAndNullWriteFile(lz4fWrite);
256 RETURN_ERROR(allocation_failed);
257 }
258
259 ret = LZ4F_createCompressionContext(&(*lz4fWrite)->cctxPtr, LZ4F_VERSION);
260 if (LZ4F_isError(ret)) {
261 LZ4F_freeAndNullWriteFile(lz4fWrite);
262 return ret;
263 }
264
265 ret = LZ4F_compressBegin((*lz4fWrite)->cctxPtr, buf, LZ4F_HEADER_SIZE_MAX, prefsPtr);
266 if (LZ4F_isError(ret)) {
267 LZ4F_freeAndNullWriteFile(lz4fWrite);
268 return ret;
269 }
270
271 if (ret != fwrite(buf, 1, ret, fp)) {
272 LZ4F_freeAndNullWriteFile(lz4fWrite);
273 RETURN_ERROR(io_write);
274 }
275
276 (*lz4fWrite)->fp = fp;
277 (*lz4fWrite)->errCode = LZ4F_OK_NoError;
278 return LZ4F_OK_NoError;
279 }
280
LZ4F_write(LZ4_writeFile_t * lz4fWrite,const void * buf,size_t size)281 size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, const void* buf, size_t size)
282 {
283 const LZ4_byte* p = (const LZ4_byte*)buf;
284 size_t remain = size;
285 size_t chunk;
286 size_t ret;
287
288 if (lz4fWrite == NULL || buf == NULL)
289 RETURN_ERROR(parameter_null);
290 while (remain) {
291 if (remain > lz4fWrite->maxWriteSize)
292 chunk = lz4fWrite->maxWriteSize;
293 else
294 chunk = remain;
295
296 ret = LZ4F_compressUpdate(lz4fWrite->cctxPtr,
297 lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize,
298 p, chunk,
299 NULL);
300 if (LZ4F_isError(ret)) {
301 lz4fWrite->errCode = ret;
302 return ret;
303 }
304
305 if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) {
306 lz4fWrite->errCode = returnErrorCode(LZ4F_ERROR_io_write);
307 RETURN_ERROR(io_write);
308 }
309
310 p += chunk;
311 remain -= chunk;
312 }
313
314 return size;
315 }
316
LZ4F_writeClose(LZ4_writeFile_t * lz4fWrite)317 LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite)
318 {
319 LZ4F_errorCode_t ret = LZ4F_OK_NoError;
320
321 if (lz4fWrite == NULL) {
322 RETURN_ERROR(parameter_null);
323 }
324
325 if (lz4fWrite->errCode == LZ4F_OK_NoError) {
326 ret = LZ4F_compressEnd(lz4fWrite->cctxPtr,
327 lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize,
328 NULL);
329 if (LZ4F_isError(ret)) {
330 goto out;
331 }
332
333 if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) {
334 ret = returnErrorCode(LZ4F_ERROR_io_write);
335 }
336 }
337
338 out:
339 LZ4F_freeWriteFile(lz4fWrite);
340 return ret;
341 }
342