2 LZ4c - LZ4 Compression CLI program
\r
3 Copyright (C) Yann Collet 2011-2013
\r
6 This program is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 This program is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License along
\r
17 with this program; if not, write to the Free Software Foundation, Inc.,
\r
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 You can contact the author at :
\r
21 - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
\r
22 - LZ4 source repository : http://code.google.com/p/lz4/
\r
25 Note : this is stand-alone program.
\r
26 It is not part of LZ4 compression library, it is a user program of LZ4 library.
\r
27 The license of LZ4 library is BSD.
\r
28 The license of xxHash library is BSD.
\r
29 The license of this compression CLI program is GPLv2.
\r
32 //**************************************
\r
34 //**************************************
\r
35 // Disable some Visual warning messages
\r
36 #ifdef _MSC_VER // Visual Studio
\r
37 # define _CRT_SECURE_NO_WARNINGS
\r
38 # define _CRT_SECURE_NO_DEPRECATE // VS2005
\r
39 # pragma warning(disable : 4127) // disable: C4127: conditional expression is constant
\r
43 //****************************
\r
45 //****************************
\r
46 #include <stdio.h> // fprintf, fopen, fread, _fileno(?)
\r
47 #include <stdlib.h> // malloc
\r
48 #include <string.h> // strcmp
\r
49 #include <time.h> // clock
\r
51 #include <io.h> // _setmode
\r
52 #include <fcntl.h> // _O_BINARY
\r
60 //**************************************
\r
61 // Compiler-specific functions
\r
62 //**************************************
\r
63 #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
\r
65 #if defined(_MSC_VER) // Visual Studio
\r
66 # define swap32 _byteswap_ulong
\r
67 #elif GCC_VERSION >= 403
\r
68 # define swap32 __builtin_bswap32
\r
70 static inline unsigned int swap32(unsigned int x) {
\r
71 return ((x << 24) & 0xff000000 ) |
\r
72 ((x << 8) & 0x00ff0000 ) |
\r
73 ((x >> 8) & 0x0000ff00 ) |
\r
74 ((x >> 24) & 0x000000ff );
\r
79 //****************************
\r
81 //****************************
\r
82 #define COMPRESSOR_NAME "LZ4 Compression CLI"
\r
83 #define COMPRESSOR_VERSION ""
\r
84 #define COMPILED __DATE__
\r
85 #define AUTHOR "Yann Collet"
\r
86 #define EXTENSION ".lz4"
\r
87 #define WELCOME_MESSAGE "*** %s %s, by %s (%s) ***\n", COMPRESSOR_NAME, COMPRESSOR_VERSION, AUTHOR, COMPILED
\r
89 #define KB *(1U<<10)
\r
90 #define MB *(1U<<20)
\r
91 #define GB *(1U<<30)
\r
99 #define MAGICNUMBER_SIZE 4
\r
100 #define LZ4S_MAGICNUMBER 0x184D2204
\r
101 #define LZ4S_SKIPPABLE0 0x184D2A50
\r
102 #define LZ4S_SKIPPABLEMASK 0xFFFFFFF0
\r
103 #define LEGACY_MAGICNUMBER 0x184C2102
\r
105 #define CACHELINE 64
\r
106 #define LEGACY_BLOCKSIZE (8 MB)
\r
107 #define MIN_STREAM_BUFSIZE (1 MB + 64 KB)
\r
108 #define LZ4S_BLOCKSIZEID_DEFAULT 7
\r
109 #define LZ4S_CHECKSUM_SEED 0
\r
111 #define LZ4S_MAXHEADERSIZE (4+2+8+4+1)
\r
114 //**************************************
\r
115 // Architecture Macros
\r
116 //**************************************
\r
117 static const int one = 1;
\r
118 #define CPU_LITTLE_ENDIAN (*(char*)(&one))
\r
119 #define CPU_BIG_ENDIAN (!CPU_LITTLE_ENDIAN)
\r
120 #define LITTLE_ENDIAN_32(i) (CPU_LITTLE_ENDIAN?(i):swap32(i))
\r
123 //**************************************
\r
125 //**************************************
\r
126 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
\r
129 //**************************************
\r
130 // Special input/output
\r
131 //**************************************
\r
132 #define NULL_INPUT "null"
\r
133 char stdinmark[] = "stdin";
\r
134 char stdoutmark[] = "stdout";
\r
136 char nulmark[] = "nul";
\r
138 char nulmark[] = "/dev/null";
\r
142 //**************************************
\r
143 // Local Parameters
\r
144 //**************************************
\r
145 static int overwrite = 0;
\r
146 static int blockSizeId = LZ4S_BLOCKSIZEID_DEFAULT;
\r
147 static int blockChecksum = 0;
\r
148 static int streamChecksum = 1;
\r
149 static int blockIndependence = 1;
\r
151 //**************************************
\r
153 //**************************************
\r
155 #define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
\r
156 #define EXM_THROW(error, ...) \
\r
158 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
\r
159 DISPLAY("Error %i : ", error); \
\r
160 DISPLAY(__VA_ARGS__); \
\r
167 //****************************
\r
169 //****************************
\r
170 int usage(char* exename)
\r
172 DISPLAY( "Usage :\n");
\r
173 DISPLAY( " %s [arg] input [output]\n", exename);
\r
174 DISPLAY( "Arguments :\n");
\r
175 DISPLAY( " -c0/-c : Fast compression (default) \n");
\r
176 DISPLAY( " -c1/-hc: High compression \n");
\r
177 DISPLAY( " -d : decompression \n");
\r
178 DISPLAY( " -y : overwrite without prompting \n");
\r
179 DISPLAY( " -H : Help (this text + advanced options)\n");
\r
183 int usage_advanced()
\r
185 DISPLAY( "\nAdvanced options :\n");
\r
186 DISPLAY( " -t : test compressed file \n");
\r
187 DISPLAY( " -B# : Block size [4-7](default : 7)\n");
\r
188 DISPLAY( " -BD : Block dependency (improve compression ratio)\n");
\r
189 DISPLAY( " -BX : enable block checksum (default:disabled)\n");
\r
190 DISPLAY( " -Sx : disable stream checksum (default:enabled)\n");
\r
191 DISPLAY( " -b# : benchmark files, using # [0-1] compression level\n");
\r
192 DISPLAY( " -i# : iteration loops [1-9](default : 3), benchmark mode only\n");
\r
193 DISPLAY( "input : can be 'stdin' (pipe) or a filename\n");
\r
194 DISPLAY( "output : can be 'stdout'(pipe) or a filename or 'null'\n");
\r
195 DISPLAY( " example 1 : lz4c -hc stdin compressedfile.lz4\n");
\r
196 DISPLAY( " example 2 : lz4c -hcyB4D filename \n");
\r
200 int badusage(char* exename)
\r
202 DISPLAY("Wrong parameters\n");
\r
208 static int LZ4S_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }
\r
209 static unsigned int LZ4S_GetCheckBits_FromXXH (unsigned int xxh) { return (xxh >> 8) & _8BITS; }
\r
210 static int LZ4S_isSkippableMagicNumber(unsigned int magic) { return (magic & LZ4S_SKIPPABLEMASK) == LZ4S_SKIPPABLE0; }
\r
213 int get_fileHandle(char* input_filename, char* output_filename, FILE** pfinput, FILE** pfoutput)
\r
216 if (!strcmp (input_filename, stdinmark))
\r
218 DISPLAY( "Using stdin for input\n");
\r
220 #ifdef _WIN32 // Need to set stdin/stdout to binary mode specifically for windows
\r
221 _setmode( _fileno( stdin ), _O_BINARY );
\r
226 *pfinput = fopen(input_filename, "rb");
\r
229 if (!strcmp (output_filename, stdoutmark))
\r
231 DISPLAY( "Using stdout for output\n");
\r
232 *pfoutput = stdout;
\r
233 #ifdef _WIN32 // Need to set stdin/stdout to binary mode specifically for windows
\r
234 _setmode( _fileno( stdout ), _O_BINARY );
\r
239 // Check if destination file already exists
\r
241 if (output_filename != nulmark) *pfoutput = fopen( output_filename, "rb" );
\r
245 fclose(*pfoutput);
\r
246 DISPLAY( "Warning : %s already exists\n", output_filename);
\r
249 DISPLAY( "Overwrite ? (Y/N) : ");
\r
250 ch = (char)getchar();
\r
251 if (ch!='Y') EXM_THROW(11, "Operation aborted : %s already exists", output_filename);
\r
254 *pfoutput = fopen( output_filename, "wb" );
\r
257 if ( *pfinput==0 ) EXM_THROW(12, "Pb opening %s", input_filename);
\r
258 if ( *pfoutput==0) EXM_THROW(13, "Pb opening %s", output_filename);
\r
265 int legacy_compress_file(char* input_filename, char* output_filename, int compressionlevel)
\r
267 int (*compressionFunction)(const char*, char*, int);
\r
268 unsigned long long filesize = 0;
\r
269 unsigned long long compressedfilesize = MAGICNUMBER_SIZE;
\r
274 int displayLevel = (compressionlevel>0);
\r
275 clock_t start, end;
\r
280 switch (compressionlevel)
\r
282 case 0 : compressionFunction = LZ4_compress; break;
\r
283 case 1 : compressionFunction = LZ4_compressHC; break;
\r
284 default: compressionFunction = LZ4_compress;
\r
287 get_fileHandle(input_filename, output_filename, &finput, &foutput);
\r
290 in_buff = (char*)malloc(LEGACY_BLOCKSIZE);
\r
291 out_buff = (char*)malloc(LZ4_compressBound(LEGACY_BLOCKSIZE));
\r
292 if (!in_buff || !out_buff) EXM_THROW(21, "Allocation error : not enough memory");
\r
294 // Write Archive Header
\r
295 *(unsigned int*)out_buff = LITTLE_ENDIAN_32(LEGACY_MAGICNUMBER);
\r
296 sizeCheck = fwrite(out_buff, 1, MAGICNUMBER_SIZE, foutput);
\r
297 if (sizeCheck!=MAGICNUMBER_SIZE) EXM_THROW(22, "Write error : cannot write header");
\r
302 unsigned int outSize;
\r
304 int inSize = (int) fread(in_buff, (size_t)1, (size_t)LEGACY_BLOCKSIZE, finput);
\r
305 if( inSize<=0 ) break;
\r
306 filesize += inSize;
\r
307 if (displayLevel) DISPLAY("Read : %i MB \r", (int)(filesize>>20));
\r
310 outSize = compressionFunction(in_buff, out_buff+4, inSize);
\r
311 compressedfilesize += outSize+4;
\r
312 if (displayLevel) DISPLAY("Read : %i MB ==> %.2f%%\r", (int)(filesize>>20), (double)compressedfilesize/filesize*100);
\r
315 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(outSize);
\r
316 sizeCheck = fwrite(out_buff, 1, outSize+4, foutput);
\r
317 if (sizeCheck!=(size_t)(outSize+4)) EXM_THROW(23, "Write error : cannot write compressed block");
\r
322 DISPLAY( "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
\r
323 (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);
\r
325 double seconds = (double)(end - start)/CLOCKS_PER_SEC;
\r
326 DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
\r
339 int compress_file_blockDependency(char* input_filename, char* output_filename, int compressionlevel)
\r
341 void* (*initFunction) (const char*);
\r
342 int (*compressionFunction)(void*, const char*, char*, int, int);
\r
343 char* (*translateFunction) (void*);
\r
344 int (*freeFunction) (void*);
\r
346 unsigned long long filesize = 0;
\r
347 unsigned long long compressedfilesize = 0;
\r
348 unsigned int checkbits;
\r
349 char* in_buff, *in_start, *in_end;
\r
354 int displayLevel = (compressionlevel>0);
\r
355 clock_t start, end;
\r
356 unsigned int blockSize, inputBufferSize;
\r
357 size_t sizeCheck, header_size;
\r
358 void* streamChecksumState=NULL;
\r
363 switch (compressionlevel)
\r
368 initFunction = LZ4_createHC;
\r
369 compressionFunction = LZ4_compressHC_limitedOutput_continue;
\r
370 translateFunction = LZ4_slideInputBufferHC;
\r
371 freeFunction = LZ4_freeHC;
\r
373 errorcode = get_fileHandle(input_filename, output_filename, &finput, &foutput);
\r
374 if (errorcode) return errorcode;
\r
375 blockSize = LZ4S_GetBlockSize_FromBlockId (blockSizeId);
\r
378 inputBufferSize = blockSize + 64 KB;
\r
379 if (inputBufferSize < MIN_STREAM_BUFSIZE) inputBufferSize = MIN_STREAM_BUFSIZE;
\r
380 in_buff = (char*)malloc(inputBufferSize);
\r
381 out_buff = (char*)malloc(blockSize+CACHELINE);
\r
382 if (!in_buff || !out_buff) EXM_THROW(31, "Allocation error : not enough memory");
\r
383 in_start = in_buff; in_end = in_buff + inputBufferSize;
\r
384 if (streamChecksum) streamChecksumState = XXH32_init(LZ4S_CHECKSUM_SEED);
\r
385 ctx = initFunction(in_buff);
\r
387 // Write Archive Header
\r
388 *(unsigned int*)out_buff = LITTLE_ENDIAN_32(LZ4S_MAGICNUMBER); // Magic Number, in Little Endian convention
\r
389 *(out_buff+4) = (1 & _2BITS) << 6 ; // Version('01')
\r
390 *(out_buff+4) |= (blockIndependence & _1BIT) << 5;
\r
391 *(out_buff+4) |= (blockChecksum & _1BIT) << 4;
\r
392 *(out_buff+4) |= (streamChecksum & _1BIT) << 2;
\r
393 *(out_buff+5) = (char)((blockSizeId & _3BITS) << 4);
\r
394 checkbits = XXH32((out_buff+4), 2, LZ4S_CHECKSUM_SEED);
\r
395 checkbits = LZ4S_GetCheckBits_FromXXH(checkbits);
\r
396 *(out_buff+6) = (unsigned char) checkbits;
\r
398 sizeCheck = fwrite(out_buff, 1, header_size, foutput);
\r
399 if (sizeCheck!=header_size) EXM_THROW(32, "Write error : cannot write header");
\r
400 compressedfilesize += header_size;
\r
405 unsigned int outSize;
\r
406 unsigned int inSize;
\r
408 if ((in_start+blockSize) > in_end) in_start = translateFunction(ctx);
\r
409 inSize = (unsigned int) fread(in_start, (size_t)1, (size_t)blockSize, finput);
\r
410 if( inSize<=0 ) break; // No more input : end of compression
\r
411 filesize += inSize;
\r
412 if (displayLevel) DISPLAY("Read : %i MB \r", (int)(filesize>>20));
\r
413 if (streamChecksum) XXH32_update(streamChecksumState, in_start, inSize);
\r
416 outSize = compressionFunction(ctx, in_start, out_buff+4, inSize, inSize-1);
\r
417 if (outSize > 0) compressedfilesize += outSize+4; else compressedfilesize += inSize+4;
\r
418 if (blockChecksum) compressedfilesize+=4;
\r
419 if (displayLevel) DISPLAY("Read : %i MB ==> %.2f%%\r", (int)(filesize>>20), (double)compressedfilesize/filesize*100);
\r
424 unsigned int checksum;
\r
426 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(outSize);
\r
429 checksum = XXH32(out_buff+4, outSize, LZ4S_CHECKSUM_SEED);
\r
430 * (unsigned int*) (out_buff+4+outSize) = LITTLE_ENDIAN_32(checksum);
\r
432 sizeToWrite = 4 + outSize + (4*blockChecksum);
\r
433 sizeCheck = fwrite(out_buff, 1, sizeToWrite, foutput);
\r
434 if (sizeCheck!=(size_t)(sizeToWrite)) EXM_THROW(33, "Write error : cannot write compressed block");
\r
437 else // Copy Original
\r
439 unsigned int checksum;
\r
440 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(inSize|0x80000000); // Add Uncompressed flag
\r
441 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
442 if (sizeCheck!=(size_t)(4)) EXM_THROW(34, "Write error : cannot write block header");
\r
443 sizeCheck = fwrite(in_start, 1, inSize, foutput);
\r
444 if (sizeCheck!=(size_t)(inSize)) EXM_THROW(35, "Write error : cannot write block");
\r
447 checksum = XXH32(in_start, inSize, LZ4S_CHECKSUM_SEED);
\r
448 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);
\r
449 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
450 if (sizeCheck!=(size_t)(4)) EXM_THROW(36, "Write error : cannot write block checksum");
\r
453 in_start += inSize;
\r
456 // End of Stream mark
\r
457 * (unsigned int*) out_buff = LZ4S_EOS;
\r
458 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
459 if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write end of stream");
\r
460 compressedfilesize += 4;
\r
461 if (streamChecksum)
\r
463 unsigned int checksum = XXH32_digest(streamChecksumState);
\r
464 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);
\r
465 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
466 if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write stream checksum");
\r
467 compressedfilesize += 4;
\r
472 DISPLAY( "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
\r
473 (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);
\r
475 double seconds = (double)(end - start)/CLOCKS_PER_SEC;
\r
476 DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
\r
490 int compress_file(char* input_filename, char* output_filename, int compressionlevel)
\r
492 int (*compressionFunction)(const char*, char*, int, int);
\r
493 unsigned long long filesize = 0;
\r
494 unsigned long long compressedfilesize = 0;
\r
495 unsigned int checkbits;
\r
501 int displayLevel = (compressionlevel>0);
\r
502 clock_t start, end;
\r
504 size_t sizeCheck, header_size;
\r
505 void* streamChecksumState=NULL;
\r
508 if (blockIndependence==0) return compress_file_blockDependency(input_filename, output_filename, compressionlevel);
\r
512 switch (compressionlevel)
\r
514 case 0 : compressionFunction = LZ4_compress_limitedOutput; break;
\r
515 case 1 : compressionFunction = LZ4_compressHC_limitedOutput; break;
\r
516 default: compressionFunction = LZ4_compress_limitedOutput;
\r
518 errorcode = get_fileHandle(input_filename, output_filename, &finput, &foutput);
\r
519 if (errorcode) return errorcode;
\r
520 blockSize = LZ4S_GetBlockSize_FromBlockId (blockSizeId);
\r
523 in_buff = (char*)malloc(blockSize);
\r
524 out_buff = (char*)malloc(blockSize+CACHELINE);
\r
525 if (!in_buff || !out_buff) EXM_THROW(31, "Allocation error : not enough memory");
\r
526 if (streamChecksum) streamChecksumState = XXH32_init(LZ4S_CHECKSUM_SEED);
\r
528 // Write Archive Header
\r
529 *(unsigned int*)out_buff = LITTLE_ENDIAN_32(LZ4S_MAGICNUMBER); // Magic Number, in Little Endian convention
\r
530 *(out_buff+4) = (1 & _2BITS) << 6 ; // Version('01')
\r
531 *(out_buff+4) |= (blockIndependence & _1BIT) << 5;
\r
532 *(out_buff+4) |= (blockChecksum & _1BIT) << 4;
\r
533 *(out_buff+4) |= (streamChecksum & _1BIT) << 2;
\r
534 *(out_buff+5) = (char)((blockSizeId & _3BITS) << 4);
\r
535 checkbits = XXH32((out_buff+4), 2, LZ4S_CHECKSUM_SEED);
\r
536 checkbits = LZ4S_GetCheckBits_FromXXH(checkbits);
\r
537 *(out_buff+6) = (unsigned char) checkbits;
\r
539 sizeCheck = fwrite(out_buff, 1, header_size, foutput);
\r
540 if (sizeCheck!=header_size) EXM_THROW(32, "Write error : cannot write header");
\r
541 compressedfilesize += header_size;
\r
546 unsigned int outSize;
\r
548 unsigned int inSize = (unsigned int) fread(in_buff, (size_t)1, (size_t)blockSize, finput);
\r
549 if( inSize<=0 ) break; // No more input : end of compression
\r
550 filesize += inSize;
\r
551 if (displayLevel) DISPLAY("Read : %i MB \r", (int)(filesize>>20));
\r
552 if (streamChecksum) XXH32_update(streamChecksumState, in_buff, inSize);
\r
555 outSize = compressionFunction(in_buff, out_buff+4, inSize, inSize-1);
\r
556 if (outSize > 0) compressedfilesize += outSize+4; else compressedfilesize += inSize+4;
\r
557 if (blockChecksum) compressedfilesize+=4;
\r
558 if (displayLevel) DISPLAY("Read : %i MB ==> %.2f%%\r", (int)(filesize>>20), (double)compressedfilesize/filesize*100);
\r
563 unsigned int checksum;
\r
565 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(outSize);
\r
568 checksum = XXH32(out_buff+4, outSize, LZ4S_CHECKSUM_SEED);
\r
569 * (unsigned int*) (out_buff+4+outSize) = LITTLE_ENDIAN_32(checksum);
\r
571 sizeToWrite = 4 + outSize + (4*blockChecksum);
\r
572 sizeCheck = fwrite(out_buff, 1, sizeToWrite, foutput);
\r
573 if (sizeCheck!=(size_t)(sizeToWrite)) EXM_THROW(33, "Write error : cannot write compressed block");
\r
576 else // Copy Original
\r
578 unsigned int checksum;
\r
579 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(inSize|0x80000000); // Add Uncompressed flag
\r
580 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
581 if (sizeCheck!=(size_t)(4)) EXM_THROW(34, "Write error : cannot write block header");
\r
582 sizeCheck = fwrite(in_buff, 1, inSize, foutput);
\r
583 if (sizeCheck!=(size_t)(inSize)) EXM_THROW(35, "Write error : cannot write block");
\r
586 checksum = XXH32(in_buff, inSize, LZ4S_CHECKSUM_SEED);
\r
587 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);
\r
588 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
589 if (sizeCheck!=(size_t)(4)) EXM_THROW(36, "Write error : cannot write block checksum");
\r
594 // End of Stream mark
\r
595 * (unsigned int*) out_buff = LZ4S_EOS;
\r
596 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
597 if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write end of stream");
\r
598 compressedfilesize += 4;
\r
599 if (streamChecksum)
\r
601 unsigned int checksum = XXH32_digest(streamChecksumState);
\r
602 * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);
\r
603 sizeCheck = fwrite(out_buff, 1, 4, foutput);
\r
604 if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write stream checksum");
\r
605 compressedfilesize += 4;
\r
610 DISPLAY( "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
\r
611 (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);
\r
613 double seconds = (double)(end - start)/CLOCKS_PER_SEC;
\r
614 DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
\r
627 unsigned long long decodeLegacyStream(FILE* finput, FILE* foutput)
\r
629 unsigned long long filesize = 0;
\r
634 unsigned int blockSize;
\r
639 in_buff = (char*)malloc(LZ4_compressBound(LEGACY_BLOCKSIZE));
\r
640 out_buff = (char*)malloc(LEGACY_BLOCKSIZE);
\r
641 if (!in_buff || !out_buff) EXM_THROW(51, "Allocation error : not enough memory");
\r
647 uselessRet = fread(&blockSize, 1, 4, finput);
\r
648 if( uselessRet==0 ) break; // Nothing to read : file read is completed
\r
649 blockSize = LITTLE_ENDIAN_32(blockSize); // Convert to Little Endian
\r
650 if (blockSize > LZ4_COMPRESSBOUND(LEGACY_BLOCKSIZE))
\r
651 { // Cannot read next block : maybe new stream ?
\r
652 fseek(finput, -4, SEEK_CUR);
\r
657 uselessRet = fread(in_buff, 1, blockSize, finput);
\r
660 sinkint = LZ4_decompress_safe(in_buff, out_buff, blockSize, LEGACY_BLOCKSIZE);
\r
661 if (sinkint < 0) EXM_THROW(52, "Decoding Failed ! Corrupted input detected !");
\r
662 filesize += sinkint;
\r
665 sizeCheck = fwrite(out_buff, 1, sinkint, foutput);
\r
666 if (sizeCheck != (size_t)sinkint) EXM_THROW(53, "Write error : cannot write decoded block into output\n");
\r
677 unsigned long long decodeLZ4S(FILE* finput, FILE* foutput)
\r
679 unsigned long long filesize = 0;
\r
681 char* out_buff, *out_start, *out_end;
\r
682 unsigned char descriptor[LZ4S_MAXHEADERSIZE];
\r
683 size_t nbReadBytes;
\r
684 int decodedBytes=0;
\r
685 unsigned int maxBlockSize;
\r
687 int blockChecksumFlag, streamChecksumFlag, blockIndependenceFlag;
\r
688 void* streamChecksumState=NULL;
\r
689 int (*decompressionFunction)(const char*, char*, int, int) = LZ4_decompress_safe;
\r
690 unsigned int prefix64k = 0;
\r
692 // Decode stream descriptor
\r
693 nbReadBytes = fread(descriptor, 1, 3, finput);
\r
694 if (nbReadBytes != 3) EXM_THROW(61, "Unreadable header");
\r
696 int version = (descriptor[0] >> 6) & _2BITS;
\r
697 int streamSize = (descriptor[0] >> 3) & _1BIT;
\r
698 int reserved1 = (descriptor[0] >> 1) & _1BIT;
\r
699 int dictionary = (descriptor[0] >> 0) & _1BIT;
\r
701 int reserved2 = (descriptor[1] >> 7) & _1BIT;
\r
702 int blockSizeId = (descriptor[1] >> 4) & _3BITS;
\r
703 int reserved3 = (descriptor[1] >> 0) & _4BITS;
\r
704 int checkBits = (descriptor[2] >> 0) & _8BITS;
\r
705 int checkBits_xxh32;
\r
707 blockIndependenceFlag=(descriptor[0] >> 5) & _1BIT;
\r
708 blockChecksumFlag = (descriptor[0] >> 4) & _1BIT;
\r
709 streamChecksumFlag= (descriptor[0] >> 2) & _1BIT;
\r
711 if (version != 1) EXM_THROW(62, "Wrong version number");
\r
712 if (streamSize == 1) EXM_THROW(64, "Does not support stream size");
\r
713 if (reserved1 != 0) EXM_THROW(65, "Wrong value for reserved bits");
\r
714 if (dictionary == 1) EXM_THROW(66, "Does not support dictionary");
\r
715 if (reserved2 != 0) EXM_THROW(67, "Wrong value for reserved bits");
\r
716 if (blockSizeId < 4) EXM_THROW(68, "Unsupported block size");
\r
717 if (reserved3 != 0) EXM_THROW(67, "Wrong value for reserved bits");
\r
718 maxBlockSize = LZ4S_GetBlockSize_FromBlockId(blockSizeId);
\r
719 // Checkbits verification
\r
720 descriptor[1] &= 0xF0;
\r
721 checkBits_xxh32 = XXH32(descriptor, 2, LZ4S_CHECKSUM_SEED);
\r
722 checkBits_xxh32 = LZ4S_GetCheckBits_FromXXH(checkBits_xxh32);
\r
723 if (checkBits != checkBits_xxh32) EXM_THROW(69, "Stream descriptor error detected");
\r
726 if (!blockIndependenceFlag)
\r
728 decompressionFunction = LZ4_decompress_safe_withPrefix64k;
\r
734 unsigned int outbuffSize = prefix64k+maxBlockSize;
\r
735 in_buff = (char*)malloc(maxBlockSize);
\r
736 if (outbuffSize < MIN_STREAM_BUFSIZE) outbuffSize = MIN_STREAM_BUFSIZE;
\r
737 out_buff = (char*)malloc(outbuffSize);
\r
738 out_end = out_buff + outbuffSize;
\r
739 out_start = out_buff + prefix64k;
\r
740 if (!in_buff || !out_buff) EXM_THROW(70, "Allocation error : not enough memory");
\r
742 if (streamChecksumFlag) streamChecksumState = XXH32_init(LZ4S_CHECKSUM_SEED);
\r
747 unsigned int blockSize, uncompressedFlag;
\r
750 nbReadBytes = fread(&blockSize, 1, 4, finput);
\r
751 if( nbReadBytes != 4 ) EXM_THROW(71, "Read error : cannot read next block size");
\r
752 if (blockSize == LZ4S_EOS) break; // End of Stream Mark : stream is completed
\r
753 blockSize = LITTLE_ENDIAN_32(blockSize); // Convert to little endian
\r
754 uncompressedFlag = blockSize >> 31;
\r
755 blockSize &= 0x7FFFFFFF;
\r
756 if (blockSize > maxBlockSize) EXM_THROW(72, "Error : invalid block size");
\r
759 nbReadBytes = fread(in_buff, 1, blockSize, finput);
\r
760 if( nbReadBytes != blockSize ) EXM_THROW(73, "Read error : cannot read data block" );
\r
763 if (blockChecksumFlag)
\r
765 unsigned int checksum = XXH32(in_buff, blockSize, LZ4S_CHECKSUM_SEED);
\r
766 unsigned int readChecksum;
\r
767 sizeCheck = fread(&readChecksum, 1, 4, finput);
\r
768 if( sizeCheck != 4 ) EXM_THROW(74, "Read error : cannot read next block size");
\r
769 readChecksum = LITTLE_ENDIAN_32(readChecksum); // Convert to little endian
\r
770 if (checksum != readChecksum) EXM_THROW(75, "Error : invalid block checksum detected");
\r
773 if (uncompressedFlag)
\r
775 // Write uncompressed Block
\r
776 sizeCheck = fwrite(in_buff, 1, blockSize, foutput);
\r
777 if (sizeCheck != (size_t)blockSize) EXM_THROW(76, "Write error : cannot write data block");
\r
778 filesize += blockSize;
\r
779 if (streamChecksumFlag) XXH32_update(streamChecksumState, in_buff, blockSize);
\r
780 if (!blockIndependenceFlag)
\r
782 if (blockSize >= prefix64k)
\r
784 memcpy(out_buff, in_buff + (blockSize - prefix64k), prefix64k); // Required for reference for next blocks
\r
785 out_start = out_buff + prefix64k;
\r
790 memcpy(out_start, in_buff, blockSize);
\r
797 decodedBytes = decompressionFunction(in_buff, out_start, blockSize, maxBlockSize);
\r
798 if (decodedBytes < 0) EXM_THROW(77, "Decoding Failed ! Corrupted input detected !");
\r
799 filesize += decodedBytes;
\r
800 if (streamChecksumFlag) XXH32_update(streamChecksumState, out_start, decodedBytes);
\r
803 sizeCheck = fwrite(out_start, 1, decodedBytes, foutput);
\r
804 if (sizeCheck != (size_t)decodedBytes) EXM_THROW(78, "Write error : cannot write decoded block\n");
\r
807 if (!blockIndependenceFlag)
\r
809 out_start += decodedBytes;
\r
810 if (out_start + maxBlockSize > out_end)
\r
812 memcpy(out_buff, out_start - prefix64k, prefix64k);
\r
813 out_start = out_buff + prefix64k;
\r
819 if (streamChecksumFlag)
\r
821 unsigned int checksum = XXH32_digest(streamChecksumState);
\r
822 unsigned int readChecksum;
\r
823 sizeCheck = fread(&readChecksum, 1, 4, finput);
\r
824 if (sizeCheck != 4) EXM_THROW(74, "Read error : cannot read stream checksum");
\r
825 readChecksum = LITTLE_ENDIAN_32(readChecksum); // Convert to little endian
\r
826 if (checksum != readChecksum) EXM_THROW(75, "Error : invalid stream checksum detected");
\r
837 unsigned long long selectDecoder( FILE* finput, FILE* foutput)
\r
839 unsigned int magicNumber, size;
\r
841 size_t nbReadBytes;
\r
843 // Check Archive Header
\r
844 nbReadBytes = fread(&magicNumber, 1, MAGICNUMBER_SIZE, finput);
\r
845 if (nbReadBytes==0) return 0; // EOF
\r
846 if (nbReadBytes != MAGICNUMBER_SIZE) EXM_THROW(41, "Unrecognized header : Magic Number unreadable");
\r
847 magicNumber = LITTLE_ENDIAN_32(magicNumber); // Convert to Little Endian format
\r
848 if (LZ4S_isSkippableMagicNumber(magicNumber)) magicNumber = LZ4S_SKIPPABLE0; // fold skippable magic numbers
\r
850 switch(magicNumber)
\r
852 case LZ4S_MAGICNUMBER:
\r
853 return decodeLZ4S(finput, foutput);
\r
854 case LEGACY_MAGICNUMBER:
\r
855 DISPLAY("Detected : Legacy format \n");
\r
856 return decodeLegacyStream(finput, foutput);
\r
857 case LZ4S_SKIPPABLE0:
\r
858 nbReadBytes = fread(&size, 1, 4, finput);
\r
859 if (nbReadBytes != 4) EXM_THROW(42, "Stream error : skippable size unreadable");
\r
860 size = LITTLE_ENDIAN_32(size); // Convert to Little Endian format
\r
861 errorNb = fseek(finput, size, SEEK_CUR);
\r
862 if (errorNb != 0) EXM_THROW(43, "Stream error : cannot skip skippable area");
\r
863 return selectDecoder(finput, foutput);
\r
865 if (ftell(finput) == MAGICNUMBER_SIZE) EXM_THROW(44,"Unrecognized header : file cannot be decoded"); // Wrong magic number at the beginning of 1st stream
\r
866 DISPLAY("Stream followed by unrecognized data\n");
\r
872 int decodeFile(char* input_filename, char* output_filename)
\r
874 unsigned long long filesize = 0, decodedSize=0;
\r
877 clock_t start, end;
\r
882 get_fileHandle(input_filename, output_filename, &finput, &foutput);
\r
884 // Loop over multiple streams
\r
887 decodedSize = selectDecoder(finput, foutput);
\r
888 filesize += decodedSize;
\r
889 } while (decodedSize);
\r
893 DISPLAY( "Successfully decoded %llu bytes \n", filesize);
\r
895 double seconds = (double)(end - start)/CLOCKS_PER_SEC;
\r
896 DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
\r
903 // Error status = OK
\r
908 int main(int argc, char** argv)
\r
916 char* exename=argv[0];
\r
917 char* input_filename=0;
\r
918 char* output_filename=0;
\r
919 char nullinput[] = NULL_INPUT;
\r
920 char extension[] = EXTENSION;
\r
923 DISPLAY( WELCOME_MESSAGE);
\r
925 if (argc<2) { badusage(exename); return 1; }
\r
927 for(i=1; i<argc; i++)
\r
929 char* argument = argv[i];
\r
931 if(!argument) continue; // Protection if argument empty
\r
933 // Decode command (note : aggregated commands are allowed)
\r
934 if (argument[0]=='-')
\r
936 while (argument[1]!=0)
\r
940 switch(argument[0])
\r
942 // Display help on usage
\r
943 case 'H': usage(exename); usage_advanced(); return 0;
\r
945 // Compression (default)
\r
946 case 'c': if ((argument[1] >='0') && (argument[1] <='1')) { cLevel=argument[1] - '0'; argument++; } break;
\r
947 case 'h': if (argument[1]=='c') { cLevel=1; argument++; } break;
\r
949 // Use Legacy format (hidden option)
\r
950 case 'l': legacy_format=1; break;
\r
953 case 'd': decode=1; break;
\r
956 case 't': decode=1; output_filename=nulmark; break;
\r
958 // Modify Block Properties
\r
960 while (argument[1]!=0)
\r
961 switch(argument[1])
\r
968 int B = argument[1] - '0';
\r
969 int S = 1 << (8 + 2*B);
\r
970 BMK_SetBlocksize(S);
\r
975 case 'D': blockIndependence = 0, argument++; break;
\r
976 case 'X': blockChecksum = 1, argument ++; break;
\r
977 default : goto _exit_blockProperties;
\r
979 _exit_blockProperties:
\r
982 // Modify Stream properties
\r
983 case 'S': if (argument[1]=='x') { streamChecksum=0; argument++; break; } else { badusage(exename); return 1; }
\r
986 case 'b': bench=1;
\r
987 if ((argument[1] >='0') && (argument[1] <='1')) { cLevel=argument[1] - '0'; argument++; }
\r
990 // Modify Nb Iterations (benchmark only)
\r
992 if ((argument[1] >='1') && (argument[1] <='9'))
\r
994 int iters = argument[1] - '0';
\r
995 BMK_SetNbIterations(iters);
\r
1000 // Pause at the end (benchmark only) (hidden option)
\r
1001 case 'p': BMK_SetPause(); break;
\r
1004 case 'y': overwrite=1; break;
\r
1006 // Unrecognised command
\r
1007 default : badusage(exename); return 1;
\r
1013 // first provided filename is input
\r
1014 if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }
\r
1016 // second provided filename is output
\r
1017 if (!output_filename)
\r
1019 output_filename=argument;
\r
1020 if (!strcmp (output_filename, nullinput)) output_filename = nulmark;
\r
1025 // No input filename ==> Error
\r
1026 if(!input_filename) { badusage(exename); return 1; }
\r
1028 if (bench) return BMK_benchFile(argv+filenamesStart, argc-filenamesStart, cLevel);
\r
1030 // No output filename ==> build one automatically (when possible)
\r
1031 if (!output_filename)
\r
1033 if (!decode) // compression
\r
1036 while (input_filename[l]!=0) l++;
\r
1037 output_filename = (char*)calloc(1,l+5);
\r
1038 for (i=0;i<l;i++) output_filename[i] = input_filename[i];
\r
1039 for (i=l;i<l+4;i++) output_filename[i] = extension[i-l];
\r
1041 else // decompression (input file must respect format extension ".lz4")
\r
1044 while (input_filename[inl]!=0) inl++;
\r
1045 output_filename = (char*)calloc(1,inl+1);
\r
1046 for (outl=0;outl<inl;outl++) output_filename[outl] = input_filename[outl];
\r
1048 while ((outl >= inl-4) && (input_filename[outl] == extension[outl-inl+4])) output_filename[outl--]=0;
\r
1049 if (outl != inl-5) output_filename = NULL;
\r
1051 if (!output_filename) { badusage(exename); return 1; }
\r
1054 if (decode) return decodeFile(input_filename, output_filename);
\r
1056 // compression is default action
\r
1057 if (legacy_format)
\r
1059 DISPLAY("! Generating compressed LZ4 using Legacy format (deprecated !) ! \n");
\r
1060 return legacy_compress_file(input_filename, output_filename, cLevel);
\r
1064 return compress_file(input_filename, output_filename, cLevel);
\r