1 /*
2 LZ4io.c - LZ4 File/Stream Interface
3 Copyright (C) Yann Collet 2011-2017
4
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 :
22 - LZ4 source repository : https://github.com/lz4/lz4
23 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
24 */
25 /*
26 Note : this is stand-alone program.
27 It is not part of LZ4 compression library, it is a user code of the LZ4 library.
28 - The license of LZ4 library is BSD.
29 - The license of xxHash library is BSD.
30 - The license of this source file is GPLv2.
31 */
32
33
34 /*-************************************
35 * Compiler options
36 **************************************/
37 #ifdef _MSC_VER /* Visual Studio */
38 # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
39 #endif
40 #if defined(__MINGW32__) && !defined(_POSIX_SOURCE)
41 # define _POSIX_SOURCE 1 /* disable %llu warnings with MinGW on Windows */
42 #endif
43
44
45 /*****************************
46 * Includes
47 *****************************/
48 #include "platform.h" /* Large File Support, SET_BINARY_MODE, SET_SPARSE_FILE_MODE, PLATFORM_POSIX_VERSION, __64BIT__ */
49 #include "util.h" /* UTIL_getFileStat, UTIL_setFileStat */
50 #include <stdio.h> /* fprintf, fopen, fread, stdin, stdout, fflush, getchar */
51 #include <stdlib.h> /* malloc, free */
52 #include <string.h> /* strerror, strcmp, strlen */
53 #include <time.h> /* clock */
54 #include <sys/types.h> /* stat64 */
55 #include <sys/stat.h> /* stat64 */
56 #include "lz4.h" /* still required for legacy format */
57 #include "lz4hc.h" /* still required for legacy format */
58 #define LZ4F_STATIC_LINKING_ONLY
59 #include "lz4frame.h"
60 #include "lz4io.h"
61
62
63 /*****************************
64 * Constants
65 *****************************/
66 #define KB *(1 <<10)
67 #define MB *(1 <<20)
68 #define GB *(1U<<30)
69
70 #define _1BIT 0x01
71 #define _2BITS 0x03
72 #define _3BITS 0x07
73 #define _4BITS 0x0F
74 #define _8BITS 0xFF
75
76 #define MAGICNUMBER_SIZE 4
77 #define LZ4IO_MAGICNUMBER 0x184D2204
78 #define LZ4IO_SKIPPABLE0 0x184D2A50
79 #define LZ4IO_SKIPPABLEMASK 0xFFFFFFF0
80 #define LEGACY_MAGICNUMBER 0x184C2102
81
82 #define CACHELINE 64
83 #define LEGACY_BLOCKSIZE (8 MB)
84 #define MIN_STREAM_BUFSIZE (192 KB)
85 #define LZ4IO_BLOCKSIZEID_DEFAULT 7
86 #define LZ4_MAX_DICT_SIZE (64 KB)
87
88
89 /**************************************
90 * Macros
91 **************************************/
92 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
93 #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
94 static int g_displayLevel = 0; /* 0 : no display ; 1: errors ; 2 : + result + interaction + warnings ; 3 : + progression; 4 : + information */
95
96 #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
97 if ( ((clock() - g_time) > refreshRate) \
98 || (g_displayLevel>=4) ) { \
99 g_time = clock(); \
100 DISPLAY(__VA_ARGS__); \
101 if (g_displayLevel>=4) fflush(stderr); \
102 } }
103 static const clock_t refreshRate = CLOCKS_PER_SEC / 6;
104 static clock_t g_time = 0;
105 #define LZ4IO_STATIC_ASSERT(c) { enum { LZ4IO_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */
106
107
108 /**************************************
109 * Local Parameters
110 **************************************/
111
112 struct LZ4IO_prefs_s {
113 int passThrough;
114 int overwrite;
115 int testMode;
116 int blockSizeId;
117 size_t blockSize;
118 int blockChecksum;
119 int streamChecksum;
120 int blockIndependence;
121 int sparseFileSupport;
122 int contentSizeFlag;
123 int useDictionary;
124 unsigned favorDecSpeed;
125 const char* dictionaryFilename;
126 int removeSrcFile;
127 };
128
129 /**************************************
130 * Exceptions
131 ***************************************/
132 #ifndef DEBUG
133 # define DEBUG 0
134 #endif
135 #define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
136 #define EXM_THROW(error, ...) \
137 { \
138 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
139 DISPLAYLEVEL(1, "Error %i : ", error); \
140 DISPLAYLEVEL(1, __VA_ARGS__); \
141 DISPLAYLEVEL(1, " \n"); \
142 exit(error); \
143 }
144
145
146 /**************************************
147 * Version modifiers
148 **************************************/
149 #define EXTENDED_ARGUMENTS
150 #define EXTENDED_HELP
151 #define EXTENDED_FORMAT
152 #define DEFAULT_DECOMPRESSOR LZ4IO_decompressLZ4F
153
154
155 /* ************************************************** */
156 /* ****************** Parameters ******************** */
157 /* ************************************************** */
158
LZ4IO_defaultPreferences(void)159 LZ4IO_prefs_t* LZ4IO_defaultPreferences(void)
160 {
161 LZ4IO_prefs_t* const ret = (LZ4IO_prefs_t*)malloc(sizeof(LZ4IO_prefs_t));
162 if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
163 ret->passThrough = 0;
164 ret->overwrite = 1;
165 ret->testMode = 0;
166 ret->blockSizeId = LZ4IO_BLOCKSIZEID_DEFAULT;
167 ret->blockSize = 0;
168 ret->blockChecksum = 0;
169 ret->streamChecksum = 1;
170 ret->blockIndependence = 1;
171 ret->sparseFileSupport = 1;
172 ret->contentSizeFlag = 0;
173 ret->useDictionary = 0;
174 ret->favorDecSpeed = 0;
175 ret->dictionaryFilename = NULL;
176 ret->removeSrcFile = 0;
177 return ret;
178 }
179
LZ4IO_freePreferences(LZ4IO_prefs_t * const prefs)180 void LZ4IO_freePreferences(LZ4IO_prefs_t* const prefs)
181 {
182 free(prefs);
183 }
184
185
LZ4IO_setDictionaryFilename(LZ4IO_prefs_t * const prefs,const char * dictionaryFilename)186 int LZ4IO_setDictionaryFilename(LZ4IO_prefs_t* const prefs, const char* dictionaryFilename)
187 {
188 prefs->dictionaryFilename = dictionaryFilename;
189 prefs->useDictionary = dictionaryFilename != NULL;
190 return prefs->useDictionary;
191 }
192
193 /* Default setting : passThrough = 0; return : passThrough mode (0/1) */
LZ4IO_setPassThrough(LZ4IO_prefs_t * const prefs,int yes)194 int LZ4IO_setPassThrough(LZ4IO_prefs_t* const prefs, int yes)
195 {
196 prefs->passThrough = (yes!=0);
197 return prefs->passThrough;
198 }
199
200
201 /* Default setting : overwrite = 1; return : overwrite mode (0/1) */
LZ4IO_setOverwrite(LZ4IO_prefs_t * const prefs,int yes)202 int LZ4IO_setOverwrite(LZ4IO_prefs_t* const prefs, int yes)
203 {
204 prefs->overwrite = (yes!=0);
205 return prefs->overwrite;
206 }
207
208 /* Default setting : testMode = 0; return : testMode (0/1) */
LZ4IO_setTestMode(LZ4IO_prefs_t * const prefs,int yes)209 int LZ4IO_setTestMode(LZ4IO_prefs_t* const prefs, int yes)
210 {
211 prefs->testMode = (yes!=0);
212 return prefs->testMode;
213 }
214
215 /* blockSizeID : valid values : 4-5-6-7 */
LZ4IO_setBlockSizeID(LZ4IO_prefs_t * const prefs,unsigned bsid)216 size_t LZ4IO_setBlockSizeID(LZ4IO_prefs_t* const prefs, unsigned bsid)
217 {
218 static const size_t blockSizeTable[] = { 64 KB, 256 KB, 1 MB, 4 MB };
219 static const unsigned minBlockSizeID = 4;
220 static const unsigned maxBlockSizeID = 7;
221 if ((bsid < minBlockSizeID) || (bsid > maxBlockSizeID)) return 0;
222 prefs->blockSizeId = (int)bsid;
223 prefs->blockSize = blockSizeTable[(unsigned)prefs->blockSizeId-minBlockSizeID];
224 return prefs->blockSize;
225 }
226
LZ4IO_setBlockSize(LZ4IO_prefs_t * const prefs,size_t blockSize)227 size_t LZ4IO_setBlockSize(LZ4IO_prefs_t* const prefs, size_t blockSize)
228 {
229 static const size_t minBlockSize = 32;
230 static const size_t maxBlockSize = 4 MB;
231 unsigned bsid = 0;
232 if (blockSize < minBlockSize) blockSize = minBlockSize;
233 if (blockSize > maxBlockSize) blockSize = maxBlockSize;
234 prefs->blockSize = blockSize;
235 blockSize--;
236 /* find which of { 64k, 256k, 1MB, 4MB } is closest to blockSize */
237 while (blockSize >>= 2)
238 bsid++;
239 if (bsid < 7) bsid = 7;
240 prefs->blockSizeId = (int)(bsid-3);
241 return prefs->blockSize;
242 }
243
LZ4IO_setBlockMode(LZ4IO_prefs_t * const prefs,LZ4IO_blockMode_t blockMode)244 int LZ4IO_setBlockMode(LZ4IO_prefs_t* const prefs, LZ4IO_blockMode_t blockMode)
245 {
246 prefs->blockIndependence = (blockMode == LZ4IO_blockIndependent);
247 return prefs->blockIndependence;
248 }
249
250 /* Default setting : no block checksum */
LZ4IO_setBlockChecksumMode(LZ4IO_prefs_t * const prefs,int enable)251 int LZ4IO_setBlockChecksumMode(LZ4IO_prefs_t* const prefs, int enable)
252 {
253 prefs->blockChecksum = (enable != 0);
254 return prefs->blockChecksum;
255 }
256
257 /* Default setting : checksum enabled */
LZ4IO_setStreamChecksumMode(LZ4IO_prefs_t * const prefs,int enable)258 int LZ4IO_setStreamChecksumMode(LZ4IO_prefs_t* const prefs, int enable)
259 {
260 prefs->streamChecksum = (enable != 0);
261 return prefs->streamChecksum;
262 }
263
264 /* Default setting : 0 (no notification) */
LZ4IO_setNotificationLevel(int level)265 int LZ4IO_setNotificationLevel(int level)
266 {
267 g_displayLevel = level;
268 return g_displayLevel;
269 }
270
271 /* Default setting : 0 (disabled) */
LZ4IO_setSparseFile(LZ4IO_prefs_t * const prefs,int enable)272 int LZ4IO_setSparseFile(LZ4IO_prefs_t* const prefs, int enable)
273 {
274 prefs->sparseFileSupport = (enable!=0);
275 return prefs->sparseFileSupport;
276 }
277
278 /* Default setting : 0 (disabled) */
LZ4IO_setContentSize(LZ4IO_prefs_t * const prefs,int enable)279 int LZ4IO_setContentSize(LZ4IO_prefs_t* const prefs, int enable)
280 {
281 prefs->contentSizeFlag = (enable!=0);
282 return prefs->contentSizeFlag;
283 }
284
285 /* Default setting : 0 (disabled) */
LZ4IO_favorDecSpeed(LZ4IO_prefs_t * const prefs,int favor)286 void LZ4IO_favorDecSpeed(LZ4IO_prefs_t* const prefs, int favor)
287 {
288 prefs->favorDecSpeed = (favor!=0);
289 }
290
LZ4IO_setRemoveSrcFile(LZ4IO_prefs_t * const prefs,unsigned flag)291 void LZ4IO_setRemoveSrcFile(LZ4IO_prefs_t* const prefs, unsigned flag)
292 {
293 prefs->removeSrcFile = (flag>0);
294 }
295
296
297
298 /* ************************************************************************ **
299 ** ********************** LZ4 File / Pipe compression ********************* **
300 ** ************************************************************************ */
301
LZ4IO_isSkippableMagicNumber(unsigned int magic)302 static int LZ4IO_isSkippableMagicNumber(unsigned int magic) {
303 return (magic & LZ4IO_SKIPPABLEMASK) == LZ4IO_SKIPPABLE0;
304 }
305
306
307 /** LZ4IO_openSrcFile() :
308 * condition : `srcFileName` must be non-NULL.
309 * @result : FILE* to `dstFileName`, or NULL if it fails */
LZ4IO_openSrcFile(const char * srcFileName)310 static FILE* LZ4IO_openSrcFile(const char* srcFileName)
311 {
312 FILE* f;
313
314 if (!strcmp (srcFileName, stdinmark)) {
315 DISPLAYLEVEL(4,"Using stdin for input\n");
316 f = stdin;
317 SET_BINARY_MODE(stdin);
318 } else {
319 f = fopen(srcFileName, "rb");
320 if ( f==NULL ) DISPLAYLEVEL(1, "%s: %s \n", srcFileName, strerror(errno));
321 }
322
323 return f;
324 }
325
326 /** FIO_openDstFile() :
327 * condition : `dstFileName` must be non-NULL.
328 * @result : FILE* to `dstFileName`, or NULL if it fails */
LZ4IO_openDstFile(LZ4IO_prefs_t * const prefs,const char * dstFileName)329 static FILE* LZ4IO_openDstFile(LZ4IO_prefs_t* const prefs, const char* dstFileName)
330 {
331 FILE* f;
332 assert(dstFileName != NULL);
333
334 if (!strcmp (dstFileName, stdoutmark)) {
335 DISPLAYLEVEL(4,"Using stdout for output\n");
336 f = stdout;
337 SET_BINARY_MODE(stdout);
338 if (prefs->sparseFileSupport==1) {
339 prefs->sparseFileSupport = 0;
340 DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
341 }
342 } else {
343 if (!prefs->overwrite && strcmp (dstFileName, nulmark)) { /* Check if destination file already exists */
344 f = fopen( dstFileName, "rb" );
345 if (f != NULL) { /* dest exists, prompt for overwrite authorization */
346 fclose(f);
347 if (g_displayLevel <= 1) { /* No interaction possible */
348 DISPLAY("%s already exists; not overwritten \n", dstFileName);
349 return NULL;
350 }
351 DISPLAY("%s already exists; do you wish to overwrite (y/N) ? ", dstFileName);
352 { int ch = getchar();
353 if ((ch!='Y') && (ch!='y')) {
354 DISPLAY(" not overwritten \n");
355 return NULL;
356 }
357 while ((ch!=EOF) && (ch!='\n')) ch = getchar(); /* flush rest of input line */
358 } } }
359 f = fopen( dstFileName, "wb" );
360 if (f==NULL) DISPLAYLEVEL(1, "%s: %s\n", dstFileName, strerror(errno));
361 }
362
363 /* sparse file */
364 if (f && prefs->sparseFileSupport) { SET_SPARSE_FILE_MODE(f); }
365
366 return f;
367 }
368
369
370
371 /***************************************
372 * Legacy Compression
373 ***************************************/
374
375 /* unoptimized version; solves endianess & alignment issues */
LZ4IO_writeLE32(void * p,unsigned value32)376 static void LZ4IO_writeLE32 (void* p, unsigned value32)
377 {
378 unsigned char* const dstPtr = (unsigned char*)p;
379 dstPtr[0] = (unsigned char)value32;
380 dstPtr[1] = (unsigned char)(value32 >> 8);
381 dstPtr[2] = (unsigned char)(value32 >> 16);
382 dstPtr[3] = (unsigned char)(value32 >> 24);
383 }
384
LZ4IO_LZ4_compress(const char * src,char * dst,int srcSize,int dstSize,int cLevel)385 static int LZ4IO_LZ4_compress(const char* src, char* dst, int srcSize, int dstSize, int cLevel)
386 {
387 (void)cLevel;
388 return LZ4_compress_fast(src, dst, srcSize, dstSize, 1);
389 }
390
391 /* LZ4IO_compressFilename_Legacy :
392 * This function is intentionally "hidden" (not published in .h)
393 * It generates compressed streams using the old 'legacy' format */
LZ4IO_compressFilename_Legacy(LZ4IO_prefs_t * const prefs,const char * input_filename,const char * output_filename,int compressionlevel)394 int LZ4IO_compressFilename_Legacy(LZ4IO_prefs_t* const prefs, const char* input_filename, const char* output_filename, int compressionlevel)
395 {
396 typedef int (*compress_f)(const char* src, char* dst, int srcSize, int dstSize, int cLevel);
397 compress_f const compressionFunction = (compressionlevel < 3) ? LZ4IO_LZ4_compress : LZ4_compress_HC;
398 unsigned long long filesize = 0;
399 unsigned long long compressedfilesize = MAGICNUMBER_SIZE;
400 char* in_buff;
401 char* out_buff;
402 const int outBuffSize = LZ4_compressBound(LEGACY_BLOCKSIZE);
403 FILE* const finput = LZ4IO_openSrcFile(input_filename);
404 FILE* foutput;
405 clock_t clockEnd;
406
407 /* Init */
408 clock_t const clockStart = clock();
409 if (finput == NULL)
410 EXM_THROW(20, "%s : open file error ", input_filename);
411
412 foutput = LZ4IO_openDstFile(prefs, output_filename);
413 if (foutput == NULL) {
414 fclose(finput);
415 EXM_THROW(20, "%s : open file error ", input_filename);
416 }
417
418 /* Allocate Memory */
419 in_buff = (char*)malloc(LEGACY_BLOCKSIZE);
420 out_buff = (char*)malloc((size_t)outBuffSize + 4);
421 if (!in_buff || !out_buff)
422 EXM_THROW(21, "Allocation error : not enough memory");
423
424 /* Write Archive Header */
425 LZ4IO_writeLE32(out_buff, LEGACY_MAGICNUMBER);
426 { size_t const writeSize = fwrite(out_buff, 1, MAGICNUMBER_SIZE, foutput);
427 if (writeSize != MAGICNUMBER_SIZE)
428 EXM_THROW(22, "Write error : cannot write header");
429 }
430
431 /* Main Loop */
432 while (1) {
433 int outSize;
434 /* Read Block */
435 size_t const inSize = fread(in_buff, (size_t)1, (size_t)LEGACY_BLOCKSIZE, finput);
436 assert(inSize <= LEGACY_BLOCKSIZE);
437 if (inSize == 0) break;
438 filesize += inSize;
439
440 /* Compress Block */
441 outSize = compressionFunction(in_buff, out_buff+4, (int)inSize, outBuffSize, compressionlevel);
442 compressedfilesize += outSize+4;
443 DISPLAYUPDATE(2, "\rRead : %i MB ==> %.2f%% ",
444 (int)(filesize>>20), (double)compressedfilesize/filesize*100);
445
446 /* Write Block */
447 assert(outSize > 0);
448 assert(outSize < outBuffSize);
449 LZ4IO_writeLE32(out_buff, (unsigned)outSize);
450 { size_t const writeSize = fwrite(out_buff, 1, outSize+4, foutput);
451 if (writeSize != (size_t)(outSize+4))
452 EXM_THROW(24, "Write error : cannot write compressed block");
453 } }
454 if (ferror(finput)) EXM_THROW(25, "Error while reading %s ", input_filename);
455
456 /* Status */
457 clockEnd = clock();
458 if (clockEnd==clockStart) clockEnd+=1; /* avoid division by zero (speed) */
459 filesize += !filesize; /* avoid division by zero (ratio) */
460 DISPLAYLEVEL(2, "\r%79s\r", ""); /* blank line */
461 DISPLAYLEVEL(2,"Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
462 filesize, compressedfilesize, (double)compressedfilesize / filesize * 100);
463 { double const seconds = (double)(clockEnd - clockStart) / CLOCKS_PER_SEC;
464 DISPLAYLEVEL(4,"Done in %.2f s ==> %.2f MB/s\n", seconds,
465 (double)filesize / seconds / 1024 / 1024);
466 }
467
468 /* Close & Free */
469 free(in_buff);
470 free(out_buff);
471 fclose(finput);
472 fclose(foutput);
473
474 return 0;
475 }
476
477
478 /*********************************************
479 * Compression using Frame format
480 *********************************************/
481
482 typedef struct {
483 void* srcBuffer;
484 size_t srcBufferSize;
485 void* dstBuffer;
486 size_t dstBufferSize;
487 LZ4F_compressionContext_t ctx;
488 LZ4F_CDict* cdict;
489 } cRess_t;
490
LZ4IO_createDict(LZ4IO_prefs_t * const prefs,size_t * dictSize)491 static void* LZ4IO_createDict(LZ4IO_prefs_t* const prefs, size_t *dictSize) {
492 size_t readSize;
493 size_t dictEnd = 0;
494 size_t dictLen = 0;
495 size_t dictStart;
496 size_t circularBufSize = LZ4_MAX_DICT_SIZE;
497 char* circularBuf;
498 char* dictBuf;
499 const char* dictFilename = prefs->dictionaryFilename;
500 FILE* dictFile;
501
502 if (!dictFilename) EXM_THROW(25, "Dictionary error : no filename provided");
503
504 circularBuf = (char *) malloc(circularBufSize);
505 if (!circularBuf) EXM_THROW(25, "Allocation error : not enough memory");
506
507 dictFile = LZ4IO_openSrcFile(dictFilename);
508 if (!dictFile) EXM_THROW(25, "Dictionary error : could not open dictionary file");
509
510 /* opportunistically seek to the part of the file we care about. If this */
511 /* fails it's not a problem since we'll just read everything anyways. */
512 if (strcmp(dictFilename, stdinmark)) {
513 (void)UTIL_fseek(dictFile, -LZ4_MAX_DICT_SIZE, SEEK_END);
514 }
515
516 do {
517 readSize = fread(circularBuf + dictEnd, 1, circularBufSize - dictEnd, dictFile);
518 dictEnd = (dictEnd + readSize) % circularBufSize;
519 dictLen += readSize;
520 } while (readSize>0);
521
522 if (dictLen > LZ4_MAX_DICT_SIZE) {
523 dictLen = LZ4_MAX_DICT_SIZE;
524 }
525
526 *dictSize = dictLen;
527
528 dictStart = (circularBufSize + dictEnd - dictLen) % circularBufSize;
529
530 if (dictStart == 0) {
531 /* We're in the simple case where the dict starts at the beginning of our circular buffer. */
532 dictBuf = circularBuf;
533 circularBuf = NULL;
534 } else {
535 /* Otherwise, we will alloc a new buffer and copy our dict into that. */
536 dictBuf = (char *) malloc(dictLen ? dictLen : 1);
537 if (!dictBuf) EXM_THROW(25, "Allocation error : not enough memory");
538
539 memcpy(dictBuf, circularBuf + dictStart, circularBufSize - dictStart);
540 memcpy(dictBuf + circularBufSize - dictStart, circularBuf, dictLen - (circularBufSize - dictStart));
541 }
542
543 fclose(dictFile);
544 free(circularBuf);
545
546 return dictBuf;
547 }
548
LZ4IO_createCDict(LZ4IO_prefs_t * const prefs)549 static LZ4F_CDict* LZ4IO_createCDict(LZ4IO_prefs_t* const prefs) {
550 size_t dictionarySize;
551 void* dictionaryBuffer;
552 LZ4F_CDict* cdict;
553 if (!prefs->useDictionary) {
554 return NULL;
555 }
556 dictionaryBuffer = LZ4IO_createDict(prefs, &dictionarySize);
557 if (!dictionaryBuffer) EXM_THROW(25, "Dictionary error : could not create dictionary");
558 cdict = LZ4F_createCDict(dictionaryBuffer, dictionarySize);
559 free(dictionaryBuffer);
560 return cdict;
561 }
562
LZ4IO_createCResources(LZ4IO_prefs_t * const prefs)563 static cRess_t LZ4IO_createCResources(LZ4IO_prefs_t* const prefs)
564 {
565 const size_t blockSize = prefs->blockSize;
566 cRess_t ress;
567
568 LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&(ress.ctx), LZ4F_VERSION);
569 if (LZ4F_isError(errorCode)) EXM_THROW(30, "Allocation error : can't create LZ4F context : %s", LZ4F_getErrorName(errorCode));
570
571 /* Allocate Memory */
572 ress.srcBuffer = malloc(blockSize);
573 ress.srcBufferSize = blockSize;
574 ress.dstBufferSize = LZ4F_compressFrameBound(blockSize, NULL); /* cover worst case */
575 ress.dstBuffer = malloc(ress.dstBufferSize);
576 if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(31, "Allocation error : not enough memory");
577
578 ress.cdict = LZ4IO_createCDict(prefs);
579
580 return ress;
581 }
582
LZ4IO_freeCResources(cRess_t ress)583 static void LZ4IO_freeCResources(cRess_t ress)
584 {
585 free(ress.srcBuffer);
586 free(ress.dstBuffer);
587
588 LZ4F_freeCDict(ress.cdict);
589 ress.cdict = NULL;
590
591 { LZ4F_errorCode_t const errorCode = LZ4F_freeCompressionContext(ress.ctx);
592 if (LZ4F_isError(errorCode)) EXM_THROW(38, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode)); }
593 }
594
595 /*
596 * LZ4IO_compressFilename_extRess()
597 * result : 0 : compression completed correctly
598 * 1 : missing or pb opening srcFileName
599 */
600 static int
LZ4IO_compressFilename_extRess(LZ4IO_prefs_t * const io_prefs,cRess_t ress,const char * srcFileName,const char * dstFileName,int compressionLevel)601 LZ4IO_compressFilename_extRess(LZ4IO_prefs_t* const io_prefs, cRess_t ress,
602 const char* srcFileName, const char* dstFileName,
603 int compressionLevel)
604 {
605 unsigned long long filesize = 0;
606 unsigned long long compressedfilesize = 0;
607 FILE* srcFile;
608 FILE* dstFile;
609 void* const srcBuffer = ress.srcBuffer;
610 void* const dstBuffer = ress.dstBuffer;
611 const size_t dstBufferSize = ress.dstBufferSize;
612 const size_t blockSize = io_prefs->blockSize;
613 size_t readSize;
614 LZ4F_compressionContext_t ctx = ress.ctx; /* just a pointer */
615 LZ4F_preferences_t prefs;
616
617 /* Init */
618 srcFile = LZ4IO_openSrcFile(srcFileName);
619 if (srcFile == NULL) return 1;
620 dstFile = LZ4IO_openDstFile(io_prefs, dstFileName);
621 if (dstFile == NULL) { fclose(srcFile); return 1; }
622 memset(&prefs, 0, sizeof(prefs));
623
624
625 /* Set compression parameters */
626 prefs.autoFlush = 1;
627 prefs.compressionLevel = compressionLevel;
628 prefs.frameInfo.blockMode = (LZ4F_blockMode_t)io_prefs->blockIndependence;
629 prefs.frameInfo.blockSizeID = (LZ4F_blockSizeID_t)io_prefs->blockSizeId;
630 prefs.frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)io_prefs->blockChecksum;
631 prefs.frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)io_prefs->streamChecksum;
632 prefs.favorDecSpeed = io_prefs->favorDecSpeed;
633 if (io_prefs->contentSizeFlag) {
634 U64 const fileSize = UTIL_getFileSize(srcFileName);
635 prefs.frameInfo.contentSize = fileSize; /* == 0 if input == stdin */
636 if (fileSize==0)
637 DISPLAYLEVEL(3, "Warning : cannot determine input content size \n");
638 }
639
640 /* read first block */
641 readSize = fread(srcBuffer, (size_t)1, blockSize, srcFile);
642 if (ferror(srcFile)) EXM_THROW(30, "Error reading %s ", srcFileName);
643 filesize += readSize;
644
645 /* single-block file */
646 if (readSize < blockSize) {
647 /* Compress in single pass */
648 size_t cSize = LZ4F_compressFrame_usingCDict(ctx, dstBuffer, dstBufferSize, srcBuffer, readSize, ress.cdict, &prefs);
649 if (LZ4F_isError(cSize)) EXM_THROW(31, "Compression failed : %s", LZ4F_getErrorName(cSize));
650 compressedfilesize = cSize;
651 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ",
652 (unsigned)(filesize>>20), (double)compressedfilesize/(filesize+!filesize)*100); /* avoid division by zero */
653
654 /* Write Block */
655 { size_t const sizeCheck = fwrite(dstBuffer, 1, cSize, dstFile);
656 if (sizeCheck!=cSize) EXM_THROW(32, "Write error : cannot write compressed block");
657 } }
658
659 else
660
661 /* multiple-blocks file */
662 {
663 /* Write Archive Header */
664 size_t headerSize = LZ4F_compressBegin_usingCDict(ctx, dstBuffer, dstBufferSize, ress.cdict, &prefs);
665 if (LZ4F_isError(headerSize)) EXM_THROW(33, "File header generation failed : %s", LZ4F_getErrorName(headerSize));
666 { size_t const sizeCheck = fwrite(dstBuffer, 1, headerSize, dstFile);
667 if (sizeCheck!=headerSize) EXM_THROW(34, "Write error : cannot write header"); }
668 compressedfilesize += headerSize;
669
670 /* Main Loop */
671 while (readSize>0) {
672 size_t outSize;
673
674 /* Compress Block */
675 outSize = LZ4F_compressUpdate(ctx, dstBuffer, dstBufferSize, srcBuffer, readSize, NULL);
676 if (LZ4F_isError(outSize)) EXM_THROW(35, "Compression failed : %s", LZ4F_getErrorName(outSize));
677 compressedfilesize += outSize;
678 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ", (unsigned)(filesize>>20), (double)compressedfilesize/filesize*100);
679
680 /* Write Block */
681 { size_t const sizeCheck = fwrite(dstBuffer, 1, outSize, dstFile);
682 if (sizeCheck!=outSize) EXM_THROW(36, "Write error : cannot write compressed block"); }
683
684 /* Read next block */
685 readSize = fread(srcBuffer, (size_t)1, (size_t)blockSize, srcFile);
686 filesize += readSize;
687 }
688 if (ferror(srcFile)) EXM_THROW(37, "Error reading %s ", srcFileName);
689
690 /* End of Stream mark */
691 headerSize = LZ4F_compressEnd(ctx, dstBuffer, dstBufferSize, NULL);
692 if (LZ4F_isError(headerSize)) EXM_THROW(38, "End of file generation failed : %s", LZ4F_getErrorName(headerSize));
693
694 { size_t const sizeCheck = fwrite(dstBuffer, 1, headerSize, dstFile);
695 if (sizeCheck!=headerSize) EXM_THROW(39, "Write error : cannot write end of stream"); }
696 compressedfilesize += headerSize;
697 }
698
699 /* Release file handlers */
700 fclose (srcFile);
701 if (strcmp(dstFileName,stdoutmark)) fclose (dstFile); /* do not close stdout */
702
703 /* Copy owner, file permissions and modification time */
704 { stat_t statbuf;
705 if (strcmp (srcFileName, stdinmark)
706 && strcmp (dstFileName, stdoutmark)
707 && strcmp (dstFileName, nulmark)
708 && UTIL_getFileStat(srcFileName, &statbuf)) {
709 UTIL_setFileStat(dstFileName, &statbuf);
710 } }
711
712 if (io_prefs->removeSrcFile) { /* remove source file : --rm */
713 if (remove(srcFileName))
714 EXM_THROW(40, "Remove error : %s: %s", srcFileName, strerror(errno));
715 }
716
717 /* Final Status */
718 DISPLAYLEVEL(2, "\r%79s\r", "");
719 DISPLAYLEVEL(2, "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
720 filesize, compressedfilesize,
721 (double)compressedfilesize / (filesize + !filesize /* avoid division by zero */ ) * 100);
722
723 return 0;
724 }
725
726
LZ4IO_compressFilename(LZ4IO_prefs_t * const prefs,const char * srcFileName,const char * dstFileName,int compressionLevel)727 int LZ4IO_compressFilename(LZ4IO_prefs_t* const prefs, const char* srcFileName, const char* dstFileName, int compressionLevel)
728 {
729 UTIL_time_t const timeStart = UTIL_getTime();
730 clock_t const cpuStart = clock();
731 cRess_t const ress = LZ4IO_createCResources(prefs);
732
733 int const result = LZ4IO_compressFilename_extRess(prefs, ress, srcFileName, dstFileName, compressionLevel);
734
735 /* Free resources */
736 LZ4IO_freeCResources(ress);
737
738 /* Final Status */
739 { clock_t const cpuEnd = clock();
740 double const cpuLoad_s = (double)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
741 U64 const timeLength_ns = UTIL_clockSpanNano(timeStart);
742 double const timeLength_s = (double)timeLength_ns / 1000000000;
743 DISPLAYLEVEL(4, "Completed in %.2f sec (cpu load : %.0f%%)\n",
744 timeLength_s, (cpuLoad_s / timeLength_s) * 100);
745 }
746
747 return result;
748 }
749
750
751 #define FNSPACE 30
LZ4IO_compressMultipleFilenames(LZ4IO_prefs_t * const prefs,const char ** inFileNamesTable,int ifntSize,const char * suffix,int compressionLevel)752 int LZ4IO_compressMultipleFilenames(LZ4IO_prefs_t* const prefs,
753 const char** inFileNamesTable, int ifntSize,
754 const char* suffix,
755 int compressionLevel)
756 {
757 int i;
758 int missed_files = 0;
759 char* dstFileName = (char*)malloc(FNSPACE);
760 size_t ofnSize = FNSPACE;
761 const size_t suffixSize = strlen(suffix);
762 cRess_t ress;
763
764 if (dstFileName == NULL) return ifntSize; /* not enough memory */
765 ress = LZ4IO_createCResources(prefs);
766
767 /* loop on each file */
768 for (i=0; i<ifntSize; i++) {
769 size_t const ifnSize = strlen(inFileNamesTable[i]);
770 if (!strcmp(suffix, stdoutmark)) {
771 missed_files += LZ4IO_compressFilename_extRess(prefs, ress,
772 inFileNamesTable[i], stdoutmark,
773 compressionLevel);
774 continue;
775 }
776 if (ofnSize <= ifnSize+suffixSize+1) {
777 free(dstFileName);
778 ofnSize = ifnSize + 20;
779 dstFileName = (char*)malloc(ofnSize);
780 if (dstFileName==NULL) {
781 LZ4IO_freeCResources(ress);
782 return ifntSize;
783 } }
784 strcpy(dstFileName, inFileNamesTable[i]);
785 strcat(dstFileName, suffix);
786
787 missed_files += LZ4IO_compressFilename_extRess(prefs, ress,
788 inFileNamesTable[i], dstFileName,
789 compressionLevel);
790 }
791
792 /* Close & Free */
793 LZ4IO_freeCResources(ress);
794 free(dstFileName);
795
796 return missed_files;
797 }
798
799
800 /* ********************************************************************* */
801 /* ********************** LZ4 file-stream Decompression **************** */
802 /* ********************************************************************* */
803
804 /* It's presumed that s points to a memory space of size >= 4 */
LZ4IO_readLE32(const void * s)805 static unsigned LZ4IO_readLE32 (const void* s)
806 {
807 const unsigned char* const srcPtr = (const unsigned char*)s;
808 unsigned value32 = srcPtr[0];
809 value32 += (unsigned)srcPtr[1] << 8;
810 value32 += (unsigned)srcPtr[2] << 16;
811 value32 += (unsigned)srcPtr[3] << 24;
812 return value32;
813 }
814
815
LZ4IO_fwriteSparse(LZ4IO_prefs_t * const prefs,FILE * file,const void * buffer,size_t bufferSize,unsigned storedSkips)816 static unsigned LZ4IO_fwriteSparse(LZ4IO_prefs_t* const prefs, FILE* file, const void* buffer, size_t bufferSize, unsigned storedSkips)
817 {
818 const size_t sizeT = sizeof(size_t);
819 const size_t maskT = sizeT -1 ;
820 const size_t* const bufferT = (const size_t*)buffer; /* Buffer is supposed malloc'ed, hence aligned on size_t */
821 const size_t* ptrT = bufferT;
822 size_t bufferSizeT = bufferSize / sizeT;
823 const size_t* const bufferTEnd = bufferT + bufferSizeT;
824 const size_t segmentSizeT = (32 KB) / sizeT;
825
826 if (!prefs->sparseFileSupport) { /* normal write */
827 size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
828 if (sizeCheck != bufferSize) EXM_THROW(70, "Write error : cannot write decoded block");
829 return 0;
830 }
831
832 /* avoid int overflow */
833 if (storedSkips > 1 GB) {
834 int const seekResult = UTIL_fseek(file, 1 GB, SEEK_CUR);
835 if (seekResult != 0) EXM_THROW(71, "1 GB skip error (sparse file support)");
836 storedSkips -= 1 GB;
837 }
838
839 while (ptrT < bufferTEnd) {
840 size_t seg0SizeT = segmentSizeT;
841 size_t nb0T;
842
843 /* count leading zeros */
844 if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
845 bufferSizeT -= seg0SizeT;
846 for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
847 storedSkips += (unsigned)(nb0T * sizeT);
848
849 if (nb0T != seg0SizeT) { /* not all 0s */
850 errno = 0;
851 { int const seekResult = UTIL_fseek(file, storedSkips, SEEK_CUR);
852 if (seekResult) EXM_THROW(72, "Sparse skip error(%d): %s ; try --no-sparse", (int)errno, strerror(errno));
853 }
854 storedSkips = 0;
855 seg0SizeT -= nb0T;
856 ptrT += nb0T;
857 { size_t const sizeCheck = fwrite(ptrT, sizeT, seg0SizeT, file);
858 if (sizeCheck != seg0SizeT) EXM_THROW(73, "Write error : cannot write decoded block");
859 } }
860 ptrT += seg0SizeT;
861 }
862
863 if (bufferSize & maskT) { /* size not multiple of sizeT : implies end of block */
864 const char* const restStart = (const char*)bufferTEnd;
865 const char* restPtr = restStart;
866 size_t const restSize = bufferSize & maskT;
867 const char* const restEnd = restStart + restSize;
868 for (; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
869 storedSkips += (unsigned) (restPtr - restStart);
870 if (restPtr != restEnd) {
871 int const seekResult = UTIL_fseek(file, storedSkips, SEEK_CUR);
872 if (seekResult) EXM_THROW(74, "Sparse skip error ; try --no-sparse");
873 storedSkips = 0;
874 { size_t const sizeCheck = fwrite(restPtr, 1, restEnd - restPtr, file);
875 if (sizeCheck != (size_t)(restEnd - restPtr)) EXM_THROW(75, "Write error : cannot write decoded end of block");
876 } }
877 }
878
879 return storedSkips;
880 }
881
LZ4IO_fwriteSparseEnd(FILE * file,unsigned storedSkips)882 static void LZ4IO_fwriteSparseEnd(FILE* file, unsigned storedSkips)
883 {
884 if (storedSkips>0) { /* implies g_sparseFileSupport>0 */
885 int const seekResult = UTIL_fseek(file, storedSkips-1, SEEK_CUR);
886 if (seekResult != 0) EXM_THROW(69, "Final skip error (sparse file)\n");
887 { const char lastZeroByte[1] = { 0 };
888 size_t const sizeCheck = fwrite(lastZeroByte, 1, 1, file);
889 if (sizeCheck != 1) EXM_THROW(69, "Write error : cannot write last zero\n");
890 } }
891 }
892
893
894 static unsigned g_magicRead = 0; /* out-parameter of LZ4IO_decodeLegacyStream() */
LZ4IO_decodeLegacyStream(LZ4IO_prefs_t * const prefs,FILE * finput,FILE * foutput)895 static unsigned long long LZ4IO_decodeLegacyStream(LZ4IO_prefs_t* const prefs, FILE* finput, FILE* foutput)
896 {
897 unsigned long long streamSize = 0;
898 unsigned storedSkips = 0;
899
900 /* Allocate Memory */
901 char* const in_buff = (char*)malloc((size_t)LZ4_compressBound(LEGACY_BLOCKSIZE));
902 char* const out_buff = (char*)malloc(LEGACY_BLOCKSIZE);
903 if (!in_buff || !out_buff) EXM_THROW(51, "Allocation error : not enough memory");
904
905 /* Main Loop */
906 while (1) {
907 unsigned int blockSize;
908
909 /* Block Size */
910 { size_t const sizeCheck = fread(in_buff, 1, 4, finput);
911 if (sizeCheck == 0) break; /* Nothing to read : file read is completed */
912 if (sizeCheck != 4) EXM_THROW(52, "Read error : cannot access block size "); }
913 blockSize = LZ4IO_readLE32(in_buff); /* Convert to Little Endian */
914 if (blockSize > LZ4_COMPRESSBOUND(LEGACY_BLOCKSIZE)) {
915 /* Cannot read next block : maybe new stream ? */
916 g_magicRead = blockSize;
917 break;
918 }
919
920 /* Read Block */
921 { size_t const sizeCheck = fread(in_buff, 1, blockSize, finput);
922 if (sizeCheck!=blockSize) EXM_THROW(52, "Read error : cannot access compressed block !"); }
923
924 /* Decode Block */
925 { int const decodeSize = LZ4_decompress_safe(in_buff, out_buff, (int)blockSize, LEGACY_BLOCKSIZE);
926 if (decodeSize < 0) EXM_THROW(53, "Decoding Failed ! Corrupted input detected !");
927 streamSize += (unsigned long long)decodeSize;
928 /* Write Block */
929 storedSkips = LZ4IO_fwriteSparse(prefs, foutput, out_buff, (size_t)decodeSize, storedSkips); /* success or die */
930 } }
931 if (ferror(finput)) EXM_THROW(54, "Read error : ferror");
932
933 LZ4IO_fwriteSparseEnd(foutput, storedSkips);
934
935 /* Free */
936 free(in_buff);
937 free(out_buff);
938
939 return streamSize;
940 }
941
942
943
944 typedef struct {
945 void* srcBuffer;
946 size_t srcBufferSize;
947 void* dstBuffer;
948 size_t dstBufferSize;
949 FILE* dstFile;
950 LZ4F_decompressionContext_t dCtx;
951 void* dictBuffer;
952 size_t dictBufferSize;
953 } dRess_t;
954
LZ4IO_loadDDict(LZ4IO_prefs_t * const prefs,dRess_t * ress)955 static void LZ4IO_loadDDict(LZ4IO_prefs_t* const prefs, dRess_t* ress) {
956 if (!prefs->useDictionary) {
957 ress->dictBuffer = NULL;
958 ress->dictBufferSize = 0;
959 return;
960 }
961
962 ress->dictBuffer = LZ4IO_createDict(prefs, &ress->dictBufferSize);
963 if (!ress->dictBuffer) EXM_THROW(25, "Dictionary error : could not create dictionary");
964 }
965
966 static const size_t LZ4IO_dBufferSize = 64 KB;
LZ4IO_createDResources(LZ4IO_prefs_t * const prefs)967 static dRess_t LZ4IO_createDResources(LZ4IO_prefs_t* const prefs)
968 {
969 dRess_t ress;
970
971 /* init */
972 LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&ress.dCtx, LZ4F_VERSION);
973 if (LZ4F_isError(errorCode)) EXM_THROW(60, "Can't create LZ4F context : %s", LZ4F_getErrorName(errorCode));
974
975 /* Allocate Memory */
976 ress.srcBufferSize = LZ4IO_dBufferSize;
977 ress.srcBuffer = malloc(ress.srcBufferSize);
978 ress.dstBufferSize = LZ4IO_dBufferSize;
979 ress.dstBuffer = malloc(ress.dstBufferSize);
980 if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(61, "Allocation error : not enough memory");
981
982 LZ4IO_loadDDict(prefs, &ress);
983
984 ress.dstFile = NULL;
985 return ress;
986 }
987
LZ4IO_freeDResources(dRess_t ress)988 static void LZ4IO_freeDResources(dRess_t ress)
989 {
990 LZ4F_errorCode_t errorCode = LZ4F_freeDecompressionContext(ress.dCtx);
991 if (LZ4F_isError(errorCode)) EXM_THROW(69, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode));
992 free(ress.srcBuffer);
993 free(ress.dstBuffer);
994 free(ress.dictBuffer);
995 }
996
997
LZ4IO_decompressLZ4F(LZ4IO_prefs_t * const prefs,dRess_t ress,FILE * srcFile,FILE * dstFile)998 static unsigned long long LZ4IO_decompressLZ4F(LZ4IO_prefs_t* const prefs, dRess_t ress, FILE* srcFile, FILE* dstFile)
999 {
1000 unsigned long long filesize = 0;
1001 LZ4F_errorCode_t nextToLoad;
1002 unsigned storedSkips = 0;
1003
1004 /* Init feed with magic number (already consumed from FILE* sFile) */
1005 { size_t inSize = MAGICNUMBER_SIZE;
1006 size_t outSize= 0;
1007 LZ4IO_writeLE32(ress.srcBuffer, LZ4IO_MAGICNUMBER);
1008 nextToLoad = LZ4F_decompress_usingDict(ress.dCtx, ress.dstBuffer, &outSize, ress.srcBuffer, &inSize, ress.dictBuffer, ress.dictBufferSize, NULL);
1009 if (LZ4F_isError(nextToLoad)) EXM_THROW(62, "Header error : %s", LZ4F_getErrorName(nextToLoad));
1010 }
1011
1012 /* Main Loop */
1013 for (;nextToLoad;) {
1014 size_t readSize;
1015 size_t pos = 0;
1016 size_t decodedBytes = ress.dstBufferSize;
1017
1018 /* Read input */
1019 if (nextToLoad > ress.srcBufferSize) nextToLoad = ress.srcBufferSize;
1020 readSize = fread(ress.srcBuffer, 1, nextToLoad, srcFile);
1021 if (!readSize) break; /* reached end of file or stream */
1022
1023 while ((pos < readSize) || (decodedBytes == ress.dstBufferSize)) { /* still to read, or still to flush */
1024 /* Decode Input (at least partially) */
1025 size_t remaining = readSize - pos;
1026 decodedBytes = ress.dstBufferSize;
1027 nextToLoad = LZ4F_decompress_usingDict(ress.dCtx, ress.dstBuffer, &decodedBytes, (char*)(ress.srcBuffer)+pos, &remaining, ress.dictBuffer, ress.dictBufferSize, NULL);
1028 if (LZ4F_isError(nextToLoad)) EXM_THROW(66, "Decompression error : %s", LZ4F_getErrorName(nextToLoad));
1029 pos += remaining;
1030
1031 /* Write Block */
1032 if (decodedBytes) {
1033 if (!prefs->testMode)
1034 storedSkips = LZ4IO_fwriteSparse(prefs, dstFile, ress.dstBuffer, decodedBytes, storedSkips);
1035 filesize += decodedBytes;
1036 DISPLAYUPDATE(2, "\rDecompressed : %u MB ", (unsigned)(filesize>>20));
1037 }
1038
1039 if (!nextToLoad) break;
1040 }
1041 }
1042 /* can be out because readSize == 0, which could be an fread() error */
1043 if (ferror(srcFile)) EXM_THROW(67, "Read error");
1044
1045 if (!prefs->testMode) LZ4IO_fwriteSparseEnd(dstFile, storedSkips);
1046 if (nextToLoad!=0) EXM_THROW(68, "Unfinished stream");
1047
1048 return filesize;
1049 }
1050
1051
1052 #define PTSIZE (64 KB)
1053 #define PTSIZET (PTSIZE / sizeof(size_t))
LZ4IO_passThrough(LZ4IO_prefs_t * const prefs,FILE * finput,FILE * foutput,unsigned char MNstore[MAGICNUMBER_SIZE])1054 static unsigned long long LZ4IO_passThrough(LZ4IO_prefs_t* const prefs, FILE* finput, FILE* foutput, unsigned char MNstore[MAGICNUMBER_SIZE])
1055 {
1056 size_t buffer[PTSIZET];
1057 size_t readBytes = 1;
1058 unsigned long long total = MAGICNUMBER_SIZE;
1059 unsigned storedSkips = 0;
1060
1061 size_t const sizeCheck = fwrite(MNstore, 1, MAGICNUMBER_SIZE, foutput);
1062 if (sizeCheck != MAGICNUMBER_SIZE) EXM_THROW(50, "Pass-through write error");
1063
1064 while (readBytes) {
1065 readBytes = fread(buffer, 1, PTSIZE, finput);
1066 total += readBytes;
1067 storedSkips = LZ4IO_fwriteSparse(prefs, foutput, buffer, readBytes, storedSkips);
1068 }
1069 if (ferror(finput)) EXM_THROW(51, "Read Error");
1070
1071 LZ4IO_fwriteSparseEnd(foutput, storedSkips);
1072 return total;
1073 }
1074
1075
1076 /** Safely handle cases when (unsigned)offset > LONG_MAX */
fseek_u32(FILE * fp,unsigned offset,int where)1077 static int fseek_u32(FILE *fp, unsigned offset, int where)
1078 {
1079 const unsigned stepMax = 1U << 30;
1080 int errorNb = 0;
1081
1082 if (where != SEEK_CUR) return -1; /* Only allows SEEK_CUR */
1083 while (offset > 0) {
1084 unsigned s = offset;
1085 if (s > stepMax) s = stepMax;
1086 errorNb = UTIL_fseek(fp, (long) s, SEEK_CUR);
1087 if (errorNb != 0) break;
1088 offset -= s;
1089 }
1090 return errorNb;
1091 }
1092
1093 #define ENDOFSTREAM ((unsigned long long)-1)
selectDecoder(LZ4IO_prefs_t * const prefs,dRess_t ress,FILE * finput,FILE * foutput)1094 static unsigned long long selectDecoder(LZ4IO_prefs_t* const prefs, dRess_t ress, FILE* finput, FILE* foutput)
1095 {
1096 unsigned char MNstore[MAGICNUMBER_SIZE];
1097 unsigned magicNumber;
1098 static unsigned nbFrames = 0;
1099
1100 /* init */
1101 nbFrames++;
1102
1103 /* Check Archive Header */
1104 if (g_magicRead) { /* magic number already read from finput (see legacy frame)*/
1105 magicNumber = g_magicRead;
1106 g_magicRead = 0;
1107 } else {
1108 size_t const nbReadBytes = fread(MNstore, 1, MAGICNUMBER_SIZE, finput);
1109 if (nbReadBytes==0) { nbFrames = 0; return ENDOFSTREAM; } /* EOF */
1110 if (nbReadBytes != MAGICNUMBER_SIZE)
1111 EXM_THROW(40, "Unrecognized header : Magic Number unreadable");
1112 magicNumber = LZ4IO_readLE32(MNstore); /* Little Endian format */
1113 }
1114 if (LZ4IO_isSkippableMagicNumber(magicNumber))
1115 magicNumber = LZ4IO_SKIPPABLE0; /* fold skippable magic numbers */
1116
1117 switch(magicNumber)
1118 {
1119 case LZ4IO_MAGICNUMBER:
1120 return LZ4IO_decompressLZ4F(prefs, ress, finput, foutput);
1121 case LEGACY_MAGICNUMBER:
1122 DISPLAYLEVEL(4, "Detected : Legacy format \n");
1123 return LZ4IO_decodeLegacyStream(prefs, finput, foutput);
1124 case LZ4IO_SKIPPABLE0:
1125 DISPLAYLEVEL(4, "Skipping detected skippable area \n");
1126 { size_t const nbReadBytes = fread(MNstore, 1, 4, finput);
1127 if (nbReadBytes != 4)
1128 EXM_THROW(42, "Stream error : skippable size unreadable");
1129 }
1130 { unsigned const size = LZ4IO_readLE32(MNstore);
1131 int const errorNb = fseek_u32(finput, size, SEEK_CUR);
1132 if (errorNb != 0)
1133 EXM_THROW(43, "Stream error : cannot skip skippable area");
1134 }
1135 return 0;
1136 EXTENDED_FORMAT; /* macro extension for custom formats */
1137 default:
1138 if (nbFrames == 1) { /* just started */
1139 /* Wrong magic number at the beginning of 1st stream */
1140 if (!prefs->testMode && prefs->overwrite && prefs->passThrough) {
1141 nbFrames = 0;
1142 return LZ4IO_passThrough(prefs, finput, foutput, MNstore);
1143 }
1144 EXM_THROW(44,"Unrecognized header : file cannot be decoded");
1145 }
1146 { long int const position = ftell(finput); /* only works for files < 2 GB */
1147 DISPLAYLEVEL(2, "Stream followed by undecodable data ");
1148 if (position != -1L)
1149 DISPLAYLEVEL(2, "at position %i ", (int)position);
1150 DISPLAYLEVEL(2, "\n");
1151 }
1152 return ENDOFSTREAM;
1153 }
1154 }
1155
1156
LZ4IO_decompressSrcFile(LZ4IO_prefs_t * const prefs,dRess_t ress,const char * input_filename,const char * output_filename)1157 static int LZ4IO_decompressSrcFile(LZ4IO_prefs_t* const prefs, dRess_t ress, const char* input_filename, const char* output_filename)
1158 {
1159 FILE* const foutput = ress.dstFile;
1160 unsigned long long filesize = 0;
1161
1162 /* Init */
1163 FILE* const finput = LZ4IO_openSrcFile(input_filename);
1164 if (finput==NULL) return 1;
1165
1166 /* Loop over multiple streams */
1167 for ( ; ; ) { /* endless loop, see break condition */
1168 unsigned long long const decodedSize =
1169 selectDecoder(prefs, ress, finput, foutput);
1170 if (decodedSize == ENDOFSTREAM) break;
1171 filesize += decodedSize;
1172 }
1173
1174 /* Close input */
1175 fclose(finput);
1176 if (prefs->removeSrcFile) { /* --rm */
1177 if (remove(input_filename))
1178 EXM_THROW(45, "Remove error : %s: %s", input_filename, strerror(errno));
1179 }
1180
1181 /* Final Status */
1182 DISPLAYLEVEL(2, "\r%79s\r", "");
1183 DISPLAYLEVEL(2, "%-20.20s : decoded %llu bytes \n", input_filename, filesize);
1184 (void)output_filename;
1185
1186 return 0;
1187 }
1188
1189
LZ4IO_decompressDstFile(LZ4IO_prefs_t * const prefs,dRess_t ress,const char * input_filename,const char * output_filename)1190 static int LZ4IO_decompressDstFile(LZ4IO_prefs_t* const prefs, dRess_t ress, const char* input_filename, const char* output_filename)
1191 {
1192 stat_t statbuf;
1193 int stat_result = 0;
1194 FILE* const foutput = LZ4IO_openDstFile(prefs, output_filename);
1195 if (foutput==NULL) return 1; /* failure */
1196
1197 if ( strcmp(input_filename, stdinmark)
1198 && UTIL_getFileStat(input_filename, &statbuf))
1199 stat_result = 1;
1200
1201 ress.dstFile = foutput;
1202 LZ4IO_decompressSrcFile(prefs, ress, input_filename, output_filename);
1203
1204 fclose(foutput);
1205
1206 /* Copy owner, file permissions and modification time */
1207 if ( stat_result != 0
1208 && strcmp (output_filename, stdoutmark)
1209 && strcmp (output_filename, nulmark)) {
1210 UTIL_setFileStat(output_filename, &statbuf);
1211 /* should return value be read ? or is silent fail good enough ? */
1212 }
1213
1214 return 0;
1215 }
1216
1217
LZ4IO_decompressFilename(LZ4IO_prefs_t * const prefs,const char * input_filename,const char * output_filename)1218 int LZ4IO_decompressFilename(LZ4IO_prefs_t* const prefs, const char* input_filename, const char* output_filename)
1219 {
1220 dRess_t const ress = LZ4IO_createDResources(prefs);
1221 clock_t const start = clock();
1222
1223 int const missingFiles = LZ4IO_decompressDstFile(prefs, ress, input_filename, output_filename);
1224
1225 clock_t const end = clock();
1226 double const seconds = (double)(end - start) / CLOCKS_PER_SEC;
1227 DISPLAYLEVEL(4, "Done in %.2f sec \n", seconds);
1228
1229 LZ4IO_freeDResources(ress);
1230 return missingFiles;
1231 }
1232
1233
LZ4IO_decompressMultipleFilenames(LZ4IO_prefs_t * const prefs,const char ** inFileNamesTable,int ifntSize,const char * suffix)1234 int LZ4IO_decompressMultipleFilenames(LZ4IO_prefs_t* const prefs,
1235 const char** inFileNamesTable, int ifntSize,
1236 const char* suffix)
1237 {
1238 int i;
1239 int skippedFiles = 0;
1240 int missingFiles = 0;
1241 char* outFileName = (char*)malloc(FNSPACE);
1242 size_t ofnSize = FNSPACE;
1243 size_t const suffixSize = strlen(suffix);
1244 dRess_t ress = LZ4IO_createDResources(prefs);
1245
1246 if (outFileName==NULL) return ifntSize; /* not enough memory */
1247 ress.dstFile = LZ4IO_openDstFile(prefs, stdoutmark);
1248
1249 for (i=0; i<ifntSize; i++) {
1250 size_t const ifnSize = strlen(inFileNamesTable[i]);
1251 const char* const suffixPtr = inFileNamesTable[i] + ifnSize - suffixSize;
1252 if (!strcmp(suffix, stdoutmark)) {
1253 missingFiles += LZ4IO_decompressSrcFile(prefs, ress, inFileNamesTable[i], stdoutmark);
1254 continue;
1255 }
1256 if (ofnSize <= ifnSize-suffixSize+1) {
1257 free(outFileName);
1258 ofnSize = ifnSize + 20;
1259 outFileName = (char*)malloc(ofnSize);
1260 if (outFileName==NULL) return ifntSize;
1261 }
1262 if (ifnSize <= suffixSize || strcmp(suffixPtr, suffix) != 0) {
1263 DISPLAYLEVEL(1, "File extension doesn't match expected LZ4_EXTENSION (%4s); will not process file: %s\n", suffix, inFileNamesTable[i]);
1264 skippedFiles++;
1265 continue;
1266 }
1267 memcpy(outFileName, inFileNamesTable[i], ifnSize - suffixSize);
1268 outFileName[ifnSize-suffixSize] = '\0';
1269 missingFiles += LZ4IO_decompressDstFile(prefs, ress, inFileNamesTable[i], outFileName);
1270 }
1271
1272 LZ4IO_freeDResources(ress);
1273 free(outFileName);
1274 return missingFiles + skippedFiles;
1275 }
1276
1277
1278 /* ********************************************************************* */
1279 /* ********************** LZ4 --list command *********************** */
1280 /* ********************************************************************* */
1281
1282 typedef enum
1283 {
1284 lz4Frame = 0,
1285 legacyFrame,
1286 skippableFrame
1287 } LZ4IO_frameType_t;
1288
1289 typedef struct {
1290 LZ4F_frameInfo_t lz4FrameInfo;
1291 LZ4IO_frameType_t frameType;
1292 } LZ4IO_frameInfo_t;
1293
1294 #define LZ4IO_INIT_FRAMEINFO { LZ4F_INIT_FRAMEINFO, lz4Frame }
1295
1296 typedef struct {
1297 const char* fileName;
1298 unsigned long long fileSize;
1299 unsigned long long frameCount;
1300 LZ4IO_frameInfo_t frameSummary;
1301 unsigned short eqFrameTypes;
1302 unsigned short eqBlockTypes;
1303 unsigned short allContentSize;
1304 } LZ4IO_cFileInfo_t;
1305
1306 #define LZ4IO_INIT_CFILEINFO { NULL, 0ULL, 0, LZ4IO_INIT_FRAMEINFO, 1, 1, 1 }
1307
1308 typedef enum { LZ4IO_LZ4F_OK, LZ4IO_format_not_known, LZ4IO_not_a_file } LZ4IO_infoResult;
1309
1310 static const char * LZ4IO_frameTypeNames[] = {"LZ4Frame", "LegacyFrame", "SkippableFrame" };
1311
1312 /* Read block headers and skip block data
1313 Return total blocks size for this frame including block headers,
1314 block checksums and content checksums.
1315 returns 0 in case it can't succesfully skip block data.
1316 Assumes SEEK_CUR after frame header.
1317 */
LZ4IO_skipBlocksData(FILE * finput,const LZ4F_blockChecksum_t blockChecksumFlag,const LZ4F_contentChecksum_t contentChecksumFlag)1318 static unsigned long long LZ4IO_skipBlocksData(FILE* finput,
1319 const LZ4F_blockChecksum_t blockChecksumFlag,
1320 const LZ4F_contentChecksum_t contentChecksumFlag) {
1321 unsigned char blockInfo[LZ4F_BLOCK_HEADER_SIZE];
1322 unsigned long long totalBlocksSize = 0;
1323 for (;;) {
1324 if (!fread(blockInfo, 1, LZ4F_BLOCK_HEADER_SIZE, finput)) {
1325 if (feof(finput)) return totalBlocksSize;
1326 return 0;
1327 }
1328 totalBlocksSize += LZ4F_BLOCK_HEADER_SIZE;
1329 {
1330 const unsigned long nextCBlockSize = LZ4IO_readLE32(&blockInfo) & 0x7FFFFFFFU;
1331 const unsigned long nextBlock = nextCBlockSize + (blockChecksumFlag * LZ4F_BLOCK_CHECKSUM_SIZE);
1332 if (nextCBlockSize == 0) {
1333 /* Reached EndMark */
1334 if (contentChecksumFlag) {
1335 /* Skip content checksum */
1336 if (UTIL_fseek(finput, LZ4F_CONTENT_CHECKSUM_SIZE, SEEK_CUR) != 0) {
1337 return 0;
1338 }
1339 totalBlocksSize += LZ4F_CONTENT_CHECKSUM_SIZE;
1340 }
1341 break;
1342 }
1343 totalBlocksSize += nextBlock;
1344 /* skip to the next block */
1345 if (UTIL_fseek(finput, nextBlock, SEEK_CUR) != 0) {
1346 return 0;
1347 }
1348 }
1349 }
1350 return totalBlocksSize;
1351 }
1352
1353 /* For legacy frames only.
1354 Read block headers and skip block data.
1355 Return total blocks size for this frame including block headers.
1356 or 0 in case it can't succesfully skip block data.
1357 This works as long as legacy block header size = magic number size.
1358 Assumes SEEK_CUR after frame header.
1359 */
LZ4IO_skipLegacyBlocksData(FILE * finput)1360 static unsigned long long LZ4IO_skipLegacyBlocksData(FILE* finput) {
1361 unsigned char blockInfo[LZIO_LEGACY_BLOCK_HEADER_SIZE];
1362 unsigned long long totalBlocksSize = 0;
1363 LZ4IO_STATIC_ASSERT(LZIO_LEGACY_BLOCK_HEADER_SIZE == MAGICNUMBER_SIZE);
1364 for (;;) {
1365 if (!fread(blockInfo, 1, LZIO_LEGACY_BLOCK_HEADER_SIZE, finput)) {
1366 if (feof(finput)) return totalBlocksSize;
1367 return 0;
1368 }
1369 { const unsigned int nextCBlockSize = LZ4IO_readLE32(&blockInfo);
1370 if ( nextCBlockSize == LEGACY_MAGICNUMBER ||
1371 nextCBlockSize == LZ4IO_MAGICNUMBER ||
1372 LZ4IO_isSkippableMagicNumber(nextCBlockSize)) {
1373 /* Rewind back. we want cursor at the begining of next frame.*/
1374 if (fseek(finput, -LZIO_LEGACY_BLOCK_HEADER_SIZE, SEEK_CUR) != 0) {
1375 return 0;
1376 }
1377 break;
1378 }
1379 totalBlocksSize += LZIO_LEGACY_BLOCK_HEADER_SIZE + nextCBlockSize;
1380 /* skip to the next block */
1381 if (UTIL_fseek(finput, nextCBlockSize, SEEK_CUR) != 0) {
1382 return 0;
1383 }
1384 }
1385 }
1386 return totalBlocksSize;
1387 }
1388
1389 /* buffer : must be a valid memory area of at least 4 bytes */
LZ4IO_blockTypeID(int sizeID,int blockMode,char * buffer)1390 const char* LZ4IO_blockTypeID(int sizeID, int blockMode, char* buffer) {
1391 buffer[0] = 'B';
1392 assert(sizeID >= 4); assert(sizeID <= 7);
1393 buffer[1] = (char)(sizeID + '0');
1394 buffer[2] = (blockMode == LZ4F_blockIndependent) ? 'I' : 'D';
1395 buffer[3] = 0;
1396 return buffer;
1397 }
1398
1399 /* buffer : must be valid memory area of at least 10 bytes */
LZ4IO_toHuman(long double size,char * buf)1400 static const char* LZ4IO_toHuman(long double size, char *buf) {
1401 const char units[] = {"\0KMGTPEZY"};
1402 size_t i = 0;
1403 for (; size >= 1024; i++) size /= 1024;
1404 sprintf(buf, "%.2Lf%c", size, units[i]);
1405 return buf;
1406 }
1407
1408 /* Get filename without path prefix */
LZ4IO_baseName(const char * input_filename)1409 static const char* LZ4IO_baseName(const char* input_filename) {
1410 const char* b = strrchr(input_filename, '/');
1411 if (!b) b = strrchr(input_filename, '\\');
1412 if (!b) return input_filename;
1413 return b ? b + 1 : b;
1414 }
1415
1416 /* Report frame/s information in verbose mode.
1417 * Will populate file info with fileName and frameSummary where applicable.
1418 * - TODO :
1419 * + report nb of blocks, hence max. possible decompressed size (when not reported in header)
1420 */
1421 static LZ4IO_infoResult
LZ4IO_getCompressedFileInfo(LZ4IO_cFileInfo_t * cfinfo,const char * input_filename)1422 LZ4IO_getCompressedFileInfo(LZ4IO_cFileInfo_t* cfinfo, const char* input_filename)
1423 {
1424 LZ4IO_infoResult result = LZ4IO_format_not_known; /* default result (error) */
1425 unsigned char buffer[LZ4F_HEADER_SIZE_MAX];
1426 FILE* const finput = LZ4IO_openSrcFile(input_filename);
1427 cfinfo->fileSize = UTIL_getFileSize(input_filename);
1428
1429 while (!feof(finput)) {
1430 LZ4IO_frameInfo_t frameInfo = LZ4IO_INIT_FRAMEINFO;
1431 unsigned magicNumber;
1432 /* Get MagicNumber */
1433 size_t nbReadBytes = fread(buffer, 1, MAGICNUMBER_SIZE, finput);
1434 if (nbReadBytes == 0) { break; } /* EOF */
1435 result = LZ4IO_format_not_known; /* default result (error) */
1436 if (nbReadBytes != MAGICNUMBER_SIZE)
1437 EXM_THROW(40, "Unrecognized header : Magic Number unreadable");
1438 magicNumber = LZ4IO_readLE32(buffer); /* Little Endian format */
1439 if (LZ4IO_isSkippableMagicNumber(magicNumber))
1440 magicNumber = LZ4IO_SKIPPABLE0; /* fold skippable magic numbers */
1441
1442 switch (magicNumber) {
1443 case LZ4IO_MAGICNUMBER:
1444 if (cfinfo->frameSummary.frameType != lz4Frame) cfinfo->eqFrameTypes = 0;
1445 /* Get frame info */
1446 { const size_t readBytes = fread(buffer + MAGICNUMBER_SIZE, 1, LZ4F_HEADER_SIZE_MIN - MAGICNUMBER_SIZE, finput);
1447 if (!readBytes || ferror(finput)) EXM_THROW(71, "Error reading %s", input_filename);
1448 }
1449 { size_t hSize = LZ4F_headerSize(&buffer, LZ4F_HEADER_SIZE_MIN);
1450 if (!LZ4F_isError(hSize)) {
1451 if (hSize > (LZ4F_HEADER_SIZE_MIN + MAGICNUMBER_SIZE)) {
1452 /* We've already read LZ4F_HEADER_SIZE_MIN so read any extra until hSize*/
1453 const size_t readBytes = fread(buffer + LZ4F_HEADER_SIZE_MIN, 1, hSize - LZ4F_HEADER_SIZE_MIN, finput);
1454 if (!readBytes || ferror(finput)) EXM_THROW(72, "Error reading %s", input_filename);
1455 }
1456 /* Create decompression context */
1457 { LZ4F_dctx* dctx;
1458 unsigned isError = LZ4F_isError(LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION));
1459 if (!isError) {
1460 isError = LZ4F_isError(LZ4F_getFrameInfo(dctx, &frameInfo.lz4FrameInfo, buffer, &hSize));
1461 LZ4F_freeDecompressionContext(dctx);
1462 if (!isError) {
1463 if ((cfinfo->frameSummary.lz4FrameInfo.blockSizeID != frameInfo.lz4FrameInfo.blockSizeID ||
1464 cfinfo->frameSummary.lz4FrameInfo.blockMode != frameInfo.lz4FrameInfo.blockMode)
1465 && cfinfo->frameCount != 0)
1466 cfinfo->eqBlockTypes = 0;
1467 { const unsigned long long totalBlocksSize = LZ4IO_skipBlocksData(finput,
1468 frameInfo.lz4FrameInfo.blockChecksumFlag,
1469 frameInfo.lz4FrameInfo.contentChecksumFlag);
1470 if (totalBlocksSize) {
1471 char bTypeBuffer[5];
1472 LZ4IO_blockTypeID(frameInfo.lz4FrameInfo.blockSizeID, frameInfo.lz4FrameInfo.blockMode, bTypeBuffer);
1473 DISPLAYLEVEL(3, " %6llu %14s %5s %8s",
1474 cfinfo->frameCount + 1,
1475 LZ4IO_frameTypeNames[frameInfo.frameType],
1476 bTypeBuffer,
1477 frameInfo.lz4FrameInfo.contentChecksumFlag ? "XXH32" : "-");
1478 if (frameInfo.lz4FrameInfo.contentSize) {
1479 { double const ratio = (double)(totalBlocksSize + hSize) / frameInfo.lz4FrameInfo.contentSize * 100;
1480 DISPLAYLEVEL(3, " %20llu %20llu %9.2f%%\n",
1481 totalBlocksSize + hSize,
1482 frameInfo.lz4FrameInfo.contentSize,
1483 ratio);
1484 }
1485 /* Now we've consumed frameInfo we can use it to store the total contentSize */
1486 frameInfo.lz4FrameInfo.contentSize += cfinfo->frameSummary.lz4FrameInfo.contentSize;
1487 }
1488 else {
1489 DISPLAYLEVEL(3, " %20llu %20s %9s \n", totalBlocksSize + hSize, "-", "-");
1490 cfinfo->allContentSize = 0;
1491 }
1492 result = LZ4IO_LZ4F_OK;
1493 }
1494 }
1495 }
1496 }
1497 }
1498 }
1499 }
1500 break;
1501 case LEGACY_MAGICNUMBER:
1502 frameInfo.frameType = legacyFrame;
1503 if (cfinfo->frameSummary.frameType != legacyFrame && cfinfo->frameCount != 0) cfinfo->eqFrameTypes = 0;
1504 cfinfo->eqBlockTypes = 0;
1505 cfinfo->allContentSize = 0;
1506 { const unsigned long long totalBlocksSize = LZ4IO_skipLegacyBlocksData(finput);
1507 if (totalBlocksSize) {
1508 DISPLAYLEVEL(3, " %6llu %14s %5s %8s %20llu %20s %9s\n",
1509 cfinfo->frameCount + 1,
1510 LZ4IO_frameTypeNames[frameInfo.frameType],
1511 "-", "-",
1512 totalBlocksSize + 4,
1513 "-", "-");
1514 result = LZ4IO_LZ4F_OK;
1515 }
1516 }
1517 break;
1518 case LZ4IO_SKIPPABLE0:
1519 frameInfo.frameType = skippableFrame;
1520 if (cfinfo->frameSummary.frameType != skippableFrame && cfinfo->frameCount != 0) cfinfo->eqFrameTypes = 0;
1521 cfinfo->eqBlockTypes = 0;
1522 cfinfo->allContentSize = 0;
1523 { nbReadBytes = fread(buffer, 1, 4, finput);
1524 if (nbReadBytes != 4)
1525 EXM_THROW(42, "Stream error : skippable size unreadable");
1526 }
1527 { unsigned const size = LZ4IO_readLE32(buffer);
1528 int const errorNb = fseek_u32(finput, size, SEEK_CUR);
1529 if (errorNb != 0)
1530 EXM_THROW(43, "Stream error : cannot skip skippable area");
1531 DISPLAYLEVEL(3, " %6llu %14s %5s %8s %20u %20s %9s\n",
1532 cfinfo->frameCount + 1,
1533 "SkippableFrame",
1534 "-", "-", size + 8, "-", "-");
1535
1536 result = LZ4IO_LZ4F_OK;
1537 }
1538 break;
1539 default:
1540 { long int const position = ftell(finput); /* only works for files < 2 GB */
1541 DISPLAYLEVEL(3, "Stream followed by undecodable data ");
1542 if (position != -1L)
1543 DISPLAYLEVEL(3, "at position %i ", (int)position);
1544 DISPLAYLEVEL(3, "\n");
1545 }
1546 break;
1547 }
1548 if (result != LZ4IO_LZ4F_OK) {
1549 break;
1550 }
1551 cfinfo->frameSummary = frameInfo;
1552 cfinfo->frameCount++;
1553 }
1554 fclose(finput);
1555 return result;
1556 }
1557
1558
LZ4IO_displayCompressedFilesInfo(const char ** inFileNames,size_t ifnIdx)1559 int LZ4IO_displayCompressedFilesInfo(const char** inFileNames, size_t ifnIdx)
1560 {
1561 int result = 0;
1562 size_t idx = 0;
1563 if (g_displayLevel < 3) {
1564 DISPLAY("%10s %14s %5s %11s %13s %9s %s\n",
1565 "Frames", "Type", "Block", "Compressed", "Uncompressed", "Ratio", "Filename");
1566 }
1567 for (; idx < ifnIdx; idx++) {
1568 /* Get file info */
1569 LZ4IO_cFileInfo_t cfinfo = LZ4IO_INIT_CFILEINFO;
1570 cfinfo.fileName = LZ4IO_baseName(inFileNames[idx]);
1571 if (!UTIL_isRegFile(inFileNames[idx])) {
1572 DISPLAYLEVEL(1, "lz4: %s is not a regular file \n", inFileNames[idx]);
1573 return 0;
1574 }
1575 DISPLAYLEVEL(3, "%s(%llu/%llu)\n", cfinfo.fileName, (unsigned long long)idx + 1, (unsigned long long)ifnIdx);
1576 DISPLAYLEVEL(3, " %6s %14s %5s %8s %20s %20s %9s\n",
1577 "Frame", "Type", "Block", "Checksum", "Compressed", "Uncompressed", "Ratio")
1578 { LZ4IO_infoResult const op_result = LZ4IO_getCompressedFileInfo(&cfinfo, inFileNames[idx]);
1579 if (op_result != LZ4IO_LZ4F_OK) {
1580 assert(op_result == LZ4IO_format_not_known);
1581 DISPLAYLEVEL(1, "lz4: %s: File format not recognized \n", inFileNames[idx]);
1582 return 0;
1583 }
1584 }
1585 DISPLAYLEVEL(3, "\n");
1586 if (g_displayLevel < 3) {
1587 /* Display Summary */
1588 { char buffers[3][10];
1589 DISPLAY("%10llu %14s %5s %11s %13s ",
1590 cfinfo.frameCount,
1591 cfinfo.eqFrameTypes ? LZ4IO_frameTypeNames[cfinfo.frameSummary.frameType] : "-" ,
1592 cfinfo.eqBlockTypes ? LZ4IO_blockTypeID(cfinfo.frameSummary.lz4FrameInfo.blockSizeID,
1593 cfinfo.frameSummary.lz4FrameInfo.blockMode, buffers[0]) : "-",
1594 LZ4IO_toHuman((long double)cfinfo.fileSize, buffers[1]),
1595 cfinfo.allContentSize ? LZ4IO_toHuman((long double)cfinfo.frameSummary.lz4FrameInfo.contentSize, buffers[2]) : "-");
1596 if (cfinfo.allContentSize) {
1597 double const ratio = (double)cfinfo.fileSize / cfinfo.frameSummary.lz4FrameInfo.contentSize * 100;
1598 DISPLAY("%9.2f%% %s \n", ratio, cfinfo.fileName);
1599 } else {
1600 DISPLAY("%9s %s\n",
1601 "-",
1602 cfinfo.fileName);
1603 }
1604 }
1605 }
1606 }
1607
1608 return result;
1609 }
1610