blob: 003e1bd51f3accf70dd3b34b9946ef91764350f5 [file] [log] [blame]
Yann Collet4856a002015-01-24 01:58:16 +01001/*
Yann Collet44886612016-02-11 04:17:50 +01002 fileio.c - File i/o handler for zstd
3 Copyright (C) Yann Collet 2013-2016
Yann Collet4856a002015-01-24 01:58:16 +01004
5 GPL v2 License
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 You can contact the author at :
Yann Collet44886612016-02-11 04:17:50 +010022 - zstd homepage : http://www.zstd.net
Yann Collet4856a002015-01-24 01:58:16 +010023*/
24/*
25 Note : this is stand-alone program.
26 It is not part of ZSTD compression library, it is a user program of ZSTD library.
27 The license of ZSTD library is BSD.
28 The license of this file is GPLv2.
29*/
30
Yann Colleteeb8ba12015-10-22 16:55:40 +010031/* *************************************
Yann Colletb1f3f4b2015-10-18 22:18:32 +010032* Tuning options
Yann Colleteeb8ba12015-10-22 16:55:40 +010033***************************************/
Yann Colletb1f3f4b2015-10-18 22:18:32 +010034#ifndef ZSTD_LEGACY_SUPPORT
Yann Collet44886612016-02-11 04:17:50 +010035/* LEGACY_SUPPORT :
Yann Colletb1f3f4b2015-10-18 22:18:32 +010036* decompressor can decode older formats (starting from Zstd 0.1+) */
37# define ZSTD_LEGACY_SUPPORT 1
Yann Collet9f432922015-11-09 17:42:17 +010038#endif
Yann Colletb1f3f4b2015-10-18 22:18:32 +010039
40
Yann Colleteeb8ba12015-10-22 16:55:40 +010041/* *************************************
Yann Collet4856a002015-01-24 01:58:16 +010042* Compiler Options
Yann Colleteeb8ba12015-10-22 16:55:40 +010043***************************************/
Yann Collet4856a002015-01-24 01:58:16 +010044/* Disable some Visual warning messages */
45#ifdef _MSC_VER
46# define _CRT_SECURE_NO_WARNINGS
47# define _CRT_SECURE_NO_DEPRECATE /* VS2005 */
48# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
49#endif
50
Yann Collet4856a002015-01-24 01:58:16 +010051#define _FILE_OFFSET_BITS 64 /* Large file support on 32-bits unix */
52#define _POSIX_SOURCE 1 /* enable fileno() within <stdio.h> on unix */
53
54
Yann Collet6f3acba2016-02-12 20:19:48 +010055/*-*************************************
Yann Collet4856a002015-01-24 01:58:16 +010056* Includes
Yann Colleteeb8ba12015-10-22 16:55:40 +010057***************************************/
Yann Collet9f432922015-11-09 17:42:17 +010058#include <stdio.h> /* fprintf, fopen, fread, _fileno, stdin, stdout */
59#include <stdlib.h> /* malloc, free */
60#include <string.h> /* strcmp, strlen */
61#include <time.h> /* clock */
62#include <errno.h> /* errno */
63#include <sys/types.h> /* stat64 */
64#include <sys/stat.h> /* stat64 */
Yann Colletb1f3f4b2015-10-18 22:18:32 +010065#include "mem.h"
Yann Collet4856a002015-01-24 01:58:16 +010066#include "fileio.h"
Yann Collet88fcd292015-11-25 14:42:45 +010067#include "zstd_static.h" /* ZSTD_magicNumber */
Yann Collet62ae5fb2016-02-12 18:59:11 +010068#include "zbuff_static.h"
Yann Collet4856a002015-01-24 01:58:16 +010069
Yann Colletb1f3f4b2015-10-18 22:18:32 +010070#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1)
Yann Collet6f3acba2016-02-12 20:19:48 +010071# include "zstd_legacy.h" /* ZSTD_isLegacy */
72# include "fileio_legacy.h" /* FIO_decompressLegacyFrame */
Yann Colleteeb8ba12015-10-22 16:55:40 +010073#endif
Yann Colletb1f3f4b2015-10-18 22:18:32 +010074
Yann Collet4856a002015-01-24 01:58:16 +010075
Yann Collet6f3acba2016-02-12 20:19:48 +010076/*-*************************************
Yann Collet4856a002015-01-24 01:58:16 +010077* OS-specific Includes
Yann Colleteeb8ba12015-10-22 16:55:40 +010078***************************************/
Yann Collet4856a002015-01-24 01:58:16 +010079#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
80# include <fcntl.h> /* _O_BINARY */
81# include <io.h> /* _setmode, _isatty */
Yann Colletb5e06dc2015-07-04 23:20:56 -080082# define SET_BINARY_MODE(file) { int unused = _setmode(_fileno(file), _O_BINARY); (void)unused; }
Yann Collet4856a002015-01-24 01:58:16 +010083# define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream))
84#else
85# include <unistd.h> /* isatty */
86# define SET_BINARY_MODE(file)
87# define IS_CONSOLE(stdStream) isatty(fileno(stdStream))
88#endif
89
Yann Colletb7d6e8f2015-11-09 18:20:39 +010090#if !defined(S_ISREG)
91# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
92#endif
93
Yann Collet4856a002015-01-24 01:58:16 +010094
Yann Collet6f3acba2016-02-12 20:19:48 +010095/*-*************************************
Yann Collet4856a002015-01-24 01:58:16 +010096* Constants
Yann Colleteeb8ba12015-10-22 16:55:40 +010097***************************************/
Yann Collet4856a002015-01-24 01:58:16 +010098#define KB *(1U<<10)
99#define MB *(1U<<20)
100#define GB *(1U<<30)
101
102#define _1BIT 0x01
103#define _2BITS 0x03
104#define _3BITS 0x07
105#define _4BITS 0x0F
106#define _6BITS 0x3F
107#define _8BITS 0xFF
108
109#define BIT6 0x40
110#define BIT7 0x80
111
Yann Collet88fcd292015-11-25 14:42:45 +0100112#define BLOCKSIZE (128 KB)
113#define ROLLBUFFERSIZE (BLOCKSIZE*8*64)
Yann Collet4856a002015-01-24 01:58:16 +0100114
Yann Collet6f3acba2016-02-12 20:19:48 +0100115#define FIO_FRAMEHEADERSIZE 5 /* as a define, because needed to allocated table on stack */
116#define FSE_CHECKSUM_SEED 0
Yann Collet4856a002015-01-24 01:58:16 +0100117
118#define CACHELINE 64
119
Yann Collet6f3acba2016-02-12 20:19:48 +0100120#define MAX_DICT_SIZE (1 MB) /* protection against large input (attack scenario) ; can be changed */
121
Yann Collet4856a002015-01-24 01:58:16 +0100122
Yann Colleteeb8ba12015-10-22 16:55:40 +0100123/* *************************************
Yann Collet4856a002015-01-24 01:58:16 +0100124* Macros
Yann Colleteeb8ba12015-10-22 16:55:40 +0100125***************************************/
Yann Collet4856a002015-01-24 01:58:16 +0100126#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
127#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
128static U32 g_displayLevel = 2; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */
129
130#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
131 if ((FIO_GetMilliSpan(g_time) > refreshRate) || (g_displayLevel>=4)) \
132 { g_time = clock(); DISPLAY(__VA_ARGS__); \
133 if (g_displayLevel>=4) fflush(stdout); } }
134static const unsigned refreshRate = 150;
135static clock_t g_time = 0;
136
137
Yann Colleteeb8ba12015-10-22 16:55:40 +0100138/* *************************************
Yann Collet4856a002015-01-24 01:58:16 +0100139* Local Parameters
Yann Colleteeb8ba12015-10-22 16:55:40 +0100140***************************************/
Yann Collet4856a002015-01-24 01:58:16 +0100141static U32 g_overwrite = 0;
Yann Collet4856a002015-01-24 01:58:16 +0100142void FIO_overwriteMode(void) { g_overwrite=1; }
143void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; }
144
145
Yann Colleteeb8ba12015-10-22 16:55:40 +0100146/* *************************************
Yann Collet4856a002015-01-24 01:58:16 +0100147* Exceptions
Yann Colleteeb8ba12015-10-22 16:55:40 +0100148***************************************/
149#ifndef DEBUG
150# define DEBUG 0
151#endif
Yann Collet4856a002015-01-24 01:58:16 +0100152#define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
153#define EXM_THROW(error, ...) \
154{ \
155 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
156 DISPLAYLEVEL(1, "Error %i : ", error); \
157 DISPLAYLEVEL(1, __VA_ARGS__); \
158 DISPLAYLEVEL(1, "\n"); \
159 exit(error); \
160}
161
162
Yann Colleteeb8ba12015-10-22 16:55:40 +0100163/* *************************************
Yann Collet4856a002015-01-24 01:58:16 +0100164* Functions
Yann Colleteeb8ba12015-10-22 16:55:40 +0100165***************************************/
Yann Collet4856a002015-01-24 01:58:16 +0100166static unsigned FIO_GetMilliSpan(clock_t nPrevious)
167{
168 clock_t nCurrent = clock();
169 unsigned nSpan = (unsigned)(((nCurrent - nPrevious) * 1000) / CLOCKS_PER_SEC);
170 return nSpan;
171}
172
173
Yann Collet9f432922015-11-09 17:42:17 +0100174static U64 FIO_getFileSize(const char* infilename)
175{
176 int r;
177#if defined(_MSC_VER)
178 struct _stat64 statbuf;
179 r = _stat64(infilename, &statbuf);
180#else
181 struct stat statbuf;
182 r = stat(infilename, &statbuf);
183#endif
184 if (r || !S_ISREG(statbuf.st_mode)) return 0;
185 return (U64)statbuf.st_size;
186}
187
188
Yann Colletf0624362016-02-12 15:56:46 +0100189static FILE* FIO_openSrcFile(const char* srcFileName)
190{
191 FILE* f;
192
193 if (!strcmp (srcFileName, stdinmark)) {
194 DISPLAYLEVEL(4,"Using stdin for input\n");
195 f = stdin;
196 SET_BINARY_MODE(stdin);
197 } else {
198 f = fopen(srcFileName, "rb");
199 }
200
201 if ( f==NULL ) DISPLAYLEVEL(1, "zstd: %s: No such file\n", srcFileName);
202
203 return f;
204}
205
206
207static FILE* FIO_openDstFile(const char* dstFileName)
208{
209 FILE* f;
210
211 if (!strcmp (dstFileName, stdoutmark)) {
212 DISPLAYLEVEL(4,"Using stdout for output\n");
213 f = stdout;
214 SET_BINARY_MODE(stdout);
215 } else {
216 if (!g_overwrite) { /* Check if destination file already exists */
217 f = fopen( dstFileName, "rb" );
218 if (f != 0) { /* dest file exists, prompt for overwrite authorization */
219 fclose(f);
220 if (g_displayLevel <= 1) {
221 /* No interaction possible */
222 DISPLAY("zstd: %s already exists; not overwritten \n", dstFileName);
223 return 0;
224 }
225 DISPLAY("zstd: %s already exists; do you wish to overwrite (y/N) ? ", dstFileName);
226 {
227 int ch = getchar();
228 if ((ch!='Y') && (ch!='y')) {
229 DISPLAY(" not overwritten \n");
230 return 0;
231 }
232 while ((ch!=EOF) && (ch!='\n')) ch = getchar(); /* flush rest of input line */
233 } } }
234 f = fopen( dstFileName, "wb" );
235 }
236 return f;
237}
238
239
Yann Colletdeb078b2015-12-17 20:30:14 +0100240/*!FIO_loadFile
241* creates a buffer, pointed by *bufferPtr,
242* loads "filename" content into it
243* up to MAX_DICT_SIZE bytes
244*/
245static size_t FIO_loadFile(void** bufferPtr, const char* fileName)
246{
247 FILE* fileHandle;
248 size_t readSize;
249 U64 fileSize;
250
251 *bufferPtr = NULL;
Yann Collet2ce49232016-02-02 14:36:49 +0100252 if (fileName == NULL) return 0;
Yann Colletdeb078b2015-12-17 20:30:14 +0100253
254 DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
255 fileHandle = fopen(fileName, "rb");
256 if (fileHandle==0) EXM_THROW(31, "Error opening file %s", fileName);
257 fileSize = FIO_getFileSize(fileName);
Yann Collet2ce49232016-02-02 14:36:49 +0100258 if (fileSize > MAX_DICT_SIZE) {
Yann Colletdeb078b2015-12-17 20:30:14 +0100259 int seekResult;
260 if (fileSize > 1 GB) EXM_THROW(32, "Dictionary file %s is too large", fileName); /* avoid extreme cases */
261 DISPLAYLEVEL(2,"Dictionary %s is too large : using last %u bytes only \n", fileName, MAX_DICT_SIZE);
262 seekResult = fseek(fileHandle, (long int)(fileSize-MAX_DICT_SIZE), SEEK_SET); /* use end of file */
263 if (seekResult != 0) EXM_THROW(33, "Error seeking into file %s", fileName);
264 fileSize = MAX_DICT_SIZE;
265 }
266 *bufferPtr = (BYTE*)malloc((size_t)fileSize);
267 if (*bufferPtr==NULL) EXM_THROW(34, "Allocation error : not enough memory for dictBuffer");
268 readSize = fread(*bufferPtr, 1, (size_t)fileSize, fileHandle);
269 if (readSize!=fileSize) EXM_THROW(35, "Error reading dictionary file %s", fileName);
270 fclose(fileHandle);
271 return (size_t)fileSize;
272}
273
Yann Collet4f137032015-12-17 02:23:58 +0100274
275/* **********************************************************************
276* Compression
277************************************************************************/
278typedef struct {
279 void* srcBuffer;
280 size_t srcBufferSize;
281 void* dstBuffer;
282 size_t dstBufferSize;
283 void* dictBuffer;
284 size_t dictBufferSize;
285 ZBUFF_CCtx* ctx;
Yann Colletf0624362016-02-12 15:56:46 +0100286 FILE* dstFile;
Yann Collet4f137032015-12-17 02:23:58 +0100287} cRess_t;
288
289static cRess_t FIO_createCResources(const char* dictFileName)
290{
291 cRess_t ress;
292
293 ress.ctx = ZBUFF_createCCtx();
294 if (ress.ctx == NULL) EXM_THROW(30, "Allocation error : can't create ZBUFF context");
295
296 /* Allocate Memory */
297 ress.srcBufferSize = ZBUFF_recommendedCInSize();
298 ress.srcBuffer = malloc(ress.srcBufferSize);
299 ress.dstBufferSize = ZBUFF_recommendedCOutSize();
300 ress.dstBuffer = malloc(ress.dstBufferSize);
301 if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(31, "Allocation error : not enough memory");
302
303 /* dictionary */
Yann Colletdeb078b2015-12-17 20:30:14 +0100304 ress.dictBufferSize = FIO_loadFile(&(ress.dictBuffer), dictFileName);
Yann Collet4f137032015-12-17 02:23:58 +0100305
306 return ress;
307}
308
309static void FIO_freeCResources(cRess_t ress)
310{
311 size_t errorCode;
312 free(ress.srcBuffer);
313 free(ress.dstBuffer);
314 free(ress.dictBuffer);
315 errorCode = ZBUFF_freeCCtx(ress.ctx);
316 if (ZBUFF_isError(errorCode)) EXM_THROW(38, "Error : can't release ZBUFF context resource : %s", ZBUFF_getErrorName(errorCode));
317}
318
319
Yann Colletf0624362016-02-12 15:56:46 +0100320/*! FIO_compressFilename_internal() :
321 * same as FIO_compressFilename_extRess(), with ress.desFile already opened
322 * @return : 0 : compression completed correctly,
323 * 1 : missing or pb opening srcFileName
Yann Collet4f137032015-12-17 02:23:58 +0100324 */
Yann Colletf0624362016-02-12 15:56:46 +0100325static int FIO_compressFilename_internal(cRess_t ress,
326 const char* dstFileName, const char* srcFileName,
327 int cLevel)
Yann Collet4f137032015-12-17 02:23:58 +0100328{
329 FILE* srcFile;
Yann Colletf0624362016-02-12 15:56:46 +0100330 FILE* dstFile = ress.dstFile;
Yann Collet4f137032015-12-17 02:23:58 +0100331 U64 filesize = 0;
332 U64 compressedfilesize = 0;
333 size_t dictSize = ress.dictBufferSize;
334 size_t sizeCheck, errorCode;
335
336 /* File check */
Yann Colletf0624362016-02-12 15:56:46 +0100337 srcFile = FIO_openSrcFile(srcFileName);
338 if (!srcFile) return 1; /* srcFile could not be opened */
Yann Collet4f137032015-12-17 02:23:58 +0100339
340 /* init */
341 filesize = FIO_getFileSize(srcFileName) + dictSize;
Yann Collet1c8e1942016-01-26 16:31:22 +0100342 errorCode = ZBUFF_compressInit_advanced(ress.ctx, ress.dictBuffer, ress.dictBufferSize, ZSTD_getParams(cLevel, filesize));
343 if (ZBUFF_isError(errorCode)) EXM_THROW(21, "Error initializing compression : %s", ZBUFF_getErrorName(errorCode));
Yann Collet4f137032015-12-17 02:23:58 +0100344
345 /* Main compression loop */
346 filesize = 0;
Yann Collet1c8e1942016-01-26 16:31:22 +0100347 while (1) {
Yann Collet4f137032015-12-17 02:23:58 +0100348 /* Fill input Buffer */
Yann Collet1c8e1942016-01-26 16:31:22 +0100349 size_t inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile);
Yann Collet4f137032015-12-17 02:23:58 +0100350 if (inSize==0) break;
351 filesize += inSize;
352 DISPLAYUPDATE(2, "\rRead : %u MB ", (U32)(filesize>>20));
353
Yann Colletf0624362016-02-12 15:56:46 +0100354 { /* Compress using buffered streaming */
Yann Collet4f137032015-12-17 02:23:58 +0100355 size_t usedInSize = inSize;
356 size_t cSize = ress.dstBufferSize;
357 size_t result = ZBUFF_compressContinue(ress.ctx, ress.dstBuffer, &cSize, ress.srcBuffer, &usedInSize);
358 if (ZBUFF_isError(result))
359 EXM_THROW(23, "Compression error : %s ", ZBUFF_getErrorName(result));
360 if (inSize != usedInSize)
361 /* inBuff should be entirely consumed since buffer sizes are recommended ones */
362 EXM_THROW(24, "Compression error : input block not fully consumed");
363
364 /* Write cBlock */
365 sizeCheck = fwrite(ress.dstBuffer, 1, cSize, dstFile);
366 if (sizeCheck!=cSize) EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName);
367 compressedfilesize += cSize;
368 }
Yann Collet4f137032015-12-17 02:23:58 +0100369 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ", (U32)(filesize>>20), (double)compressedfilesize/filesize*100);
370 }
371
372 /* End of Frame */
373 {
374 size_t cSize = ress.dstBufferSize;
375 size_t result = ZBUFF_compressEnd(ress.ctx, ress.dstBuffer, &cSize);
376 if (result!=0) EXM_THROW(26, "Compression error : cannot create frame end");
377
378 sizeCheck = fwrite(ress.dstBuffer, 1, cSize, dstFile);
379 if (sizeCheck!=cSize) EXM_THROW(27, "Write error : cannot write frame end into %s", dstFileName);
380 compressedfilesize += cSize;
381 }
382
383 /* Status */
384 DISPLAYLEVEL(2, "\r%79s\r", "");
385 DISPLAYLEVEL(2,"Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
386 (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);
387
388 /* clean */
389 fclose(srcFile);
Yann Collet4f137032015-12-17 02:23:58 +0100390
391 return 0;
392}
393
394
Yann Colletf0624362016-02-12 15:56:46 +0100395/*! FIO_compressFilename_extRess() :
396 * @return : 0 : compression completed correctly,
397 * 1 : missing or pb opening srcFileName
398 */
399static int FIO_compressFilename_extRess(cRess_t ress,
400 const char* dstFileName, const char* srcFileName,
401 int cLevel)
402{
403 int result;
404
405 ress.dstFile = FIO_openDstFile(dstFileName);
406 if (ress.dstFile==0) return 1;
407
408 result = FIO_compressFilename_internal(ress, dstFileName, srcFileName, cLevel);
409
410 if (fclose(ress.dstFile)) EXM_THROW(28, "Write error : cannot properly close %s", dstFileName);
411 return result;
412}
413
414
Yann Collet9d909222015-12-17 14:09:55 +0100415int FIO_compressFilename(const char* dstFileName, const char* srcFileName,
416 const char* dictFileName, int compressionLevel)
Yann Collet4856a002015-01-24 01:58:16 +0100417{
Yann Collet9d909222015-12-17 14:09:55 +0100418 clock_t start, end;
419 cRess_t ress;
420 int issueWithSrcFile = 0;
Yann Collet88fcd292015-11-25 14:42:45 +0100421
Yann Collet9d909222015-12-17 14:09:55 +0100422 /* Init */
423 start = clock();
424 ress = FIO_createCResources(dictFileName);
Yann Colletf6f3d752015-12-13 13:35:21 +0100425
Yann Collet9d909222015-12-17 14:09:55 +0100426 /* Compress File */
427 issueWithSrcFile += FIO_compressFilename_extRess(ress, dstFileName, srcFileName, compressionLevel);
428
429 /* Free resources */
430 FIO_freeCResources(ress);
431
432 /* Final Status */
433 end = clock();
Yann Colletf6f3d752015-12-13 13:35:21 +0100434 {
Yann Collet9d909222015-12-17 14:09:55 +0100435 double seconds = (double)(end - start) / CLOCKS_PER_SEC;
436 DISPLAYLEVEL(4, "Completed in %.2f sec \n", seconds);
Yann Colletf6f3d752015-12-13 13:35:21 +0100437 }
Yann Collet4856a002015-01-24 01:58:16 +0100438
Yann Collet9d909222015-12-17 14:09:55 +0100439 return issueWithSrcFile;
Yann Collet4856a002015-01-24 01:58:16 +0100440}
441
442
Yann Collet4f137032015-12-17 02:23:58 +0100443#define FNSPACE 30
Yann Collet9d909222015-12-17 14:09:55 +0100444int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFiles,
Yann Collet4f137032015-12-17 02:23:58 +0100445 const char* suffix,
446 const char* dictFileName, int compressionLevel)
447{
Yann Collet9d909222015-12-17 14:09:55 +0100448 unsigned u;
Yann Collet4f137032015-12-17 02:23:58 +0100449 int missed_files = 0;
450 char* dstFileName = (char*)malloc(FNSPACE);
451 size_t dfnSize = FNSPACE;
Yann Colletf0624362016-02-12 15:56:46 +0100452 const size_t suffixSize = suffix ? strlen(suffix) : 0;
Yann Collet4f137032015-12-17 02:23:58 +0100453 cRess_t ress;
454
455 /* init */
456 ress = FIO_createCResources(dictFileName);
457
458 /* loop on each file */
Yann Colletf0624362016-02-12 15:56:46 +0100459 if (suffix) {
460 for (u=0; u<nbFiles; u++) {
461 size_t ifnSize = strlen(inFileNamesTable[u]);
462 if (dfnSize <= ifnSize+suffixSize+1) { free(dstFileName); dfnSize = ifnSize + 20; dstFileName = (char*)malloc(dfnSize); }
463 strcpy(dstFileName, inFileNamesTable[u]);
464 strcat(dstFileName, suffix);
465 missed_files += FIO_compressFilename_extRess(ress, dstFileName,
466 inFileNamesTable[u], compressionLevel);
467 }
468 } else {
469 ress.dstFile = stdout;
470 for (u=0; u<nbFiles; u++)
471 missed_files += FIO_compressFilename_internal(ress, stdoutmark,
472 inFileNamesTable[u], compressionLevel);
473 if (fclose(ress.dstFile)) EXM_THROW(29, "Write error : cannot properly close %s", stdoutmark);
Yann Collet4f137032015-12-17 02:23:58 +0100474 }
475
476 /* Close & Free */
477 FIO_freeCResources(ress);
478 free(dstFileName);
479
480 return missed_files;
481}
482
483
Yann Collet4f137032015-12-17 02:23:58 +0100484/* **************************************************************************
485* Decompression
486****************************************************************************/
Yann Colletdeb078b2015-12-17 20:30:14 +0100487typedef struct {
488 void* srcBuffer;
489 size_t srcBufferSize;
490 void* dstBuffer;
491 size_t dstBufferSize;
492 void* dictBuffer;
493 size_t dictBufferSize;
494 ZBUFF_DCtx* dctx;
Yann Collet1f1f2392016-02-12 18:33:26 +0100495 FILE* dstFile;
Yann Colletdeb078b2015-12-17 20:30:14 +0100496} dRess_t;
497
498static dRess_t FIO_createDResources(const char* dictFileName)
499{
500 dRess_t ress;
501
502 /* init */
503 ress.dctx = ZBUFF_createDCtx();
504 if (ress.dctx==NULL) EXM_THROW(60, "Can't create ZBUFF decompression context");
505
506 /* Allocate Memory */
507 ress.srcBufferSize = ZBUFF_recommendedDInSize();
508 ress.srcBuffer = malloc(ress.srcBufferSize);
509 ress.dstBufferSize = ZBUFF_recommendedDOutSize();
510 ress.dstBuffer = malloc(ress.dstBufferSize);
511 if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(61, "Allocation error : not enough memory");
512
513 /* dictionary */
514 ress.dictBufferSize = FIO_loadFile(&(ress.dictBuffer), dictFileName);
515
516 return ress;
517}
518
519static void FIO_freeDResources(dRess_t ress)
520{
521 size_t errorCode = ZBUFF_freeDCtx(ress.dctx);
522 if (ZBUFF_isError(errorCode)) EXM_THROW(69, "Error : can't free ZBUFF context resource : %s", ZBUFF_getErrorName(errorCode));
523 free(ress.srcBuffer);
524 free(ress.dstBuffer);
525 free(ress.dictBuffer);
526}
Yann Collet4f137032015-12-17 02:23:58 +0100527
528
Yann Colletdeb078b2015-12-17 20:30:14 +0100529unsigned long long FIO_decompressFrame(dRess_t ress,
530 FILE* foutput, FILE* finput, size_t alreadyLoaded)
Yann Collet4856a002015-01-24 01:58:16 +0100531{
Yann Collet88fcd292015-11-25 14:42:45 +0100532 U64 frameSize = 0;
533 size_t readSize=alreadyLoaded;
Yann Collet4856a002015-01-24 01:58:16 +0100534
Yann Collet4856a002015-01-24 01:58:16 +0100535 /* Main decompression Loop */
Yann Collet7b51a292016-01-26 15:58:49 +0100536 ZBUFF_decompressInitDictionary(ress.dctx, ress.dictBuffer, ress.dictBufferSize);
Yann Collet2ce49232016-02-02 14:36:49 +0100537 while (1) {
Yann Collet88fcd292015-11-25 14:42:45 +0100538 /* Decode */
539 size_t sizeCheck;
Yann Colletdeb078b2015-12-17 20:30:14 +0100540 size_t inSize=readSize, decodedSize=ress.dstBufferSize;
541 size_t toRead = ZBUFF_decompressContinue(ress.dctx, ress.dstBuffer, &decodedSize, ress.srcBuffer, &inSize);
Yann Collet88fcd292015-11-25 14:42:45 +0100542 if (ZBUFF_isError(toRead)) EXM_THROW(36, "Decoding error : %s", ZBUFF_getErrorName(toRead));
Yann Collet88fcd292015-11-25 14:42:45 +0100543 readSize -= inSize;
Yann Collet88fcd292015-11-25 14:42:45 +0100544
545 /* Write block */
Yann Colletdeb078b2015-12-17 20:30:14 +0100546 sizeCheck = fwrite(ress.dstBuffer, 1, decodedSize, foutput);
Yann Collet88fcd292015-11-25 14:42:45 +0100547 if (sizeCheck != decodedSize) EXM_THROW(37, "Write error : unable to write data block to destination file");
548 frameSize += decodedSize;
549 DISPLAYUPDATE(2, "\rDecoded : %u MB... ", (U32)(frameSize>>20) );
550
Yann Collet31d18062015-11-27 14:07:36 +0100551 if (toRead == 0) break;
Yann Colletdeb078b2015-12-17 20:30:14 +0100552 if (readSize) EXM_THROW(38, "Decoding error : should consume entire input");
Yann Collet4856a002015-01-24 01:58:16 +0100553
554 /* Fill input buffer */
Yann Colletdeb078b2015-12-17 20:30:14 +0100555 if (toRead > ress.srcBufferSize) EXM_THROW(34, "too large block");
556 readSize = fread(ress.srcBuffer, 1, toRead, finput);
Yann Collet88fcd292015-11-25 14:42:45 +0100557 if (readSize != toRead) EXM_THROW(35, "Read error");
Yann Collet4856a002015-01-24 01:58:16 +0100558 }
559
Yann Collet88fcd292015-11-25 14:42:45 +0100560 return frameSize;
Yann Colletbe50aaa2015-09-10 23:26:09 +0100561}
562
563
Yann Collet1f1f2392016-02-12 18:33:26 +0100564/** FIO_decompressSrcFile() :
565 Decompression `srcFileName` into `ress.dstFile`
566 @return : 0 : OK
567 1 : operation not started
568*/
569static int FIO_decompressSrcFile(dRess_t ress, const char* srcFileName)
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100570{
Yann Colletdeb078b2015-12-17 20:30:14 +0100571 unsigned long long filesize = 0;
Yann Collet1f1f2392016-02-12 18:33:26 +0100572 FILE* dstFile = ress.dstFile;
573 FILE* srcFile = FIO_openSrcFile(srcFileName);
574 if (srcFile==0) return 1;
Yann Collet88fcd292015-11-25 14:42:45 +0100575
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100576 /* for each frame */
Yann Collet2ce49232016-02-02 14:36:49 +0100577 for ( ; ; ) {
Yann Colleta85a8dd2015-11-30 11:53:11 +0100578 size_t sizeCheck;
579 /* check magic number -> version */
Yann Colletdeb078b2015-12-17 20:30:14 +0100580 size_t toRead = 4;
581 sizeCheck = fread(ress.srcBuffer, (size_t)1, toRead, srcFile);
Yann Colleta85a8dd2015-11-30 11:53:11 +0100582 if (sizeCheck==0) break; /* no more input */
Yann Collet1f1f2392016-02-12 18:33:26 +0100583 if (sizeCheck != toRead) EXM_THROW(31, "zstd: %s read error : cannot read header", srcFileName);
Yann Collet88fcd292015-11-25 14:42:45 +0100584#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1)
Yann Collet2ce49232016-02-02 14:36:49 +0100585 if (ZSTD_isLegacy(MEM_readLE32(ress.srcBuffer))) {
Yann Colletdeb078b2015-12-17 20:30:14 +0100586 filesize += FIO_decompressLegacyFrame(dstFile, srcFile, MEM_readLE32(ress.srcBuffer));
Yann Colleta85a8dd2015-11-30 11:53:11 +0100587 continue;
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100588 }
Yann Colletaa074052015-10-30 11:21:50 +0100589#endif /* ZSTD_LEGACY_SUPPORT */
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100590
Yann Colletdeb078b2015-12-17 20:30:14 +0100591 filesize += FIO_decompressFrame(ress, dstFile, srcFile, toRead);
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100592 }
593
Yann Colletdeb078b2015-12-17 20:30:14 +0100594 /* Final Status */
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100595 DISPLAYLEVEL(2, "\r%79s\r", "");
Yann Colletdeb078b2015-12-17 20:30:14 +0100596 DISPLAYLEVEL(2, "Successfully decoded %llu bytes \n", filesize);
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100597
Yann Colletdeb078b2015-12-17 20:30:14 +0100598 /* Close */
599 fclose(srcFile);
Yann Collet1f1f2392016-02-12 18:33:26 +0100600 return 0;
601}
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100602
Yann Collet1f1f2392016-02-12 18:33:26 +0100603
604/** FIO_decompressFile_extRess() :
605 decompress `srcFileName` into `dstFileName`
606 @return : 0 : OK
607 1 : operation aborted (src not available, dst already taken, etc.)
608*/
609static int FIO_decompressFile_extRess(dRess_t ress,
610 const char* dstFileName, const char* srcFileName)
611{
612 ress.dstFile = FIO_openDstFile(dstFileName);
613 if (ress.dstFile==0) return 1;
614
615 FIO_decompressSrcFile(ress, srcFileName);
616
617 if (fclose(ress.dstFile)) EXM_THROW(38, "Write error : cannot properly close %s", dstFileName);
Yann Colletdeb078b2015-12-17 20:30:14 +0100618 return 0;
Yann Colletb1f3f4b2015-10-18 22:18:32 +0100619}
620
621
Yann Colletdeb078b2015-12-17 20:30:14 +0100622int FIO_decompressFilename(const char* dstFileName, const char* srcFileName,
623 const char* dictFileName)
624{
625 int missingFiles = 0;
626 dRess_t ress = FIO_createDResources(dictFileName);
627
628 missingFiles += FIO_decompressFile_extRess(ress, dstFileName, srcFileName);
629
630 FIO_freeDResources(ress);
631 return missingFiles;
632}
633
634
635#define MAXSUFFIXSIZE 8
636int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles,
637 const char* suffix,
638 const char* dictFileName)
639{
640 unsigned u;
641 int skippedFiles = 0;
642 int missingFiles = 0;
643 char* dstFileName = (char*)malloc(FNSPACE);
644 size_t dfnSize = FNSPACE;
Yann Collet1f1f2392016-02-12 18:33:26 +0100645 const size_t suffixSize = suffix ? strlen(suffix) : 0;
Yann Colletdeb078b2015-12-17 20:30:14 +0100646 dRess_t ress;
647
648 if (dstFileName==NULL) EXM_THROW(70, "not enough memory for dstFileName");
649 ress = FIO_createDResources(dictFileName);
650
Yann Collet1f1f2392016-02-12 18:33:26 +0100651 if (suffix) {
652 for (u=0; u<nbFiles; u++) { /* create dstFileName */
653 const char* srcFileName = srcNamesTable[u];
654 size_t sfnSize = strlen(srcFileName);
655 const char* suffixPtr = srcFileName + sfnSize - suffixSize;
656 if (dfnSize <= sfnSize-suffixSize+1) { free(dstFileName); dfnSize = sfnSize + 20; dstFileName = (char*)malloc(dfnSize); if (dstFileName==NULL) EXM_THROW(71, "not enough memory for dstFileName"); }
657 if (sfnSize <= suffixSize || strcmp(suffixPtr, suffix) != 0) {
658 DISPLAYLEVEL(1, "zstd: %s: unknown suffix (%4s expected) -- ignored \n", srcFileName, suffix);
659 skippedFiles++;
660 continue;
661 }
662 memcpy(dstFileName, srcFileName, sfnSize - suffixSize);
663 dstFileName[sfnSize-suffixSize] = '\0';
Yann Colletdeb078b2015-12-17 20:30:14 +0100664
Yann Collet1f1f2392016-02-12 18:33:26 +0100665 missingFiles += FIO_decompressFile_extRess(ress, dstFileName, srcFileName);
666 }
667 } else {
668 ress.dstFile = stdout;
669 for (u=0; u<nbFiles; u++)
670 missingFiles += FIO_decompressSrcFile(ress, srcNamesTable[u]);
671 if (fclose(ress.dstFile)) EXM_THROW(39, "Write error : cannot properly close %s", stdoutmark);
Yann Colletdeb078b2015-12-17 20:30:14 +0100672 }
673
674 FIO_freeDResources(ress);
675 free(dstFileName);
676 return missingFiles + skippedFiles;
677}