]> git.proxmox.com Git - ceph.git/blame - ceph/src/zstd/programs/fileio.c
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / zstd / programs / fileio.c
CommitLineData
11fdf7f2 1/*
7c673cae
FG
2 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3 * All rights reserved.
4 *
11fdf7f2
TL
5 * This source code is licensed under both the BSD-style license (found in the
6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7 * in the COPYING file in the root directory of this source tree).
8 * You may select, at your option, one of the above-listed licenses.
7c673cae
FG
9 */
10
11fdf7f2 11
7c673cae
FG
12/* *************************************
13* Compiler Options
14***************************************/
15#ifdef _MSC_VER /* Visual */
11fdf7f2 16# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
7c673cae
FG
17# pragma warning(disable : 4204) /* non-constant aggregate initializer */
18#endif
19#if defined(__MINGW32__) && !defined(_POSIX_SOURCE)
20# define _POSIX_SOURCE 1 /* disable %llu warnings with MinGW on Windows */
21#endif
22
23
24/*-*************************************
25* Includes
26***************************************/
11fdf7f2
TL
27#include "platform.h" /* Large Files support, SET_BINARY_MODE */
28#include "util.h" /* UTIL_getFileSize */
7c673cae
FG
29#include <stdio.h> /* fprintf, fopen, fread, _fileno, stdin, stdout */
30#include <stdlib.h> /* malloc, free */
31#include <string.h> /* strcmp, strlen */
32#include <time.h> /* clock */
33#include <errno.h> /* errno */
34
11fdf7f2
TL
35#if defined (_MSC_VER)
36# include <sys/stat.h>
37# include <io.h>
38#endif
39
40#include "bitstream.h"
7c673cae
FG
41#include "mem.h"
42#include "fileio.h"
43#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
44#include "zstd.h"
11fdf7f2
TL
45#ifdef ZSTD_MULTITHREAD
46# include "zstdmt_compress.h"
7c673cae 47#endif
11fdf7f2
TL
48#if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
49# include <zlib.h>
50# if !defined(z_const)
51# define z_const
52# endif
53#endif
54#if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
55# include <lzma.h>
7c673cae
FG
56#endif
57
11fdf7f2
TL
58#define LZ4_MAGICNUMBER 0x184D2204
59#if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
60# define LZ4F_ENABLE_OBSOLETE_ENUMS
61# include <lz4frame.h>
62# include <lz4.h>
7c673cae
FG
63#endif
64
65
66/*-*************************************
67* Constants
68***************************************/
69#define KB *(1<<10)
70#define MB *(1<<20)
71#define GB *(1U<<30)
72
73#define _1BIT 0x01
74#define _2BITS 0x03
75#define _3BITS 0x07
76#define _4BITS 0x0F
77#define _6BITS 0x3F
78#define _8BITS 0xFF
79
80#define BLOCKSIZE (128 KB)
81#define ROLLBUFFERSIZE (BLOCKSIZE*8*64)
82
11fdf7f2 83#define FIO_FRAMEHEADERSIZE 5 /* as a define, because needed to allocated table on stack */
7c673cae 84
11fdf7f2 85#define DICTSIZE_MAX (32 MB) /* protection against large input (attack scenario) */
7c673cae
FG
86
87#define FNSPACE 30
7c673cae
FG
88
89
90/*-*************************************
91* Macros
92***************************************/
93#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
11fdf7f2
TL
94#define DISPLAYOUT(...) fprintf(stdout, __VA_ARGS__)
95#define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
96static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2: + result + interaction + warnings; 3: + progression; 4: + information */
7c673cae
FG
97void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; }
98
11fdf7f2 99#define DISPLAYUPDATE(l, ...) { if (g_displayLevel>=l) { \
7c673cae
FG
100 if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \
101 { g_time = clock(); DISPLAY(__VA_ARGS__); \
11fdf7f2 102 if (g_displayLevel>=4) fflush(stderr); } } }
7c673cae
FG
103static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100;
104static clock_t g_time = 0;
105
11fdf7f2 106#undef MIN /* in case it would be already defined */
7c673cae
FG
107#define MIN(a,b) ((a) < (b) ? (a) : (b))
108
109
11fdf7f2
TL
110/*-*************************************
111* Debug
112***************************************/
113#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1)
114# include <assert.h>
115#else
116# ifndef assert
117# define assert(condition) ((void)0)
118# endif
119#endif
120
121#ifndef ZSTD_DEBUG
122# define ZSTD_DEBUG 0
123#endif
124#define DEBUGLOG(l,...) if (l<=ZSTD_DEBUG) DISPLAY(__VA_ARGS__);
125#define EXM_THROW(error, ...) \
126{ \
127 DISPLAYLEVEL(1, "zstd: "); \
128 DEBUGLOG(1, "Error defined at %s, line %i : \n", __FILE__, __LINE__); \
129 DISPLAYLEVEL(1, "error %i : ", error); \
130 DISPLAYLEVEL(1, __VA_ARGS__); \
131 DISPLAYLEVEL(1, " \n"); \
132 exit(error); \
133}
134
135#define CHECK(f) { \
136 size_t const err = f; \
137 if (ZSTD_isError(err)) { \
138 DEBUGLOG(1, "%s \n", #f); \
139 EXM_THROW(11, "%s", ZSTD_getErrorName(err)); \
140} }
141
142
143/*-************************************
144* Signal (Ctrl-C trapping)
145**************************************/
146#include <signal.h>
147
148static const char* g_artefact = NULL;
149static void INThandler(int sig)
150{
151 assert(sig==SIGINT); (void)sig;
152#if !defined(_MSC_VER)
153 signal(sig, SIG_IGN); /* this invocation generates a buggy warning in Visual Studio */
154#endif
155 if (g_artefact) remove(g_artefact);
156 DISPLAY("\n");
157 exit(2);
158}
159
160
161/* ************************************************************
162* Avoid fseek()'s 2GiB barrier with MSVC, MacOS, *BSD, MinGW
163***************************************************************/
164#if defined(_MSC_VER) && _MSC_VER >= 1400
165# define LONG_SEEK _fseeki64
166#elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
167# define LONG_SEEK fseeko
168#elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
169# define LONG_SEEK fseeko64
170#elif defined(_WIN32) && !defined(__DJGPP__)
171# include <windows.h>
172 static int LONG_SEEK(FILE* file, __int64 offset, int origin) {
173 LARGE_INTEGER off;
174 DWORD method;
175 off.QuadPart = offset;
176 if (origin == SEEK_END)
177 method = FILE_END;
178 else if (origin == SEEK_CUR)
179 method = FILE_CURRENT;
180 else
181 method = FILE_BEGIN;
182
183 if (SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, NULL, method))
184 return 0;
185 else
186 return -1;
187 }
188#else
189# define LONG_SEEK fseek
190#endif
191
192
7c673cae
FG
193/*-*************************************
194* Local Parameters - Not thread safe
195***************************************/
11fdf7f2
TL
196static FIO_compressionType_t g_compressionType = FIO_zstdCompression;
197void FIO_setCompressionType(FIO_compressionType_t compressionType) { g_compressionType = compressionType; }
7c673cae
FG
198static U32 g_overwrite = 0;
199void FIO_overwriteMode(void) { g_overwrite=1; }
11fdf7f2 200static U32 g_sparseFileSupport = 1; /* 0: no sparse allowed; 1: auto (file yes, stdout no); 2: force sparse */
7c673cae
FG
201void FIO_setSparseWrite(unsigned sparse) { g_sparseFileSupport=sparse; }
202static U32 g_dictIDFlag = 1;
203void FIO_setDictIDFlag(unsigned dictIDFlag) { g_dictIDFlag = dictIDFlag; }
204static U32 g_checksumFlag = 1;
205void FIO_setChecksumFlag(unsigned checksumFlag) { g_checksumFlag = checksumFlag; }
206static U32 g_removeSrcFile = 0;
207void FIO_setRemoveSrcFile(unsigned flag) { g_removeSrcFile = (flag>0); }
208static U32 g_memLimit = 0;
209void FIO_setMemLimit(unsigned memLimit) { g_memLimit = memLimit; }
11fdf7f2
TL
210static U32 g_nbThreads = 1;
211void FIO_setNbThreads(unsigned nbThreads) {
212#ifndef ZSTD_MULTITHREAD
213 if (nbThreads > 1) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n");
214#endif
215 g_nbThreads = nbThreads;
216}
217static U32 g_blockSize = 0;
218void FIO_setBlockSize(unsigned blockSize) {
219 if (blockSize && g_nbThreads==1)
220 DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n");
221#ifdef ZSTD_MULTITHREAD
222 if (blockSize-1 < ZSTDMT_SECTION_SIZE_MIN-1) /* intentional underflow */
223 DISPLAYLEVEL(2, "Note : minimum block size is %u KB \n", (ZSTDMT_SECTION_SIZE_MIN>>10));
224#endif
225 g_blockSize = blockSize;
226}
227#define FIO_OVERLAP_LOG_NOTSET 9999
228static U32 g_overlapLog = FIO_OVERLAP_LOG_NOTSET;
229void FIO_setOverlapLog(unsigned overlapLog){
230 if (overlapLog && g_nbThreads==1)
231 DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n");
232 g_overlapLog = overlapLog;
233}
234static U32 g_ldmFlag = 0;
235void FIO_setLdmFlag(unsigned ldmFlag) {
236 g_ldmFlag = (ldmFlag>0);
237}
238static U32 g_ldmHashLog = 0;
239void FIO_setLdmHashLog(unsigned ldmHashLog) {
240 g_ldmHashLog = ldmHashLog;
241}
242static U32 g_ldmMinMatch = 0;
243void FIO_setLdmMinMatch(unsigned ldmMinMatch) {
244 g_ldmMinMatch = ldmMinMatch;
245}
7c673cae 246
11fdf7f2
TL
247#define FIO_LDM_PARAM_NOTSET 9999
248static U32 g_ldmBucketSizeLog = FIO_LDM_PARAM_NOTSET;
249void FIO_setLdmBucketSizeLog(unsigned ldmBucketSizeLog) {
250 g_ldmBucketSizeLog = ldmBucketSizeLog;
251}
7c673cae 252
11fdf7f2
TL
253static U32 g_ldmHashEveryLog = FIO_LDM_PARAM_NOTSET;
254void FIO_setLdmHashEveryLog(unsigned ldmHashEveryLog) {
255 g_ldmHashEveryLog = ldmHashEveryLog;
7c673cae
FG
256}
257
258
11fdf7f2 259
7c673cae
FG
260/*-*************************************
261* Functions
262***************************************/
11fdf7f2
TL
263/** FIO_remove() :
264 * @result : Unlink `fileName`, even if it's read-only */
265static int FIO_remove(const char* path)
266{
267#if defined(_WIN32) || defined(WIN32)
268 /* windows doesn't allow remove read-only files,
269 * so try to make it writable first */
270 chmod(path, _S_IWRITE);
271#endif
272 return remove(path);
273}
274
7c673cae
FG
275/** FIO_openSrcFile() :
276 * condition : `dstFileName` must be non-NULL.
277 * @result : FILE* to `dstFileName`, or NULL if it fails */
278static FILE* FIO_openSrcFile(const char* srcFileName)
279{
280 FILE* f;
281
282 if (!strcmp (srcFileName, stdinmark)) {
283 DISPLAYLEVEL(4,"Using stdin for input\n");
284 f = stdin;
285 SET_BINARY_MODE(stdin);
286 } else {
11fdf7f2
TL
287 if (!UTIL_isRegularFile(srcFileName)) {
288 DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n",
289 srcFileName);
290 return NULL;
291 }
7c673cae 292 f = fopen(srcFileName, "rb");
11fdf7f2
TL
293 if ( f==NULL )
294 DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
7c673cae
FG
295 }
296
297 return f;
298}
299
300/** FIO_openDstFile() :
301 * condition : `dstFileName` must be non-NULL.
302 * @result : FILE* to `dstFileName`, or NULL if it fails */
303static FILE* FIO_openDstFile(const char* dstFileName)
304{
305 FILE* f;
306
307 if (!strcmp (dstFileName, stdoutmark)) {
308 DISPLAYLEVEL(4,"Using stdout for output\n");
309 f = stdout;
310 SET_BINARY_MODE(stdout);
311 if (g_sparseFileSupport==1) {
312 g_sparseFileSupport = 0;
313 DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
314 }
315 } else {
11fdf7f2
TL
316 if (g_sparseFileSupport == 1) {
317 g_sparseFileSupport = ZSTD_SPARSE_DEFAULT;
318 }
319 if (strcmp (dstFileName, nulmark)) {
320 /* Check if destination file already exists */
7c673cae 321 f = fopen( dstFileName, "rb" );
11fdf7f2 322 if (f != 0) { /* dst file exists, prompt for overwrite authorization */
7c673cae 323 fclose(f);
11fdf7f2
TL
324 if (!g_overwrite) {
325 if (g_displayLevel <= 1) {
326 /* No interaction possible */
327 DISPLAY("zstd: %s already exists; not overwritten \n",
328 dstFileName);
7c673cae
FG
329 return NULL;
330 }
11fdf7f2
TL
331 DISPLAY("zstd: %s already exists; do you wish to overwrite (y/N) ? ",
332 dstFileName);
333 { int ch = getchar();
334 if ((ch!='Y') && (ch!='y')) {
335 DISPLAY(" not overwritten \n");
336 return NULL;
337 }
338 /* flush rest of input line */
339 while ((ch!=EOF) && (ch!='\n')) ch = getchar();
340 } }
341 /* need to unlink */
342 FIO_remove(dstFileName);
343 } }
7c673cae
FG
344 f = fopen( dstFileName, "wb" );
345 if (f==NULL) DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
346 }
347
348 return f;
349}
350
351
11fdf7f2
TL
352/*! FIO_createDictBuffer() :
353 * creates a buffer, pointed by `*bufferPtr`,
354 * loads `filename` content into it, up to DICTSIZE_MAX bytes.
355 * @return : loaded size
356 * if fileName==NULL, returns 0 and a NULL pointer
357 */
358static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName)
7c673cae
FG
359{
360 FILE* fileHandle;
361 U64 fileSize;
362
363 *bufferPtr = NULL;
364 if (fileName == NULL) return 0;
365
366 DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
367 fileHandle = fopen(fileName, "rb");
11fdf7f2 368 if (fileHandle==0) EXM_THROW(31, "%s: %s", fileName, strerror(errno));
7c673cae 369 fileSize = UTIL_getFileSize(fileName);
11fdf7f2
TL
370 if (fileSize > DICTSIZE_MAX) {
371 EXM_THROW(32, "Dictionary file %s is too large (> %u MB)",
372 fileName, DICTSIZE_MAX >> 20); /* avoid extreme cases */
7c673cae
FG
373 }
374 *bufferPtr = malloc((size_t)fileSize);
11fdf7f2
TL
375 if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
376 { size_t const readSize = fread(*bufferPtr, 1, (size_t)fileSize, fileHandle);
377 if (readSize!=fileSize)
378 EXM_THROW(35, "Error reading dictionary file %s", fileName);
379 }
7c673cae
FG
380 fclose(fileHandle);
381 return (size_t)fileSize;
382}
383
384#ifndef ZSTD_NOCOMPRESS
385
386/*-**********************************************************************
387* Compression
388************************************************************************/
389typedef struct {
11fdf7f2
TL
390 FILE* srcFile;
391 FILE* dstFile;
7c673cae
FG
392 void* srcBuffer;
393 size_t srcBufferSize;
394 void* dstBuffer;
395 size_t dstBufferSize;
11fdf7f2
TL
396#if !defined(ZSTD_NEWAPI) && defined(ZSTD_MULTITHREAD)
397 ZSTDMT_CCtx* cctx;
398#else
7c673cae 399 ZSTD_CStream* cctx;
11fdf7f2 400#endif
7c673cae
FG
401} cRess_t;
402
11fdf7f2
TL
403static cRess_t FIO_createCResources(const char* dictFileName, int cLevel,
404 U64 srcSize,
405 ZSTD_compressionParameters* comprParams) {
7c673cae
FG
406 cRess_t ress;
407 memset(&ress, 0, sizeof(ress));
408
11fdf7f2
TL
409#ifdef ZSTD_NEWAPI
410 ress.cctx = ZSTD_createCCtx();
411 if (ress.cctx == NULL)
412 EXM_THROW(30, "allocation error : can't create ZSTD_CCtx");
413#elif defined(ZSTD_MULTITHREAD)
414 ress.cctx = ZSTDMT_createCCtx(g_nbThreads);
415 if (ress.cctx == NULL)
416 EXM_THROW(30, "allocation error : can't create ZSTDMT_CCtx");
417 if ((cLevel==ZSTD_maxCLevel()) && (g_overlapLog==FIO_OVERLAP_LOG_NOTSET))
418 /* use complete window for overlap */
419 ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, 9);
420 if (g_overlapLog != FIO_OVERLAP_LOG_NOTSET)
421 ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, g_overlapLog);
422#else
7c673cae 423 ress.cctx = ZSTD_createCStream();
11fdf7f2
TL
424 if (ress.cctx == NULL)
425 EXM_THROW(30, "allocation error : can't create ZSTD_CStream");
426#endif
7c673cae
FG
427 ress.srcBufferSize = ZSTD_CStreamInSize();
428 ress.srcBuffer = malloc(ress.srcBufferSize);
429 ress.dstBufferSize = ZSTD_CStreamOutSize();
430 ress.dstBuffer = malloc(ress.dstBufferSize);
11fdf7f2
TL
431 if (!ress.srcBuffer || !ress.dstBuffer)
432 EXM_THROW(31, "allocation error : not enough memory");
7c673cae
FG
433
434 /* dictionary */
435 { void* dictBuffer;
11fdf7f2
TL
436 size_t const dictBuffSize = FIO_createDictBuffer(&dictBuffer, dictFileName); /* works with dictFileName==NULL */
437 if (dictFileName && (dictBuffer==NULL))
438 EXM_THROW(32, "allocation error : can't create dictBuffer");
439
440#ifdef ZSTD_NEWAPI
441 { /* frame parameters */
442 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_dictIDFlag, g_dictIDFlag) );
443 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_checksumFlag, g_checksumFlag) );
444 (void)srcSize;
445 /* compression level */
446 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionLevel, cLevel) );
447 /* long distance matching */
448 CHECK( ZSTD_CCtx_setParameter(
449 ress.cctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag) );
450 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmHashLog, g_ldmHashLog) );
451 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmMinMatch, g_ldmMinMatch) );
452 if (g_ldmBucketSizeLog != FIO_LDM_PARAM_NOTSET) {
453 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmBucketSizeLog, g_ldmBucketSizeLog) );
454 }
455 if (g_ldmHashEveryLog != FIO_LDM_PARAM_NOTSET) {
456 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmHashEveryLog, g_ldmHashEveryLog) );
457 }
458 /* compression parameters */
459 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_windowLog, comprParams->windowLog) );
460 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_chainLog, comprParams->chainLog) );
461 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_hashLog, comprParams->hashLog) );
462 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_searchLog, comprParams->searchLog) );
463 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_minMatch, comprParams->searchLength) );
464 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_targetLength, comprParams->targetLength) );
465 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionStrategy, (U32)comprParams->strategy) );
466 /* multi-threading */
467 DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads);
468 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) );
469 /* dictionary */
470 CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) );
471 }
472#elif defined(ZSTD_MULTITHREAD)
7c673cae 473 { ZSTD_parameters params = ZSTD_getParams(cLevel, srcSize, dictBuffSize);
7c673cae
FG
474 params.fParams.checksumFlag = g_checksumFlag;
475 params.fParams.noDictIDFlag = !g_dictIDFlag;
11fdf7f2
TL
476 if (comprParams->windowLog) params.cParams.windowLog = comprParams->windowLog;
477 if (comprParams->chainLog) params.cParams.chainLog = comprParams->chainLog;
478 if (comprParams->hashLog) params.cParams.hashLog = comprParams->hashLog;
479 if (comprParams->searchLog) params.cParams.searchLog = comprParams->searchLog;
480 if (comprParams->searchLength) params.cParams.searchLength = comprParams->searchLength;
481 if (comprParams->targetLength) params.cParams.targetLength = comprParams->targetLength;
482 if (comprParams->strategy) params.cParams.strategy = (ZSTD_strategy) comprParams->strategy;
483 CHECK( ZSTDMT_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize) );
484 ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_sectionSize, g_blockSize);
485 }
486#else
487 { ZSTD_parameters params = ZSTD_getParams(cLevel, srcSize, dictBuffSize);
488 params.fParams.checksumFlag = g_checksumFlag;
489 params.fParams.noDictIDFlag = !g_dictIDFlag;
490 if (comprParams->windowLog) params.cParams.windowLog = comprParams->windowLog;
491 if (comprParams->chainLog) params.cParams.chainLog = comprParams->chainLog;
492 if (comprParams->hashLog) params.cParams.hashLog = comprParams->hashLog;
493 if (comprParams->searchLog) params.cParams.searchLog = comprParams->searchLog;
494 if (comprParams->searchLength) params.cParams.searchLength = comprParams->searchLength;
495 if (comprParams->targetLength) params.cParams.targetLength = comprParams->targetLength;
496 if (comprParams->strategy) params.cParams.strategy = (ZSTD_strategy) comprParams->strategy;
497 CHECK( ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize) );
498 }
499#endif
7c673cae
FG
500 free(dictBuffer);
501 }
502
503 return ress;
504}
505
506static void FIO_freeCResources(cRess_t ress)
507{
508 free(ress.srcBuffer);
509 free(ress.dstBuffer);
11fdf7f2
TL
510#if !defined(ZSTD_NEWAPI) && defined(ZSTD_MULTITHREAD)
511 ZSTDMT_freeCCtx(ress.cctx);
512#else
7c673cae 513 ZSTD_freeCStream(ress.cctx); /* never fails */
11fdf7f2 514#endif
7c673cae
FG
515}
516
517
11fdf7f2
TL
518#ifdef ZSTD_GZCOMPRESS
519static unsigned long long FIO_compressGzFrame(cRess_t* ress,
520 const char* srcFileName, U64 const srcFileSize,
521 int compressionLevel, U64* readsize)
522{
523 unsigned long long inFileSize = 0, outFileSize = 0;
524 z_stream strm;
525 int ret;
526
527 if (compressionLevel > Z_BEST_COMPRESSION)
528 compressionLevel = Z_BEST_COMPRESSION;
529
530 strm.zalloc = Z_NULL;
531 strm.zfree = Z_NULL;
532 strm.opaque = Z_NULL;
533
534 ret = deflateInit2(&strm, compressionLevel, Z_DEFLATED,
535 15 /* maxWindowLogSize */ + 16 /* gzip only */,
536 8, Z_DEFAULT_STRATEGY); /* see http://www.zlib.net/manual.html */
537 if (ret != Z_OK)
538 EXM_THROW(71, "zstd: %s: deflateInit2 error %d \n", srcFileName, ret);
539
540 strm.next_in = 0;
541 strm.avail_in = 0;
542 strm.next_out = (Bytef*)ress->dstBuffer;
543 strm.avail_out = (uInt)ress->dstBufferSize;
544
545 while (1) {
546 if (strm.avail_in == 0) {
547 size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
548 if (inSize == 0) break;
549 inFileSize += inSize;
550 strm.next_in = (z_const unsigned char*)ress->srcBuffer;
551 strm.avail_in = (uInt)inSize;
552 }
553 ret = deflate(&strm, Z_NO_FLUSH);
554 if (ret != Z_OK)
555 EXM_THROW(72, "zstd: %s: deflate error %d \n", srcFileName, ret);
556 { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
557 if (decompBytes) {
558 if (fwrite(ress->dstBuffer, 1, decompBytes, ress->dstFile) != decompBytes)
559 EXM_THROW(73, "Write error : cannot write to output file");
560 outFileSize += decompBytes;
561 strm.next_out = (Bytef*)ress->dstBuffer;
562 strm.avail_out = (uInt)ress->dstBufferSize;
563 }
564 }
565 if (!srcFileSize)
566 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
567 (U32)(inFileSize>>20),
568 (double)outFileSize/inFileSize*100)
569 else
570 DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
571 (U32)(inFileSize>>20), (U32)(srcFileSize>>20),
572 (double)outFileSize/inFileSize*100);
573 }
574
575 while (1) {
576 ret = deflate(&strm, Z_FINISH);
577 { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
578 if (decompBytes) {
579 if (fwrite(ress->dstBuffer, 1, decompBytes, ress->dstFile) != decompBytes)
580 EXM_THROW(75, "Write error : cannot write to output file");
581 outFileSize += decompBytes;
582 strm.next_out = (Bytef*)ress->dstBuffer;
583 strm.avail_out = (uInt)ress->dstBufferSize;
584 } }
585 if (ret == Z_STREAM_END) break;
586 if (ret != Z_BUF_ERROR)
587 EXM_THROW(77, "zstd: %s: deflate error %d \n", srcFileName, ret);
588 }
589
590 ret = deflateEnd(&strm);
591 if (ret != Z_OK)
592 EXM_THROW(79, "zstd: %s: deflateEnd error %d \n", srcFileName, ret);
593 *readsize = inFileSize;
594
595 return outFileSize;
596}
597#endif
598
599
600#ifdef ZSTD_LZMACOMPRESS
601static unsigned long long FIO_compressLzmaFrame(cRess_t* ress,
602 const char* srcFileName, U64 const srcFileSize,
603 int compressionLevel, U64* readsize, int plain_lzma)
604{
605 unsigned long long inFileSize = 0, outFileSize = 0;
606 lzma_stream strm = LZMA_STREAM_INIT;
607 lzma_action action = LZMA_RUN;
608 lzma_ret ret;
609
610 if (compressionLevel < 0) compressionLevel = 0;
611 if (compressionLevel > 9) compressionLevel = 9;
612
613 if (plain_lzma) {
614 lzma_options_lzma opt_lzma;
615 if (lzma_lzma_preset(&opt_lzma, compressionLevel))
616 EXM_THROW(71, "zstd: %s: lzma_lzma_preset error", srcFileName);
617 ret = lzma_alone_encoder(&strm, &opt_lzma); /* LZMA */
618 if (ret != LZMA_OK)
619 EXM_THROW(71, "zstd: %s: lzma_alone_encoder error %d", srcFileName, ret);
620 } else {
621 ret = lzma_easy_encoder(&strm, compressionLevel, LZMA_CHECK_CRC64); /* XZ */
622 if (ret != LZMA_OK)
623 EXM_THROW(71, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret);
624 }
625
626 strm.next_in = 0;
627 strm.avail_in = 0;
628 strm.next_out = (BYTE*)ress->dstBuffer;
629 strm.avail_out = ress->dstBufferSize;
630
631 while (1) {
632 if (strm.avail_in == 0) {
633 size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
634 if (inSize == 0) action = LZMA_FINISH;
635 inFileSize += inSize;
636 strm.next_in = (BYTE const*)ress->srcBuffer;
637 strm.avail_in = inSize;
638 }
639
640 ret = lzma_code(&strm, action);
641
642 if (ret != LZMA_OK && ret != LZMA_STREAM_END)
643 EXM_THROW(72, "zstd: %s: lzma_code encoding error %d", srcFileName, ret);
644 { size_t const compBytes = ress->dstBufferSize - strm.avail_out;
645 if (compBytes) {
646 if (fwrite(ress->dstBuffer, 1, compBytes, ress->dstFile) != compBytes)
647 EXM_THROW(73, "Write error : cannot write to output file");
648 outFileSize += compBytes;
649 strm.next_out = (BYTE*)ress->dstBuffer;
650 strm.avail_out = ress->dstBufferSize;
651 } }
652 if (!srcFileSize)
653 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
654 (U32)(inFileSize>>20),
655 (double)outFileSize/inFileSize*100)
656 else
657 DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
658 (U32)(inFileSize>>20), (U32)(srcFileSize>>20),
659 (double)outFileSize/inFileSize*100);
660 if (ret == LZMA_STREAM_END) break;
661 }
662
663 lzma_end(&strm);
664 *readsize = inFileSize;
665
666 return outFileSize;
667}
668#endif
669
670#ifdef ZSTD_LZ4COMPRESS
671static int FIO_LZ4_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }
672static unsigned long long FIO_compressLz4Frame(cRess_t* ress,
673 const char* srcFileName, U64 const srcFileSize,
674 int compressionLevel, U64* readsize)
675{
676 unsigned long long inFileSize = 0, outFileSize = 0;
677
678 LZ4F_preferences_t prefs;
679 LZ4F_compressionContext_t ctx;
680
681 LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
682 if (LZ4F_isError(errorCode))
683 EXM_THROW(31, "zstd: failed to create lz4 compression context");
684
685 memset(&prefs, 0, sizeof(prefs));
686
687#if LZ4_VERSION_NUMBER <= 10600
688#define LZ4F_blockIndependent blockIndependent
689#define LZ4F_max4MB max4MB
690#endif
691
692 prefs.autoFlush = 1;
693 prefs.compressionLevel = compressionLevel;
694 prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* stick to defaults for lz4 cli */
695 prefs.frameInfo.blockSizeID = LZ4F_max4MB;
696 prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)g_checksumFlag;
697#if LZ4_VERSION_NUMBER >= 10600
698 prefs.frameInfo.contentSize = srcFileSize;
699#endif
700
701 {
702 size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max4MB);
703 size_t readSize;
704 size_t headerSize = LZ4F_compressBegin(ctx, ress->dstBuffer, ress->dstBufferSize, &prefs);
705 if (LZ4F_isError(headerSize))
706 EXM_THROW(33, "File header generation failed : %s",
707 LZ4F_getErrorName(headerSize));
708 { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile);
709 if (sizeCheck!=headerSize) EXM_THROW(34, "Write error : cannot write header"); }
710 outFileSize += headerSize;
711
712 /* Read first block */
713 readSize = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
714 inFileSize += readSize;
715
716 /* Main Loop */
717 while (readSize>0) {
718 size_t outSize;
719
720 /* Compress Block */
721 outSize = LZ4F_compressUpdate(ctx, ress->dstBuffer, ress->dstBufferSize, ress->srcBuffer, readSize, NULL);
722 if (LZ4F_isError(outSize))
723 EXM_THROW(35, "zstd: %s: lz4 compression failed : %s",
724 srcFileName, LZ4F_getErrorName(outSize));
725 outFileSize += outSize;
726 if (!srcFileSize)
727 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
728 (U32)(inFileSize>>20),
729 (double)outFileSize/inFileSize*100)
730 else
731 DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
732 (U32)(inFileSize>>20), (U32)(srcFileSize>>20),
733 (double)outFileSize/inFileSize*100);
734
735 /* Write Block */
736 { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, outSize, ress->dstFile);
737 if (sizeCheck!=outSize) EXM_THROW(36, "Write error : cannot write compressed block"); }
738
739 /* Read next block */
740 readSize = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
741 inFileSize += readSize;
742 }
743 if (ferror(ress->srcFile)) EXM_THROW(37, "Error reading %s ", srcFileName);
744
745 /* End of Stream mark */
746 headerSize = LZ4F_compressEnd(ctx, ress->dstBuffer, ress->dstBufferSize, NULL);
747 if (LZ4F_isError(headerSize))
748 EXM_THROW(38, "zstd: %s: lz4 end of file generation failed : %s",
749 srcFileName, LZ4F_getErrorName(headerSize));
750
751 { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile);
752 if (sizeCheck!=headerSize) EXM_THROW(39, "Write error : cannot write end of stream"); }
753 outFileSize += headerSize;
754 }
755
756 *readsize = inFileSize;
757 LZ4F_freeCompressionContext(ctx);
758
759 return outFileSize;
760}
761#endif
762
763
7c673cae
FG
764/*! FIO_compressFilename_internal() :
765 * same as FIO_compressFilename_extRess(), with `ress.desFile` already opened.
766 * @return : 0 : compression completed correctly,
767 * 1 : missing or pb opening srcFileName
768 */
769static int FIO_compressFilename_internal(cRess_t ress,
11fdf7f2 770 const char* dstFileName, const char* srcFileName, int compressionLevel)
7c673cae
FG
771{
772 FILE* const srcFile = ress.srcFile;
773 FILE* const dstFile = ress.dstFile;
774 U64 readsize = 0;
775 U64 compressedfilesize = 0;
776 U64 const fileSize = UTIL_getFileSize(srcFileName);
11fdf7f2 777 DISPLAYLEVEL(5, "%s: %u bytes \n", srcFileName, (U32)fileSize);
7c673cae 778
11fdf7f2
TL
779 switch (g_compressionType) {
780 case FIO_zstdCompression:
781 break;
782
783 case FIO_gzipCompression:
784#ifdef ZSTD_GZCOMPRESS
785 compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize);
786#else
787 (void)compressionLevel;
788 EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n",
789 srcFileName);
790#endif
791 goto finish;
792
793 case FIO_xzCompression:
794 case FIO_lzmaCompression:
795#ifdef ZSTD_LZMACOMPRESS
796 compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, g_compressionType==FIO_lzmaCompression);
797#else
798 (void)compressionLevel;
799 EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n",
800 srcFileName);
801#endif
802 goto finish;
803
804 case FIO_lz4Compression:
805#ifdef ZSTD_LZ4COMPRESS
806 compressedfilesize = FIO_compressLz4Frame(&ress, srcFileName, fileSize, compressionLevel, &readsize);
807#else
808 (void)compressionLevel;
809 EXM_THROW(20, "zstd: %s: file cannot be compressed as lz4 (zstd compiled without ZSTD_LZ4COMPRESS) -- ignored \n",
810 srcFileName);
811#endif
812 goto finish;
7c673cae
FG
813 }
814
11fdf7f2
TL
815 /* init */
816#ifdef ZSTD_NEWAPI
817 if (fileSize!=0) /* when src is stdin, fileSize==0, but is effectively unknown */
818 ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); /* note : fileSize==0 means "empty" */
819#elif defined(ZSTD_MULTITHREAD)
820 CHECK( ZSTDMT_resetCStream(ress.cctx, fileSize) ); /* note : fileSize==0 means "unknown" */
821#else
822 CHECK( ZSTD_resetCStream(ress.cctx, fileSize) ); /* note : fileSize==0 means "unknown" */
823#endif
824
7c673cae
FG
825 /* Main compression loop */
826 while (1) {
827 /* Fill input Buffer */
828 size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile);
11fdf7f2 829 ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 };
7c673cae
FG
830 if (inSize==0) break;
831 readsize += inSize;
11fdf7f2
TL
832
833 while (inBuff.pos != inBuff.size) {
834 ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 };
835#ifdef ZSTD_NEWAPI
836 CHECK( ZSTD_compress_generic(ress.cctx,
837 &outBuff, &inBuff, ZSTD_e_continue) );
838#elif defined(ZSTD_MULTITHREAD)
839 CHECK( ZSTDMT_compressStream(ress.cctx, &outBuff, &inBuff) );
840#else
841 CHECK( ZSTD_compressStream(ress.cctx, &outBuff, &inBuff) );
842#endif
843
844 /* Write compressed stream */
845 if (outBuff.pos) {
846 size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile);
847 if (sizeCheck!=outBuff.pos)
848 EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName);
849 compressedfilesize += outBuff.pos;
850 } }
851 if (g_nbThreads > 1) {
852 if (!fileSize)
853 DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20))
854 else
855 DISPLAYUPDATE(2, "\rRead : %u / %u MB",
856 (U32)(readsize>>20), (U32)(fileSize>>20));
857 } else {
858 if (!fileSize)
859 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
860 (U32)(readsize>>20),
861 (double)compressedfilesize/readsize*100)
862 else
863 DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
864 (U32)(readsize>>20), (U32)(fileSize>>20),
865 (double)compressedfilesize/readsize*100);
7c673cae 866 }
7c673cae
FG
867 }
868
869 /* End of Frame */
11fdf7f2
TL
870 { size_t result = 1;
871 while (result != 0) {
872 ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 };
873#ifdef ZSTD_NEWAPI
874 ZSTD_inBuffer inBuff = { NULL, 0, 0 };
875 result = ZSTD_compress_generic(ress.cctx,
876 &outBuff, &inBuff, ZSTD_e_end);
877#elif defined(ZSTD_MULTITHREAD)
878 result = ZSTDMT_endStream(ress.cctx, &outBuff);
879#else
880 result = ZSTD_endStream(ress.cctx, &outBuff);
881#endif
882 if (ZSTD_isError(result)) {
883 EXM_THROW(26, "Compression error during frame end : %s",
884 ZSTD_getErrorName(result));
885 }
886 { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile);
887 if (sizeCheck!=outBuff.pos)
888 EXM_THROW(27, "Write error : cannot write frame end into %s", dstFileName);
889 }
890 compressedfilesize += outBuff.pos;
891 }
7c673cae
FG
892 }
893
11fdf7f2 894finish:
7c673cae
FG
895 /* Status */
896 DISPLAYLEVEL(2, "\r%79s\r", "");
897 DISPLAYLEVEL(2,"%-20s :%6.2f%% (%6llu => %6llu bytes, %s) \n", srcFileName,
898 (double)compressedfilesize/(readsize+(!readsize) /* avoid div by zero */ )*100,
899 (unsigned long long)readsize, (unsigned long long) compressedfilesize,
900 dstFileName);
901
902 return 0;
903}
904
905
906/*! FIO_compressFilename_srcFile() :
907 * note : ress.destFile already opened
908 * @return : 0 : compression completed correctly,
909 * 1 : missing or pb opening srcFileName
910 */
911static int FIO_compressFilename_srcFile(cRess_t ress,
11fdf7f2
TL
912 const char* dstFileName, const char* srcFileName,
913 int compressionLevel)
7c673cae
FG
914{
915 int result;
916
917 /* File check */
918 if (UTIL_isDirectory(srcFileName)) {
919 DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
920 return 1;
921 }
11fdf7f2 922
7c673cae
FG
923 ress.srcFile = FIO_openSrcFile(srcFileName);
924 if (!ress.srcFile) return 1; /* srcFile could not be opened */
925
11fdf7f2 926 result = FIO_compressFilename_internal(ress, dstFileName, srcFileName, compressionLevel);
7c673cae
FG
927
928 fclose(ress.srcFile);
11fdf7f2
TL
929 if (g_removeSrcFile /* --rm */ && !result && strcmp(srcFileName, stdinmark)) {
930 if (remove(srcFileName))
931 EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno));
932 }
7c673cae
FG
933 return result;
934}
935
936
937/*! FIO_compressFilename_dstFile() :
938 * @return : 0 : compression completed correctly,
939 * 1 : pb
940 */
941static int FIO_compressFilename_dstFile(cRess_t ress,
11fdf7f2
TL
942 const char* dstFileName,
943 const char* srcFileName,
944 int compressionLevel)
7c673cae
FG
945{
946 int result;
947 stat_t statbuf;
948 int stat_result = 0;
949
950 ress.dstFile = FIO_openDstFile(dstFileName);
951 if (ress.dstFile==NULL) return 1; /* could not open dstFileName */
952
11fdf7f2
TL
953 if (UTIL_isRegularFile(dstFileName)) {
954 g_artefact = dstFileName;
955 signal(SIGINT, INThandler);
956 } else {
957 g_artefact = NULL;
958 }
959
960
961 if (strcmp (srcFileName, stdinmark) && UTIL_getFileStat(srcFileName, &statbuf))
962 stat_result = 1;
963 result = FIO_compressFilename_srcFile(ress, dstFileName, srcFileName, compressionLevel);
964
965 if (fclose(ress.dstFile)) { /* error closing dstFile */
966 DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
967 result=1;
968 }
969 if (result!=0) { /* remove operation artefact */
970 if (remove(dstFileName))
971 EXM_THROW(1, "zstd: %s: %s", dstFileName, strerror(errno));
972 }
973 else if (strcmp (dstFileName, stdoutmark) && stat_result)
974 UTIL_setFileStat(dstFileName, &statbuf);
975
976 signal(SIGINT, SIG_DFL);
7c673cae 977
7c673cae
FG
978 return result;
979}
980
981
982int FIO_compressFilename(const char* dstFileName, const char* srcFileName,
11fdf7f2 983 const char* dictFileName, int compressionLevel, ZSTD_compressionParameters* comprParams)
7c673cae
FG
984{
985 clock_t const start = clock();
986 U64 const srcSize = UTIL_getFileSize(srcFileName);
987
11fdf7f2
TL
988 cRess_t const ress = FIO_createCResources(dictFileName, compressionLevel, srcSize, comprParams);
989 int const result = FIO_compressFilename_dstFile(ress, dstFileName, srcFileName, compressionLevel);
7c673cae
FG
990
991 double const seconds = (double)(clock() - start) / CLOCKS_PER_SEC;
992 DISPLAYLEVEL(4, "Completed in %.2f sec \n", seconds);
993
994 FIO_freeCResources(ress);
995 return result;
996}
997
998
999int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFiles,
1000 const char* suffix,
11fdf7f2
TL
1001 const char* dictFileName, int compressionLevel,
1002 ZSTD_compressionParameters* comprParams)
7c673cae
FG
1003{
1004 int missed_files = 0;
1005 size_t dfnSize = FNSPACE;
1006 char* dstFileName = (char*)malloc(FNSPACE);
1007 size_t const suffixSize = suffix ? strlen(suffix) : 0;
1008 U64 const srcSize = (nbFiles != 1) ? 0 : UTIL_getFileSize(inFileNamesTable[0]) ;
11fdf7f2 1009 cRess_t ress = FIO_createCResources(dictFileName, compressionLevel, srcSize, comprParams);
7c673cae
FG
1010
1011 /* init */
11fdf7f2
TL
1012 if (dstFileName==NULL)
1013 EXM_THROW(27, "FIO_compressMultipleFilenames : allocation error for dstFileName");
1014 if (suffix == NULL)
1015 EXM_THROW(28, "FIO_compressMultipleFilenames : dst unknown"); /* should never happen */
7c673cae
FG
1016
1017 /* loop on each file */
1018 if (!strcmp(suffix, stdoutmark)) {
1019 unsigned u;
1020 ress.dstFile = stdout;
1021 SET_BINARY_MODE(stdout);
1022 for (u=0; u<nbFiles; u++)
11fdf7f2
TL
1023 missed_files += FIO_compressFilename_srcFile(ress, stdoutmark, inFileNamesTable[u], compressionLevel);
1024 if (fclose(ress.dstFile))
1025 EXM_THROW(29, "Write error : cannot properly close stdout");
7c673cae
FG
1026 } else {
1027 unsigned u;
1028 for (u=0; u<nbFiles; u++) {
11fdf7f2
TL
1029 size_t const ifnSize = strlen(inFileNamesTable[u]);
1030 if (dfnSize <= ifnSize+suffixSize+1) { /* resize name buffer */
1031 free(dstFileName);
1032 dfnSize = ifnSize + 20;
1033 dstFileName = (char*)malloc(dfnSize);
1034 if (!dstFileName)
1035 EXM_THROW(30, "zstd: %s", strerror(errno));
1036 }
7c673cae
FG
1037 strcpy(dstFileName, inFileNamesTable[u]);
1038 strcat(dstFileName, suffix);
11fdf7f2 1039 missed_files += FIO_compressFilename_dstFile(ress, dstFileName, inFileNamesTable[u], compressionLevel);
7c673cae
FG
1040 } }
1041
7c673cae
FG
1042 FIO_freeCResources(ress);
1043 free(dstFileName);
7c673cae
FG
1044 return missed_files;
1045}
1046
1047#endif /* #ifndef ZSTD_NOCOMPRESS */
1048
1049
1050
1051#ifndef ZSTD_NODECOMPRESS
1052
1053/* **************************************************************************
11fdf7f2
TL
1054 * Decompression
1055 ***************************************************************************/
7c673cae
FG
1056typedef struct {
1057 void* srcBuffer;
7c673cae 1058 size_t srcBufferSize;
11fdf7f2 1059 size_t srcBufferLoaded;
7c673cae
FG
1060 void* dstBuffer;
1061 size_t dstBufferSize;
1062 ZSTD_DStream* dctx;
1063 FILE* dstFile;
1064} dRess_t;
1065
1066static dRess_t FIO_createDResources(const char* dictFileName)
1067{
1068 dRess_t ress;
1069 memset(&ress, 0, sizeof(ress));
1070
1071 /* Allocation */
1072 ress.dctx = ZSTD_createDStream();
1073 if (ress.dctx==NULL) EXM_THROW(60, "Can't create ZSTD_DStream");
11fdf7f2 1074 CHECK( ZSTD_setDStreamParameter(ress.dctx, DStream_p_maxWindowSize, g_memLimit) );
7c673cae
FG
1075 ress.srcBufferSize = ZSTD_DStreamInSize();
1076 ress.srcBuffer = malloc(ress.srcBufferSize);
1077 ress.dstBufferSize = ZSTD_DStreamOutSize();
1078 ress.dstBuffer = malloc(ress.dstBufferSize);
11fdf7f2
TL
1079 if (!ress.srcBuffer || !ress.dstBuffer)
1080 EXM_THROW(61, "Allocation error : not enough memory");
7c673cae
FG
1081
1082 /* dictionary */
1083 { void* dictBuffer;
11fdf7f2
TL
1084 size_t const dictBufferSize = FIO_createDictBuffer(&dictBuffer, dictFileName);
1085 CHECK( ZSTD_initDStream_usingDict(ress.dctx, dictBuffer, dictBufferSize) );
7c673cae
FG
1086 free(dictBuffer);
1087 }
1088
1089 return ress;
1090}
1091
1092static void FIO_freeDResources(dRess_t ress)
1093{
11fdf7f2 1094 CHECK( ZSTD_freeDStream(ress.dctx) );
7c673cae
FG
1095 free(ress.srcBuffer);
1096 free(ress.dstBuffer);
1097}
1098
1099
1100/** FIO_fwriteSparse() :
1101* @return : storedSkips, to be provided to next call to FIO_fwriteSparse() of LZ4IO_fwriteSparseEnd() */
1102static unsigned FIO_fwriteSparse(FILE* file, const void* buffer, size_t bufferSize, unsigned storedSkips)
1103{
1104 const size_t* const bufferT = (const size_t*)buffer; /* Buffer is supposed malloc'ed, hence aligned on size_t */
1105 size_t bufferSizeT = bufferSize / sizeof(size_t);
1106 const size_t* const bufferTEnd = bufferT + bufferSizeT;
1107 const size_t* ptrT = bufferT;
1108 static const size_t segmentSizeT = (32 KB) / sizeof(size_t); /* 0-test re-attempted every 32 KB */
1109
1110 if (!g_sparseFileSupport) { /* normal write */
1111 size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
1112 if (sizeCheck != bufferSize) EXM_THROW(70, "Write error : cannot write decoded block");
1113 return 0;
1114 }
1115
1116 /* avoid int overflow */
1117 if (storedSkips > 1 GB) {
11fdf7f2 1118 int const seekResult = LONG_SEEK(file, 1 GB, SEEK_CUR);
7c673cae
FG
1119 if (seekResult != 0) EXM_THROW(71, "1 GB skip error (sparse file support)");
1120 storedSkips -= 1 GB;
1121 }
1122
1123 while (ptrT < bufferTEnd) {
1124 size_t seg0SizeT = segmentSizeT;
1125 size_t nb0T;
1126
1127 /* count leading zeros */
1128 if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
1129 bufferSizeT -= seg0SizeT;
1130 for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
1131 storedSkips += (unsigned)(nb0T * sizeof(size_t));
1132
1133 if (nb0T != seg0SizeT) { /* not all 0s */
11fdf7f2 1134 int const seekResult = LONG_SEEK(file, storedSkips, SEEK_CUR);
7c673cae
FG
1135 if (seekResult) EXM_THROW(72, "Sparse skip error ; try --no-sparse");
1136 storedSkips = 0;
1137 seg0SizeT -= nb0T;
1138 ptrT += nb0T;
1139 { size_t const sizeCheck = fwrite(ptrT, sizeof(size_t), seg0SizeT, file);
11fdf7f2
TL
1140 if (sizeCheck != seg0SizeT)
1141 EXM_THROW(73, "Write error : cannot write decoded block");
7c673cae
FG
1142 } }
1143 ptrT += seg0SizeT;
1144 }
1145
1146 { static size_t const maskT = sizeof(size_t)-1;
11fdf7f2
TL
1147 if (bufferSize & maskT) {
1148 /* size not multiple of sizeof(size_t) : implies end of block */
7c673cae
FG
1149 const char* const restStart = (const char*)bufferTEnd;
1150 const char* restPtr = restStart;
1151 size_t restSize = bufferSize & maskT;
1152 const char* const restEnd = restStart + restSize;
1153 for ( ; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
1154 storedSkips += (unsigned) (restPtr - restStart);
1155 if (restPtr != restEnd) {
11fdf7f2
TL
1156 int seekResult = LONG_SEEK(file, storedSkips, SEEK_CUR);
1157 if (seekResult)
1158 EXM_THROW(74, "Sparse skip error ; try --no-sparse");
7c673cae
FG
1159 storedSkips = 0;
1160 { size_t const sizeCheck = fwrite(restPtr, 1, restEnd - restPtr, file);
11fdf7f2
TL
1161 if (sizeCheck != (size_t)(restEnd - restPtr))
1162 EXM_THROW(75, "Write error : cannot write decoded end of block");
7c673cae
FG
1163 } } } }
1164
1165 return storedSkips;
1166}
1167
1168static void FIO_fwriteSparseEnd(FILE* file, unsigned storedSkips)
1169{
1170 if (storedSkips-->0) { /* implies g_sparseFileSupport>0 */
11fdf7f2
TL
1171 int const seekResult = LONG_SEEK(file, storedSkips, SEEK_CUR);
1172 if (seekResult != 0) EXM_THROW(69, "Final skip error (sparse file)");
7c673cae
FG
1173 { const char lastZeroByte[1] = { 0 };
1174 size_t const sizeCheck = fwrite(lastZeroByte, 1, 1, file);
11fdf7f2
TL
1175 if (sizeCheck != 1)
1176 EXM_THROW(69, "Write error : cannot write last zero");
7c673cae
FG
1177 } }
1178}
1179
1180
1181/** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
1182 @return : 0 (no error) */
1183static unsigned FIO_passThrough(FILE* foutput, FILE* finput, void* buffer, size_t bufferSize, size_t alreadyLoaded)
1184{
1185 size_t const blockSize = MIN(64 KB, bufferSize);
1186 size_t readFromInput = 1;
1187 unsigned storedSkips = 0;
1188
1189 /* assumption : ress->srcBufferLoaded bytes already loaded and stored within buffer */
11fdf7f2
TL
1190 { size_t const sizeCheck = fwrite(buffer, 1, alreadyLoaded, foutput);
1191 if (sizeCheck != alreadyLoaded) {
1192 DISPLAYLEVEL(1, "Pass-through write error \n");
1193 return 1;
1194 } }
7c673cae
FG
1195
1196 while (readFromInput) {
1197 readFromInput = fread(buffer, 1, blockSize, finput);
1198 storedSkips = FIO_fwriteSparse(foutput, buffer, readFromInput, storedSkips);
1199 }
1200
1201 FIO_fwriteSparseEnd(foutput, storedSkips);
1202 return 0;
1203}
1204
11fdf7f2
TL
1205static void FIO_zstdErrorHelp(dRess_t* ress, size_t ret, char const* srcFileName)
1206{
1207 ZSTD_frameHeader header;
1208 /* No special help for these errors */
1209 if (ZSTD_getErrorCode(ret) != ZSTD_error_frameParameter_windowTooLarge)
1210 return;
1211 /* Try to decode the frame header */
1212 ret = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded);
1213 if (ret == 0) {
1214 U32 const windowSize = (U32)header.windowSize;
1215 U32 const windowLog = BIT_highbit32(windowSize) + ((windowSize & (windowSize - 1)) != 0);
1216 U32 const windowMB = (windowSize >> 20) + (windowSize & ((1 MB) - 1));
1217 assert(header.windowSize <= (U64)((U32)-1));
1218 assert(g_memLimit > 0);
1219 DISPLAYLEVEL(1, "%s : Window size larger than maximum : %llu > %u\n",
1220 srcFileName, header.windowSize, g_memLimit);
1221 if (windowLog <= ZSTD_WINDOWLOG_MAX) {
1222 DISPLAYLEVEL(1, "%s : Use --long=%u or --memory=%uMB\n",
1223 srcFileName, windowLog, windowMB);
1224 return;
1225 }
1226 } else if (ZSTD_getErrorCode(ret) != ZSTD_error_frameParameter_windowTooLarge) {
1227 DISPLAYLEVEL(1, "%s : Error decoding frame header to read window size : %s\n",
1228 srcFileName, ZSTD_getErrorName(ret));
1229 return;
1230 }
1231 DISPLAYLEVEL(1, "%s : Window log larger than ZSTD_WINDOWLOG_MAX=%u not supported\n",
1232 srcFileName, ZSTD_WINDOWLOG_MAX);
1233}
7c673cae
FG
1234
1235/** FIO_decompressFrame() :
11fdf7f2 1236 * @return : size of decoded zstd frame, or an error code
7c673cae 1237*/
11fdf7f2
TL
1238#define FIO_ERROR_FRAME_DECODING ((unsigned long long)(-2))
1239unsigned long long FIO_decompressZstdFrame(dRess_t* ress,
7c673cae 1240 FILE* finput,
11fdf7f2 1241 const char* srcFileName,
7c673cae
FG
1242 U64 alreadyDecoded)
1243{
1244 U64 frameSize = 0;
1245 U32 storedSkips = 0;
1246
11fdf7f2
TL
1247 size_t const srcFileLength = strlen(srcFileName);
1248 if (srcFileLength>20) srcFileName += srcFileLength-20; /* display last 20 characters only */
1249
7c673cae
FG
1250 ZSTD_resetDStream(ress->dctx);
1251
11fdf7f2
TL
1252 /* Header loading : ensures ZSTD_getFrameHeader() will succeed */
1253 { size_t const toDecode = ZSTD_FRAMEHEADERSIZE_MAX;
1254 if (ress->srcBufferLoaded < toDecode) {
1255 size_t const toRead = toDecode - ress->srcBufferLoaded;
1256 void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
1257 ress->srcBufferLoaded += fread(startPosition, 1, toRead, finput);
1258 } }
7c673cae
FG
1259
1260 /* Main decompression Loop */
1261 while (1) {
1262 ZSTD_inBuffer inBuff = { ress->srcBuffer, ress->srcBufferLoaded, 0 };
1263 ZSTD_outBuffer outBuff= { ress->dstBuffer, ress->dstBufferSize, 0 };
1264 size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
11fdf7f2
TL
1265 if (ZSTD_isError(readSizeHint)) {
1266 DISPLAYLEVEL(1, "%s : Decoding error (36) : %s \n",
1267 srcFileName, ZSTD_getErrorName(readSizeHint));
1268 FIO_zstdErrorHelp(ress, readSizeHint, srcFileName);
1269 return FIO_ERROR_FRAME_DECODING;
1270 }
7c673cae
FG
1271
1272 /* Write block */
11fdf7f2 1273 storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, outBuff.pos, storedSkips);
7c673cae 1274 frameSize += outBuff.pos;
11fdf7f2
TL
1275 DISPLAYUPDATE(2, "\r%-20.20s : %u MB... ",
1276 srcFileName, (U32)((alreadyDecoded+frameSize)>>20) );
7c673cae
FG
1277
1278 if (inBuff.pos > 0) {
1279 memmove(ress->srcBuffer, (char*)ress->srcBuffer + inBuff.pos, inBuff.size - inBuff.pos);
1280 ress->srcBufferLoaded -= inBuff.pos;
1281 }
1282
1283 if (readSizeHint == 0) break; /* end of frame */
11fdf7f2
TL
1284 if (inBuff.size != inBuff.pos) {
1285 DISPLAYLEVEL(1, "%s : Decoding error (37) : should consume entire input \n",
1286 srcFileName);
1287 return FIO_ERROR_FRAME_DECODING;
1288 }
7c673cae
FG
1289
1290 /* Fill input buffer */
11fdf7f2
TL
1291 { size_t const toDecode = MIN(readSizeHint, ress->srcBufferSize); /* support large skippable frames */
1292 if (ress->srcBufferLoaded < toDecode) {
1293 size_t const toRead = toDecode - ress->srcBufferLoaded; /* > 0 */
1294 void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
1295 size_t const readSize = fread(startPosition, 1, toRead, finput);
1296 if (readSize==0) {
1297 DISPLAYLEVEL(1, "%s : Read error (39) : premature end \n",
1298 srcFileName);
1299 return FIO_ERROR_FRAME_DECODING;
1300 }
1301 ress->srcBufferLoaded += readSize;
1302 } } }
7c673cae
FG
1303
1304 FIO_fwriteSparseEnd(ress->dstFile, storedSkips);
1305
1306 return frameSize;
1307}
1308
1309
1310#ifdef ZSTD_GZDECOMPRESS
11fdf7f2
TL
1311static unsigned long long FIO_decompressGzFrame(dRess_t* ress,
1312 FILE* srcFile, const char* srcFileName)
7c673cae
FG
1313{
1314 unsigned long long outFileSize = 0;
1315 z_stream strm;
11fdf7f2
TL
1316 int flush = Z_NO_FLUSH;
1317 int decodingError = 0;
7c673cae
FG
1318
1319 strm.zalloc = Z_NULL;
1320 strm.zfree = Z_NULL;
1321 strm.opaque = Z_NULL;
1322 strm.next_in = 0;
11fdf7f2
TL
1323 strm.avail_in = 0;
1324 /* see http://www.zlib.net/manual.html */
1325 if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
1326 return FIO_ERROR_FRAME_DECODING;
1327
1328 strm.next_out = (Bytef*)ress->dstBuffer;
1329 strm.avail_out = (uInt)ress->dstBufferSize;
1330 strm.avail_in = (uInt)ress->srcBufferLoaded;
7c673cae
FG
1331 strm.next_in = (z_const unsigned char*)ress->srcBuffer;
1332
1333 for ( ; ; ) {
1334 int ret;
1335 if (strm.avail_in == 0) {
1336 ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
11fdf7f2 1337 if (ress->srcBufferLoaded == 0) flush = Z_FINISH;
7c673cae 1338 strm.next_in = (z_const unsigned char*)ress->srcBuffer;
11fdf7f2
TL
1339 strm.avail_in = (uInt)ress->srcBufferLoaded;
1340 }
1341 ret = inflate(&strm, flush);
1342 if (ret == Z_BUF_ERROR) {
1343 DISPLAYLEVEL(1, "zstd: %s: premature gz end \n", srcFileName);
1344 decodingError = 1; break;
1345 }
1346 if (ret != Z_OK && ret != Z_STREAM_END) {
1347 DISPLAYLEVEL(1, "zstd: %s: inflate error %d \n", srcFileName, ret);
1348 decodingError = 1; break;
7c673cae 1349 }
7c673cae
FG
1350 { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
1351 if (decompBytes) {
11fdf7f2
TL
1352 if (fwrite(ress->dstBuffer, 1, decompBytes, ress->dstFile) != decompBytes) {
1353 DISPLAYLEVEL(1, "zstd: %s \n", strerror(errno));
1354 decodingError = 1; break;
1355 }
7c673cae 1356 outFileSize += decompBytes;
11fdf7f2
TL
1357 strm.next_out = (Bytef*)ress->dstBuffer;
1358 strm.avail_out = (uInt)ress->dstBufferSize;
7c673cae
FG
1359 }
1360 }
1361 if (ret == Z_STREAM_END) break;
1362 }
1363
11fdf7f2
TL
1364 if (strm.avail_in > 0)
1365 memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
7c673cae 1366 ress->srcBufferLoaded = strm.avail_in;
11fdf7f2
TL
1367 if ( (inflateEnd(&strm) != Z_OK) /* release resources ; error detected */
1368 && (decodingError==0) ) {
1369 DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
1370 decodingError = 1;
1371 }
1372 return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
7c673cae
FG
1373}
1374#endif
1375
1376
11fdf7f2
TL
1377#ifdef ZSTD_LZMADECOMPRESS
1378static unsigned long long FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName, int plain_lzma)
1379{
1380 unsigned long long outFileSize = 0;
1381 lzma_stream strm = LZMA_STREAM_INIT;
1382 lzma_action action = LZMA_RUN;
1383 lzma_ret initRet;
1384 int decodingError = 0;
1385
1386 strm.next_in = 0;
1387 strm.avail_in = 0;
1388 if (plain_lzma) {
1389 initRet = lzma_alone_decoder(&strm, UINT64_MAX); /* LZMA */
1390 } else {
1391 initRet = lzma_stream_decoder(&strm, UINT64_MAX, 0); /* XZ */
1392 }
1393
1394 if (initRet != LZMA_OK) {
1395 DISPLAYLEVEL(1, "zstd: %s: %s error %d \n",
1396 plain_lzma ? "lzma_alone_decoder" : "lzma_stream_decoder",
1397 srcFileName, initRet);
1398 return FIO_ERROR_FRAME_DECODING;
1399 }
1400
1401 strm.next_out = (BYTE*)ress->dstBuffer;
1402 strm.avail_out = ress->dstBufferSize;
1403 strm.next_in = (BYTE const*)ress->srcBuffer;
1404 strm.avail_in = ress->srcBufferLoaded;
1405
1406 for ( ; ; ) {
1407 lzma_ret ret;
1408 if (strm.avail_in == 0) {
1409 ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
1410 if (ress->srcBufferLoaded == 0) action = LZMA_FINISH;
1411 strm.next_in = (BYTE const*)ress->srcBuffer;
1412 strm.avail_in = ress->srcBufferLoaded;
1413 }
1414 ret = lzma_code(&strm, action);
1415
1416 if (ret == LZMA_BUF_ERROR) {
1417 DISPLAYLEVEL(1, "zstd: %s: premature lzma end \n", srcFileName);
1418 decodingError = 1; break;
1419 }
1420 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
1421 DISPLAYLEVEL(1, "zstd: %s: lzma_code decoding error %d \n",
1422 srcFileName, ret);
1423 decodingError = 1; break;
1424 }
1425 { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
1426 if (decompBytes) {
1427 if (fwrite(ress->dstBuffer, 1, decompBytes, ress->dstFile) != decompBytes) {
1428 DISPLAYLEVEL(1, "zstd: %s \n", strerror(errno));
1429 decodingError = 1; break;
1430 }
1431 outFileSize += decompBytes;
1432 strm.next_out = (BYTE*)ress->dstBuffer;
1433 strm.avail_out = ress->dstBufferSize;
1434 } }
1435 if (ret == LZMA_STREAM_END) break;
1436 }
1437
1438 if (strm.avail_in > 0)
1439 memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
1440 ress->srcBufferLoaded = strm.avail_in;
1441 lzma_end(&strm);
1442 return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
1443}
1444#endif
1445
1446#ifdef ZSTD_LZ4DECOMPRESS
1447static unsigned long long FIO_decompressLz4Frame(dRess_t* ress,
1448 FILE* srcFile, const char* srcFileName)
7c673cae 1449{
7c673cae 1450 unsigned long long filesize = 0;
11fdf7f2
TL
1451 LZ4F_errorCode_t nextToLoad;
1452 LZ4F_decompressionContext_t dCtx;
1453 LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
1454 int decodingError = 0;
1455
1456 if (LZ4F_isError(errorCode)) {
1457 DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
1458 return FIO_ERROR_FRAME_DECODING;
1459 }
7c673cae 1460
11fdf7f2
TL
1461 /* Init feed with magic number (already consumed from FILE* sFile) */
1462 { size_t inSize = 4;
1463 size_t outSize= 0;
1464 MEM_writeLE32(ress->srcBuffer, LZ4_MAGICNUMBER);
1465 nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &outSize, ress->srcBuffer, &inSize, NULL);
1466 if (LZ4F_isError(nextToLoad)) {
1467 DISPLAYLEVEL(1, "zstd: %s: lz4 header error : %s \n",
1468 srcFileName, LZ4F_getErrorName(nextToLoad));
1469 LZ4F_freeDecompressionContext(dCtx);
1470 return FIO_ERROR_FRAME_DECODING;
1471 } }
1472
1473 /* Main Loop */
1474 for (;nextToLoad;) {
1475 size_t readSize;
1476 size_t pos = 0;
1477 size_t decodedBytes = ress->dstBufferSize;
1478
1479 /* Read input */
1480 if (nextToLoad > ress->srcBufferSize) nextToLoad = ress->srcBufferSize;
1481 readSize = fread(ress->srcBuffer, 1, nextToLoad, srcFile);
1482 if (!readSize) break; /* reached end of file or stream */
1483
1484 while ((pos < readSize) || (decodedBytes == ress->dstBufferSize)) { /* still to read, or still to flush */
1485 /* Decode Input (at least partially) */
1486 size_t remaining = readSize - pos;
1487 decodedBytes = ress->dstBufferSize;
1488 nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &decodedBytes, (char*)(ress->srcBuffer)+pos, &remaining, NULL);
1489 if (LZ4F_isError(nextToLoad)) {
1490 DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
1491 srcFileName, LZ4F_getErrorName(nextToLoad));
1492 decodingError = 1; break;
1493 }
1494 pos += remaining;
1495
1496 /* Write Block */
1497 if (decodedBytes) {
1498 if (fwrite(ress->dstBuffer, 1, decodedBytes, ress->dstFile) != decodedBytes) {
1499 DISPLAYLEVEL(1, "zstd: %s \n", strerror(errno));
1500 decodingError = 1; break;
1501 }
1502 filesize += decodedBytes;
1503 DISPLAYUPDATE(2, "\rDecompressed : %u MB ", (unsigned)(filesize>>20));
1504 }
1505
1506 if (!nextToLoad) break;
1507 }
1508 }
1509 /* can be out because readSize == 0, which could be an fread() error */
1510 if (ferror(srcFile)) {
1511 DISPLAYLEVEL(1, "zstd: %s: read error \n", srcFileName);
1512 decodingError=1;
7c673cae
FG
1513 }
1514
11fdf7f2
TL
1515 if (nextToLoad!=0) {
1516 DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
1517 decodingError=1;
1518 }
1519
1520 LZ4F_freeDecompressionContext(dCtx);
1521 ress->srcBufferLoaded = 0; /* LZ4F will reach exact frame boundary */
1522
1523 return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
1524}
1525#endif
1526
1527
1528
1529/** FIO_decompressFrames() :
1530 * Find and decode frames inside srcFile
1531 * srcFile presumed opened and valid
1532 * @return : 0 : OK
1533 * 1 : error
1534 */
1535static int FIO_decompressFrames(dRess_t ress, FILE* srcFile,
1536 const char* dstFileName, const char* srcFileName)
1537{
1538 unsigned readSomething = 0;
1539 unsigned long long filesize = 0;
1540 assert(srcFile != NULL);
7c673cae
FG
1541
1542 /* for each frame */
1543 for ( ; ; ) {
1544 /* check magic number -> version */
1545 size_t const toRead = 4;
11fdf7f2
TL
1546 const BYTE* const buf = (const BYTE*)ress.srcBuffer;
1547 if (ress.srcBufferLoaded < toRead) /* load up to 4 bytes for header */
1548 ress.srcBufferLoaded += fread((char*)ress.srcBuffer + ress.srcBufferLoaded,
1549 (size_t)1, toRead - ress.srcBufferLoaded, srcFile);
7c673cae 1550 if (ress.srcBufferLoaded==0) {
11fdf7f2
TL
1551 if (readSomething==0) { /* srcFile is empty (which is invalid) */
1552 DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
1553 return 1;
1554 } /* else, just reached frame boundary */
7c673cae
FG
1555 break; /* no more input */
1556 }
11fdf7f2
TL
1557 readSomething = 1; /* there is at least 1 byte in srcFile */
1558 if (ress.srcBufferLoaded < toRead) {
1559 DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
1560 return 1;
1561 }
1562 if (ZSTD_isFrame(buf, ress.srcBufferLoaded)) {
1563 unsigned long long const frameSize = FIO_decompressZstdFrame(&ress, srcFile, srcFileName, filesize);
1564 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
1565 filesize += frameSize;
1566 } else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
7c673cae 1567#ifdef ZSTD_GZDECOMPRESS
11fdf7f2
TL
1568 unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFile, srcFileName);
1569 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
1570 filesize += frameSize;
1571#else
1572 DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
1573 return 1;
1574#endif
1575 } else if ((buf[0] == 0xFD && buf[1] == 0x37) /* xz magic number */
1576 || (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
1577#ifdef ZSTD_LZMADECOMPRESS
1578 unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFile, srcFileName, buf[0] != 0xFD);
1579 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
1580 filesize += frameSize;
1581#else
1582 DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
1583 return 1;
1584#endif
1585 } else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
1586#ifdef ZSTD_LZ4DECOMPRESS
1587 unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFile, srcFileName);
1588 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
1589 filesize += frameSize;
7c673cae 1590#else
11fdf7f2 1591 DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
7c673cae
FG
1592 return 1;
1593#endif
11fdf7f2
TL
1594 } else if ((g_overwrite) && !strcmp (dstFileName, stdoutmark)) { /* pass-through mode */
1595 return FIO_passThrough(ress.dstFile, srcFile,
1596 ress.srcBuffer, ress.srcBufferSize, ress.srcBufferLoaded);
7c673cae 1597 } else {
11fdf7f2
TL
1598 DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
1599 return 1;
1600 } } /* for each frame */
7c673cae
FG
1601
1602 /* Final Status */
1603 DISPLAYLEVEL(2, "\r%79s\r", "");
1604 DISPLAYLEVEL(2, "%-20s: %llu bytes \n", srcFileName, filesize);
1605
7c673cae
FG
1606 return 0;
1607}
1608
1609
11fdf7f2
TL
1610/** FIO_decompressSrcFile() :
1611 Decompression `srcFileName` into `ress.dstFile`
1612 @return : 0 : OK
1613 1 : operation not started
1614*/
1615static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const char* srcFileName)
1616{
1617 FILE* srcFile;
1618 int result;
1619
1620 if (UTIL_isDirectory(srcFileName)) {
1621 DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
1622 return 1;
1623 }
1624
1625 srcFile = FIO_openSrcFile(srcFileName);
1626 if (srcFile==NULL) return 1;
1627
1628 result = FIO_decompressFrames(ress, srcFile, dstFileName, srcFileName);
1629
1630 /* Close file */
1631 if (fclose(srcFile)) {
1632 DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); /* error should not happen */
1633 return 1;
1634 }
1635 if ( g_removeSrcFile /* --rm */
1636 && (result==0) /* decompression successful */
1637 && strcmp(srcFileName, stdinmark) ) /* not stdin */ {
1638 if (remove(srcFileName)) {
1639 /* failed to remove src file */
1640 DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
1641 return 1;
1642 } }
1643 return result;
1644}
1645
1646
7c673cae
FG
1647/** FIO_decompressFile_extRess() :
1648 decompress `srcFileName` into `dstFileName`
1649 @return : 0 : OK
1650 1 : operation aborted (src not available, dst already taken, etc.)
1651*/
1652static int FIO_decompressDstFile(dRess_t ress,
1653 const char* dstFileName, const char* srcFileName)
1654{
1655 int result;
1656 stat_t statbuf;
1657 int stat_result = 0;
1658
1659 ress.dstFile = FIO_openDstFile(dstFileName);
1660 if (ress.dstFile==0) return 1;
1661
11fdf7f2
TL
1662 if (UTIL_isRegularFile(dstFileName)) {
1663 g_artefact = dstFileName;
1664 signal(SIGINT, INThandler);
1665 } else {
1666 g_artefact = NULL;
1667 }
1668
1669 if ( strcmp(srcFileName, stdinmark)
1670 && UTIL_getFileStat(srcFileName, &statbuf) )
1671 stat_result = 1;
7c673cae
FG
1672 result = FIO_decompressSrcFile(ress, dstFileName, srcFileName);
1673
11fdf7f2
TL
1674 if (fclose(ress.dstFile)) {
1675 DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
1676 result = 1;
1677 }
1678
1679 if ( (result != 0) /* operation failure */
1680 && strcmp(dstFileName, nulmark) /* special case : don't remove() /dev/null (#316) */
1681 && strcmp(dstFileName, stdoutmark) ) /* special case : don't remove() stdout */
1682 remove(dstFileName); /* remove decompression artefact; note don't do anything special if remove() fails */
1683 else { /* operation success */
1684 if ( strcmp(dstFileName, stdoutmark) /* special case : don't chmod stdout */
1685 && strcmp(dstFileName, nulmark) /* special case : don't chmod /dev/null */
1686 && stat_result ) /* file permissions correctly extracted from src */
1687 UTIL_setFileStat(dstFileName, &statbuf); /* transfer file permissions from src into dst */
1688 }
1689
1690 signal(SIGINT, SIG_DFL);
7c673cae 1691
7c673cae
FG
1692 return result;
1693}
1694
1695
1696int FIO_decompressFilename(const char* dstFileName, const char* srcFileName,
1697 const char* dictFileName)
1698{
11fdf7f2 1699 dRess_t const ress = FIO_createDResources(dictFileName);
7c673cae 1700
11fdf7f2 1701 int const decodingError = FIO_decompressDstFile(ress, dstFileName, srcFileName);
7c673cae
FG
1702
1703 FIO_freeDResources(ress);
11fdf7f2 1704 return decodingError;
7c673cae
FG
1705}
1706
1707
1708#define MAXSUFFIXSIZE 8
1709int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles,
1710 const char* suffix,
1711 const char* dictFileName)
1712{
1713 int skippedFiles = 0;
1714 int missingFiles = 0;
1715 dRess_t ress = FIO_createDResources(dictFileName);
1716
11fdf7f2
TL
1717 if (suffix==NULL)
1718 EXM_THROW(70, "zstd: decompression: unknown dst"); /* should never happen */
7c673cae
FG
1719
1720 if (!strcmp(suffix, stdoutmark) || !strcmp(suffix, nulmark)) { /* special cases : -c or -t */
1721 unsigned u;
1722 ress.dstFile = FIO_openDstFile(suffix);
1723 if (ress.dstFile == 0) EXM_THROW(71, "cannot open %s", suffix);
1724 for (u=0; u<nbFiles; u++)
1725 missingFiles += FIO_decompressSrcFile(ress, suffix, srcNamesTable[u]);
11fdf7f2
TL
1726 if (fclose(ress.dstFile))
1727 EXM_THROW(72, "Write error : cannot properly close stdout");
7c673cae 1728 } else {
11fdf7f2 1729 size_t suffixSize;
7c673cae
FG
1730 size_t dfnSize = FNSPACE;
1731 unsigned u;
1732 char* dstFileName = (char*)malloc(FNSPACE);
11fdf7f2
TL
1733 if (dstFileName==NULL)
1734 EXM_THROW(73, "not enough memory for dstFileName");
7c673cae
FG
1735 for (u=0; u<nbFiles; u++) { /* create dstFileName */
1736 const char* const srcFileName = srcNamesTable[u];
11fdf7f2 1737 const char* const suffixPtr = strrchr(srcFileName, '.');
7c673cae 1738 size_t const sfnSize = strlen(srcFileName);
11fdf7f2
TL
1739 if (!suffixPtr) {
1740 DISPLAYLEVEL(1, "zstd: %s: unknown suffix -- ignored \n",
1741 srcFileName);
1742 skippedFiles++;
1743 continue;
1744 }
1745 suffixSize = strlen(suffixPtr);
7c673cae
FG
1746 if (dfnSize+suffixSize <= sfnSize+1) {
1747 free(dstFileName);
1748 dfnSize = sfnSize + 20;
1749 dstFileName = (char*)malloc(dfnSize);
11fdf7f2
TL
1750 if (dstFileName==NULL)
1751 EXM_THROW(74, "not enough memory for dstFileName");
7c673cae 1752 }
11fdf7f2
TL
1753 if (sfnSize <= suffixSize
1754 || (strcmp(suffixPtr, GZ_EXTENSION)
1755 && strcmp(suffixPtr, XZ_EXTENSION)
1756 && strcmp(suffixPtr, ZSTD_EXTENSION)
1757 && strcmp(suffixPtr, LZMA_EXTENSION)
1758 && strcmp(suffixPtr, LZ4_EXTENSION)) ) {
1759 DISPLAYLEVEL(1, "zstd: %s: unknown suffix (%s/%s/%s/%s/%s expected) -- ignored \n",
1760 srcFileName, GZ_EXTENSION, XZ_EXTENSION, ZSTD_EXTENSION, LZMA_EXTENSION, LZ4_EXTENSION);
1761 skippedFiles++;
1762 continue;
7c673cae
FG
1763 } else {
1764 memcpy(dstFileName, srcFileName, sfnSize - suffixSize);
1765 dstFileName[sfnSize-suffixSize] = '\0';
1766 }
7c673cae
FG
1767 missingFiles += FIO_decompressDstFile(ress, dstFileName, srcFileName);
1768 }
1769 free(dstFileName);
1770 }
1771
1772 FIO_freeDResources(ress);
1773 return missingFiles + skippedFiles;
1774}
1775
11fdf7f2
TL
1776
1777
1778/* **************************************************************************
1779 * .zst file info (--list command)
1780 ***************************************************************************/
1781
1782typedef struct {
1783 U64 decompressedSize;
1784 U64 compressedSize;
1785 U64 windowSize;
1786 int numActualFrames;
1787 int numSkippableFrames;
1788 int decompUnavailable;
1789 int usesCheck;
1790 U32 nbFiles;
1791} fileInfo_t;
1792
1793/** getFileInfo() :
1794 * Reads information from file, stores in *info
1795 * @return : 0 if successful
1796 * 1 for frame analysis error
1797 * 2 for file not compressed with zstd
1798 * 3 for cases in which file could not be opened.
1799 */
1800static int getFileInfo(fileInfo_t* info, const char* inFileName){
1801 int detectError = 0;
1802 FILE* const srcFile = FIO_openSrcFile(inFileName);
1803 if (srcFile == NULL) {
1804 DISPLAY("Error: could not open source file %s\n", inFileName);
1805 return 3;
1806 }
1807 info->compressedSize = UTIL_getFileSize(inFileName);
1808
1809 /* begin analyzing frame */
1810 for ( ; ; ) {
1811 BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
1812 size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
1813 if (numBytesRead < ZSTD_frameHeaderSize_min) {
1814 if ( feof(srcFile)
1815 && (numBytesRead == 0)
1816 && (info->compressedSize > 0) ) {
1817 break;
1818 }
1819 else if (feof(srcFile)) {
1820 DISPLAY("Error: reached end of file with incomplete frame\n");
1821 detectError = 2;
1822 break;
1823 }
1824 else {
1825 DISPLAY("Error: did not reach end of file but ran out of frames\n");
1826 detectError = 1;
1827 break;
1828 }
1829 }
1830 { U32 const magicNumber = MEM_readLE32(headerBuffer);
1831 /* Zstandard frame */
1832 if (magicNumber == ZSTD_MAGICNUMBER) {
1833 ZSTD_frameHeader header;
1834 U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
1835 if (frameContentSize == ZSTD_CONTENTSIZE_ERROR || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
1836 info->decompUnavailable = 1;
1837 } else {
1838 info->decompressedSize += frameContentSize;
1839 }
1840 if (ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0) {
1841 DISPLAY("Error: could not decode frame header\n");
1842 detectError = 1;
1843 break;
1844 }
1845 info->windowSize = header.windowSize;
1846 /* move to the end of the frame header */
1847 { size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
1848 if (ZSTD_isError(headerSize)) {
1849 DISPLAY("Error: could not determine frame header size\n");
1850 detectError = 1;
1851 break;
1852 }
1853 { int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR);
1854 if (ret != 0) {
1855 DISPLAY("Error: could not move to end of frame header\n");
1856 detectError = 1;
1857 break;
1858 } } }
1859
1860 /* skip the rest of the blocks in the frame */
1861 { int lastBlock = 0;
1862 do {
1863 BYTE blockHeaderBuffer[3];
1864 size_t const readBytes = fread(blockHeaderBuffer, 1, 3, srcFile);
1865 if (readBytes != 3) {
1866 DISPLAY("There was a problem reading the block header\n");
1867 detectError = 1;
1868 break;
1869 }
1870 { U32 const blockHeader = MEM_readLE24(blockHeaderBuffer);
1871 U32 const blockTypeID = (blockHeader >> 1) & 3;
1872 U32 const isRLE = (blockTypeID == 1);
1873 U32 const isWrongBlock = (blockTypeID == 3);
1874 long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3);
1875 if (isWrongBlock) {
1876 DISPLAY("Error: unsupported block type \n");
1877 detectError = 1;
1878 break;
1879 }
1880 lastBlock = blockHeader & 1;
1881 { int const ret = fseek(srcFile, blockSize, SEEK_CUR);
1882 if (ret != 0) {
1883 DISPLAY("Error: could not skip to end of block\n");
1884 detectError = 1;
1885 break;
1886 } } }
1887 } while (lastBlock != 1);
1888
1889 if (detectError) break;
1890 }
1891
1892 /* check if checksum is used */
1893 { BYTE const frameHeaderDescriptor = headerBuffer[4];
1894 int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
1895 if (contentChecksumFlag) {
1896 int const ret = fseek(srcFile, 4, SEEK_CUR);
1897 info->usesCheck = 1;
1898 if (ret != 0) {
1899 DISPLAY("Error: could not skip past checksum\n");
1900 detectError = 1;
1901 break;
1902 } } }
1903 info->numActualFrames++;
1904 }
1905 /* Skippable frame */
1906 else if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) {
1907 U32 const frameSize = MEM_readLE32(headerBuffer + 4);
1908 long const seek = (long)(8 + frameSize - numBytesRead);
1909 int const ret = LONG_SEEK(srcFile, seek, SEEK_CUR);
1910 if (ret != 0) {
1911 DISPLAY("Error: could not find end of skippable frame\n");
1912 detectError = 1;
1913 break;
1914 }
1915 info->numSkippableFrames++;
1916 }
1917 /* unknown content */
1918 else {
1919 detectError = 2;
1920 break;
1921 }
1922 }
1923 } /* end analyzing frame */
1924 fclose(srcFile);
1925 info->nbFiles = 1;
1926 return detectError;
1927}
1928
1929static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){
1930 unsigned const unit = info->compressedSize < (1 MB) ? (1 KB) : (1 MB);
1931 const char* const unitStr = info->compressedSize < (1 MB) ? "KB" : "MB";
1932 double const windowSizeUnit = (double)info->windowSize / unit;
1933 double const compressedSizeUnit = (double)info->compressedSize / unit;
1934 double const decompressedSizeUnit = (double)info->decompressedSize / unit;
1935 double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/info->compressedSize;
1936 const char* const checkString = (info->usesCheck ? "XXH64" : "None");
1937 if (displayLevel <= 2) {
1938 if (!info->decompUnavailable) {
1939 DISPLAYOUT("%6d %5d %7.2f %2s %9.2f %2s %5.3f %5s %s\n",
1940 info->numSkippableFrames + info->numActualFrames,
1941 info->numSkippableFrames,
1942 compressedSizeUnit, unitStr, decompressedSizeUnit, unitStr,
1943 ratio, checkString, inFileName);
1944 } else {
1945 DISPLAYOUT("%6d %5d %7.2f %2s %5s %s\n",
1946 info->numSkippableFrames + info->numActualFrames,
1947 info->numSkippableFrames,
1948 compressedSizeUnit, unitStr,
1949 checkString, inFileName);
1950 }
1951 } else {
1952 DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
1953 DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
1954 DISPLAYOUT("Window Size: %.2f %2s (%llu B)\n",
1955 windowSizeUnit, unitStr,
1956 (unsigned long long)info->windowSize);
1957 DISPLAYOUT("Compressed Size: %.2f %2s (%llu B)\n",
1958 compressedSizeUnit, unitStr,
1959 (unsigned long long)info->compressedSize);
1960 if (!info->decompUnavailable) {
1961 DISPLAYOUT("Decompressed Size: %.2f %2s (%llu B)\n",
1962 decompressedSizeUnit, unitStr,
1963 (unsigned long long)info->decompressedSize);
1964 DISPLAYOUT("Ratio: %.4f\n", ratio);
1965 }
1966 DISPLAYOUT("Check: %s\n", checkString);
1967 DISPLAYOUT("\n");
1968 }
1969}
1970
1971static fileInfo_t FIO_addFInfo(fileInfo_t fi1, fileInfo_t fi2)
1972{
1973 fileInfo_t total;
1974 total.numActualFrames = fi1.numActualFrames + fi2.numActualFrames;
1975 total.numSkippableFrames = fi1.numSkippableFrames + fi2.numSkippableFrames;
1976 total.compressedSize = fi1.compressedSize + fi2.compressedSize;
1977 total.decompressedSize = fi1.decompressedSize + fi2.decompressedSize;
1978 total.decompUnavailable = fi1.decompUnavailable | fi2.decompUnavailable;
1979 total.usesCheck = fi1.usesCheck & fi2.usesCheck;
1980 total.nbFiles = fi1.nbFiles + fi2.nbFiles;
1981 return total;
1982}
1983
1984static int FIO_listFile(fileInfo_t* total, const char* inFileName, int displayLevel){
1985 /* initialize info to avoid warnings */
1986 fileInfo_t info;
1987 memset(&info, 0, sizeof(info));
1988 { int const error = getFileInfo(&info, inFileName);
1989 if (error == 1) {
1990 /* display error, but provide output */
1991 DISPLAY("An error occurred while getting file info \n");
1992 }
1993 else if (error == 2) {
1994 DISPLAYOUT("File %s not compressed by zstd \n", inFileName);
1995 if (displayLevel > 2) DISPLAYOUT("\n");
1996 return 1;
1997 }
1998 else if (error == 3) {
1999 /* error occurred while opening the file */
2000 if (displayLevel > 2) DISPLAYOUT("\n");
2001 return 1;
2002 }
2003 displayInfo(inFileName, &info, displayLevel);
2004 *total = FIO_addFInfo(*total, info);
2005 return error;
2006 }
2007}
2008
2009int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel){
2010 if (numFiles == 0) {
2011 DISPLAYOUT("No files given\n");
2012 return 0;
2013 }
2014 if (displayLevel <= 2) {
2015 DISPLAYOUT("Frames Skips Compressed Uncompressed Ratio Check Filename\n");
2016 }
2017 { int error = 0;
2018 unsigned u;
2019 fileInfo_t total;
2020 memset(&total, 0, sizeof(total));
2021 total.usesCheck = 1;
2022 for (u=0; u<numFiles;u++) {
2023 error |= FIO_listFile(&total, filenameTable[u], displayLevel);
2024 }
2025 if (numFiles > 1 && displayLevel <= 2) {
2026 unsigned const unit = total.compressedSize < (1 MB) ? (1 KB) : (1 MB);
2027 const char* const unitStr = total.compressedSize < (1 MB) ? "KB" : "MB";
2028 double const compressedSizeUnit = (double)total.compressedSize / unit;
2029 double const decompressedSizeUnit = (double)total.decompressedSize / unit;
2030 double const ratio = (total.compressedSize == 0) ? 0 : ((double)total.decompressedSize)/total.compressedSize;
2031 const char* const checkString = (total.usesCheck ? "XXH64" : "");
2032 DISPLAYOUT("----------------------------------------------------------------- \n");
2033 if (total.decompUnavailable) {
2034 DISPLAYOUT("%6d %5d %7.2f %2s %5s %u files\n",
2035 total.numSkippableFrames + total.numActualFrames,
2036 total.numSkippableFrames,
2037 compressedSizeUnit, unitStr,
2038 checkString, total.nbFiles);
2039 } else {
2040 DISPLAYOUT("%6d %5d %7.2f %2s %9.2f %2s %5.3f %5s %u files\n",
2041 total.numSkippableFrames + total.numActualFrames,
2042 total.numSkippableFrames,
2043 compressedSizeUnit, unitStr, decompressedSizeUnit, unitStr,
2044 ratio, checkString, total.nbFiles);
2045 }
2046 }
2047 return error;
2048 }
2049}
2050
2051
7c673cae 2052#endif /* #ifndef ZSTD_NODECOMPRESS */