1 /* 2 LZ4io.c - LZ4 File/Stream Interface 3 Copyright (C) Yann Collet 2011-2014 4 GPL v2 License 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License along 17 with this program; if not, write to the Free Software Foundation, Inc., 18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 20 You can contact the author at : 21 - LZ4 source repository : http://code.google.com/p/lz4/ 22 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c 23 */ 24 /* 25 Note : this is stand-alone program. 26 It is not part of LZ4 compression library, it is a user code of the LZ4 library. 27 - The license of LZ4 library is BSD. 28 - The license of xxHash library is BSD. 29 - The license of this source file is GPLv2. 30 */ 31 32 /************************************** 33 * Compiler Options 34 ***************************************/ 35 #ifdef _MSC_VER /* Visual Studio */ 36 # define _CRT_SECURE_NO_WARNINGS 37 # define _CRT_SECURE_NO_DEPRECATE /* VS2005 */ 38 # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ 39 #endif 40 41 #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) 42 #ifdef __GNUC__ 43 # pragma GCC diagnostic ignored "-Wmissing-braces" /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */ 44 # pragma GCC diagnostic ignored "-Wmissing-field-initializers" /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */ 45 #endif 46 47 #define _LARGE_FILES /* Large file support on 32-bits AIX */ 48 #define _FILE_OFFSET_BITS 64 /* Large file support on 32-bits unix */ 49 #define _POSIX_SOURCE 1 /* for fileno() within <stdio.h> on unix */ 50 51 52 /**************************** 53 * Includes 54 *****************************/ 55 #include <stdio.h> /* fprintf, fopen, fread, _fileno, stdin, stdout */ 56 #include <stdlib.h> /* malloc, free */ 57 #include <string.h> /* strcmp, strlen */ 58 #include <time.h> /* clock */ 59 #include "lz4io.h" 60 #include "lz4.h" /* still required for legacy format */ 61 #include "lz4hc.h" /* still required for legacy format */ 62 #include "lz4frame.h" 63 64 65 /**************************** 66 * OS-specific Includes 67 *****************************/ 68 #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) 69 # include <fcntl.h> /* _O_BINARY */ 70 # include <io.h> /* _setmode, _isatty */ 71 # ifdef __MINGW32__ 72 int _fileno(FILE *stream); /* MINGW somehow forgets to include this windows declaration into <stdio.h> */ 73 # endif 74 # define SET_BINARY_MODE(file) _setmode(_fileno(file), _O_BINARY) 75 # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) 76 #else 77 # include <unistd.h> /* isatty */ 78 # define SET_BINARY_MODE(file) 79 # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) 80 #endif 81 82 83 /**************************** 84 * Constants 85 *****************************/ 86 #define KB *(1 <<10) 87 #define MB *(1 <<20) 88 #define GB *(1U<<30) 89 90 #define _1BIT 0x01 91 #define _2BITS 0x03 92 #define _3BITS 0x07 93 #define _4BITS 0x0F 94 #define _8BITS 0xFF 95 96 #define MAGICNUMBER_SIZE 4 97 #define LZ4S_MAGICNUMBER 0x184D2204 98 #define LZ4S_SKIPPABLE0 0x184D2A50 99 #define LZ4S_SKIPPABLEMASK 0xFFFFFFF0 100 #define LEGACY_MAGICNUMBER 0x184C2102 101 102 #define CACHELINE 64 103 #define LEGACY_BLOCKSIZE (8 MB) 104 #define MIN_STREAM_BUFSIZE (192 KB) 105 #define LZ4S_BLOCKSIZEID_DEFAULT 7 106 #define LZ4S_CHECKSUM_SEED 0 107 #define LZ4S_EOS 0 108 #define LZ4S_MAXHEADERSIZE (MAGICNUMBER_SIZE+2+8+4+1) 109 110 111 /************************************** 112 * Macros 113 ***************************************/ 114 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) 115 #define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); } 116 #define DISPLAYUPDATE(l, ...) if (displayLevel>=l) { \ 117 if ((LZ4IO_GetMilliSpan(g_time) > refreshRate) || (displayLevel>=4)) \ 118 { g_time = clock(); DISPLAY(__VA_ARGS__); \ 119 if (displayLevel>=4) fflush(stdout); } } 120 static const unsigned refreshRate = 150; 121 static clock_t g_time = 0; 122 123 124 /************************************** 125 * Local Parameters 126 ***************************************/ 127 static int displayLevel = 0; /* 0 : no display ; 1: errors ; 2 : + result + interaction + warnings ; 3 : + progression; 4 : + information */ 128 static int overwrite = 1; 129 static int globalBlockSizeId = LZ4S_BLOCKSIZEID_DEFAULT; 130 static int blockChecksum = 0; 131 static int streamChecksum = 1; 132 static int blockIndependence = 1; 133 134 static const int minBlockSizeID = 4; 135 static const int maxBlockSizeID = 7; 136 137 138 /************************************** 139 * Exceptions 140 ***************************************/ 141 #define DEBUG 0 142 #define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__); 143 #define EXM_THROW(error, ...) \ 144 { \ 145 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \ 146 DISPLAYLEVEL(1, "Error %i : ", error); \ 147 DISPLAYLEVEL(1, __VA_ARGS__); \ 148 DISPLAYLEVEL(1, "\n"); \ 149 exit(error); \ 150 } 151 152 153 /************************************** 154 * Version modifiers 155 ***************************************/ 156 #define EXTENDED_ARGUMENTS 157 #define EXTENDED_HELP 158 #define EXTENDED_FORMAT 159 #define DEFAULT_COMPRESSOR compress_file 160 #define DEFAULT_DECOMPRESSOR decodeLZ4S 161 162 163 /* ************************************************** */ 164 /* ****************** Parameters ******************** */ 165 /* ************************************************** */ 166 167 /* Default setting : overwrite = 1; return : overwrite mode (0/1) */ 168 int LZ4IO_setOverwrite(int yes) 169 { 170 overwrite = (yes!=0); 171 return overwrite; 172 } 173 174 /* blockSizeID : valid values : 4-5-6-7 */ 175 int LZ4IO_setBlockSizeID(int bsid) 176 { 177 static const int blockSizeTable[] = { 64 KB, 256 KB, 1 MB, 4 MB }; 178 if ((bsid < minBlockSizeID) || (bsid > maxBlockSizeID)) return -1; 179 globalBlockSizeId = bsid; 180 return blockSizeTable[globalBlockSizeId-minBlockSizeID]; 181 } 182 183 int LZ4IO_setBlockMode(LZ4IO_blockMode_t blockMode) 184 { 185 blockIndependence = (blockMode == LZ4IO_blockIndependent); 186 return blockIndependence; 187 } 188 189 /* Default setting : no checksum */ 190 int LZ4IO_setBlockChecksumMode(int xxhash) 191 { 192 blockChecksum = (xxhash != 0); 193 return blockChecksum; 194 } 195 196 /* Default setting : checksum enabled */ 197 int LZ4IO_setStreamChecksumMode(int xxhash) 198 { 199 streamChecksum = (xxhash != 0); 200 return streamChecksum; 201 } 202 203 /* Default setting : 0 (no notification) */ 204 int LZ4IO_setNotificationLevel(int level) 205 { 206 displayLevel = level; 207 return displayLevel; 208 } 209 210 static unsigned LZ4IO_GetMilliSpan(clock_t nPrevious) 211 { 212 clock_t nCurrent = clock(); 213 unsigned nSpan = (unsigned)(((nCurrent - nPrevious) * 1000) / CLOCKS_PER_SEC); 214 return nSpan; 215 } 216 217 218 /* ************************************************************************ */ 219 /* ********************** LZ4 File / Pipe compression ********************* */ 220 /* ************************************************************************ */ 221 222 static int LZ4S_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); } 223 static int LZ4S_isSkippableMagicNumber(unsigned int magic) { return (magic & LZ4S_SKIPPABLEMASK) == LZ4S_SKIPPABLE0; } 224 225 226 static int get_fileHandle(char* input_filename, char* output_filename, FILE** pfinput, FILE** pfoutput) 227 { 228 229 if (!strcmp (input_filename, stdinmark)) 230 { 231 DISPLAYLEVEL(4,"Using stdin for input\n"); 232 *pfinput = stdin; 233 SET_BINARY_MODE(stdin); 234 } 235 else 236 { 237 *pfinput = fopen(input_filename, "rb"); 238 } 239 240 if (!strcmp (output_filename, stdoutmark)) 241 { 242 DISPLAYLEVEL(4,"Using stdout for output\n"); 243 *pfoutput = stdout; 244 SET_BINARY_MODE(stdout); 245 } 246 else 247 { 248 /* Check if destination file already exists */ 249 *pfoutput=0; 250 if (output_filename != nulmark) *pfoutput = fopen( output_filename, "rb" ); 251 if (*pfoutput!=0) 252 { 253 fclose(*pfoutput); 254 if (!overwrite) 255 { 256 char ch; 257 DISPLAYLEVEL(2, "Warning : %s already exists\n", output_filename); 258 DISPLAYLEVEL(2, "Overwrite ? (Y/N) : "); 259 if (displayLevel <= 1) EXM_THROW(11, "Operation aborted : %s already exists", output_filename); /* No interaction possible */ 260 ch = (char)getchar(); 261 if ((ch!='Y') && (ch!='y')) EXM_THROW(11, "Operation aborted : %s already exists", output_filename); 262 } 263 } 264 *pfoutput = fopen( output_filename, "wb" ); 265 } 266 267 if ( *pfinput==0 ) EXM_THROW(12, "Pb opening %s", input_filename); 268 if ( *pfoutput==0) EXM_THROW(13, "Pb opening %s", output_filename); 269 270 return 0; 271 } 272 273 274 275 276 /*************************************** 277 * Legacy Compression 278 * *************************************/ 279 280 /* unoptimized version; solves endianess & alignment issues */ 281 static void LZ4IO_writeLE32 (void* p, unsigned value32) 282 { 283 unsigned char* dstPtr = p; 284 dstPtr[0] = (unsigned char)value32; 285 dstPtr[1] = (unsigned char)(value32 >> 8); 286 dstPtr[2] = (unsigned char)(value32 >> 16); 287 dstPtr[3] = (unsigned char)(value32 >> 24); 288 } 289 290 /* LZ4IO_compressFilename_Legacy : 291 * This function is intentionally "hidden" (not published in .h) 292 * It generates compressed streams using the old 'legacy' format */ 293 int LZ4IO_compressFilename_Legacy(char* input_filename, char* output_filename, int compressionlevel) 294 { 295 int (*compressionFunction)(const char*, char*, int); 296 unsigned long long filesize = 0; 297 unsigned long long compressedfilesize = MAGICNUMBER_SIZE; 298 char* in_buff; 299 char* out_buff; 300 FILE* finput; 301 FILE* foutput; 302 clock_t start, end; 303 size_t sizeCheck; 304 305 306 /* Init */ 307 start = clock(); 308 if (compressionlevel < 3) compressionFunction = LZ4_compress; else compressionFunction = LZ4_compressHC; 309 310 get_fileHandle(input_filename, output_filename, &finput, &foutput); 311 if ((displayLevel==2) && (compressionlevel==1)) displayLevel=3; 312 313 /* Allocate Memory */ 314 in_buff = (char*)malloc(LEGACY_BLOCKSIZE); 315 out_buff = (char*)malloc(LZ4_compressBound(LEGACY_BLOCKSIZE)); 316 if (!in_buff || !out_buff) EXM_THROW(21, "Allocation error : not enough memory"); 317 318 /* Write Archive Header */ 319 LZ4IO_writeLE32(out_buff, LEGACY_MAGICNUMBER); 320 sizeCheck = fwrite(out_buff, 1, MAGICNUMBER_SIZE, foutput); 321 if (sizeCheck!=MAGICNUMBER_SIZE) EXM_THROW(22, "Write error : cannot write header"); 322 323 /* Main Loop */ 324 while (1) 325 { 326 unsigned int outSize; 327 /* Read Block */ 328 int inSize = (int) fread(in_buff, (size_t)1, (size_t)LEGACY_BLOCKSIZE, finput); 329 if( inSize<=0 ) break; 330 filesize += inSize; 331 332 /* Compress Block */ 333 outSize = compressionFunction(in_buff, out_buff+4, inSize); 334 compressedfilesize += outSize+4; 335 DISPLAYUPDATE(3, "\rRead : %i MB ==> %.2f%% ", (int)(filesize>>20), (double)compressedfilesize/filesize*100); 336 337 /* Write Block */ 338 LZ4IO_writeLE32(out_buff, outSize); 339 sizeCheck = fwrite(out_buff, 1, outSize+4, foutput); 340 if (sizeCheck!=(size_t)(outSize+4)) EXM_THROW(23, "Write error : cannot write compressed block"); 341 } 342 343 /* Status */ 344 end = clock(); 345 DISPLAYLEVEL(2, "\r%79s\r", ""); 346 DISPLAYLEVEL(2,"Compressed %llu bytes into %llu bytes ==> %.2f%%\n", 347 (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100); 348 { 349 double seconds = (double)(end - start)/CLOCKS_PER_SEC; 350 DISPLAYLEVEL(4,"Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024); 351 } 352 353 /* Close & Free */ 354 free(in_buff); 355 free(out_buff); 356 fclose(finput); 357 fclose(foutput); 358 359 return 0; 360 } 361 362 363 /*********************************************** 364 * Compression using Frame format 365 * ********************************************/ 366 367 int LZ4IO_compressFilename(char* input_filename, char* output_filename, int compressionLevel) 368 { 369 unsigned long long filesize = 0; 370 unsigned long long compressedfilesize = 0; 371 char* in_buff; 372 char* out_buff; 373 FILE* finput; 374 FILE* foutput; 375 clock_t start, end; 376 int blockSize; 377 size_t sizeCheck, headerSize, readSize, outBuffSize; 378 LZ4F_compressionContext_t ctx; 379 LZ4F_errorCode_t errorCode; 380 LZ4F_preferences_t prefs = {0}; 381 382 383 /* Init */ 384 start = clock(); 385 if ((displayLevel==2) && (compressionLevel>=3)) displayLevel=3; 386 errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); 387 if (LZ4F_isError(errorCode)) EXM_THROW(30, "Allocation error : can't create LZ4F context : %s", LZ4F_getErrorName(errorCode)); 388 get_fileHandle(input_filename, output_filename, &finput, &foutput); 389 blockSize = LZ4S_GetBlockSize_FromBlockId (globalBlockSizeId); 390 391 /* Set compression parameters */ 392 prefs.autoFlush = 1; 393 prefs.compressionLevel = compressionLevel; 394 prefs.frameInfo.blockMode = blockIndependence; 395 prefs.frameInfo.blockSizeID = globalBlockSizeId; 396 prefs.frameInfo.contentChecksumFlag = streamChecksum; 397 398 /* Allocate Memory */ 399 in_buff = (char*)malloc(blockSize); 400 outBuffSize = LZ4F_compressBound(blockSize, &prefs); 401 out_buff = (char*)malloc(outBuffSize); 402 if (!in_buff || !out_buff) EXM_THROW(31, "Allocation error : not enough memory"); 403 404 /* Write Archive Header */ 405 headerSize = LZ4F_compressBegin(ctx, out_buff, outBuffSize, &prefs); 406 if (LZ4F_isError(headerSize)) EXM_THROW(32, "File header generation failed : %s", LZ4F_getErrorName(headerSize)); 407 sizeCheck = fwrite(out_buff, 1, headerSize, foutput); 408 if (sizeCheck!=headerSize) EXM_THROW(33, "Write error : cannot write header"); 409 compressedfilesize += headerSize; 410 411 /* read first block */ 412 readSize = fread(in_buff, (size_t)1, (size_t)blockSize, finput); 413 filesize += readSize; 414 415 /* Main Loop */ 416 while (readSize>0) 417 { 418 size_t outSize; 419 420 /* Compress Block */ 421 outSize = LZ4F_compressUpdate(ctx, out_buff, outBuffSize, in_buff, readSize, NULL); 422 if (LZ4F_isError(outSize)) EXM_THROW(34, "Compression failed : %s", LZ4F_getErrorName(outSize)); 423 compressedfilesize += outSize; 424 DISPLAYUPDATE(3, "\rRead : %i MB ==> %.2f%% ", (int)(filesize>>20), (double)compressedfilesize/filesize*100); 425 426 /* Write Block */ 427 sizeCheck = fwrite(out_buff, 1, outSize, foutput); 428 if (sizeCheck!=outSize) EXM_THROW(35, "Write error : cannot write compressed block"); 429 430 /* Read next block */ 431 readSize = fread(in_buff, (size_t)1, (size_t)blockSize, finput); 432 filesize += readSize; 433 } 434 435 /* End of Stream mark */ 436 headerSize = LZ4F_compressEnd(ctx, out_buff, outBuffSize, NULL); 437 if (LZ4F_isError(headerSize)) EXM_THROW(36, "End of file generation failed : %s", LZ4F_getErrorName(headerSize)); 438 439 sizeCheck = fwrite(out_buff, 1, headerSize, foutput); 440 if (sizeCheck!=headerSize) EXM_THROW(37, "Write error : cannot write end of stream"); 441 compressedfilesize += headerSize; 442 443 /* Close & Free */ 444 free(in_buff); 445 free(out_buff); 446 fclose(finput); 447 fclose(foutput); 448 errorCode = LZ4F_freeCompressionContext(ctx); 449 if (LZ4F_isError(errorCode)) EXM_THROW(38, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode)); 450 451 /* Final Status */ 452 end = clock(); 453 DISPLAYLEVEL(2, "\r%79s\r", ""); 454 DISPLAYLEVEL(2, "Compressed %llu bytes into %llu bytes ==> %.2f%%\n", 455 (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100); 456 { 457 double seconds = (double)(end - start)/CLOCKS_PER_SEC; 458 DISPLAYLEVEL(4, "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024); 459 } 460 461 return 0; 462 } 463 464 465 /* ********************************************************************* */ 466 /* ********************** LZ4 File / Stream decoding ******************* */ 467 /* ********************************************************************* */ 468 469 static unsigned LZ4IO_readLE32 (const void* s) 470 { 471 const unsigned char* srcPtr = s; 472 unsigned value32 = srcPtr[0]; 473 value32 += (srcPtr[1]<<8); 474 value32 += (srcPtr[2]<<16); 475 value32 += (srcPtr[3]<<24); 476 return value32; 477 } 478 479 static unsigned long long decodeLegacyStream(FILE* finput, FILE* foutput) 480 { 481 unsigned long long filesize = 0; 482 char* in_buff; 483 char* out_buff; 484 485 /* Allocate Memory */ 486 in_buff = (char*)malloc(LZ4_compressBound(LEGACY_BLOCKSIZE)); 487 out_buff = (char*)malloc(LEGACY_BLOCKSIZE); 488 if (!in_buff || !out_buff) EXM_THROW(51, "Allocation error : not enough memory"); 489 490 /* Main Loop */ 491 while (1) 492 { 493 int decodeSize; 494 size_t sizeCheck; 495 unsigned int blockSize; 496 497 /* Block Size */ 498 sizeCheck = fread(in_buff, 1, 4, finput); 499 if (sizeCheck==0) break; /* Nothing to read : file read is completed */ 500 blockSize = LZ4IO_readLE32(in_buff); /* Convert to Little Endian */ 501 if (blockSize > LZ4_COMPRESSBOUND(LEGACY_BLOCKSIZE)) 502 { /* Cannot read next block : maybe new stream ? */ 503 fseek(finput, -4, SEEK_CUR); 504 break; 505 } 506 507 /* Read Block */ 508 sizeCheck = fread(in_buff, 1, blockSize, finput); 509 510 /* Decode Block */ 511 decodeSize = LZ4_decompress_safe(in_buff, out_buff, blockSize, LEGACY_BLOCKSIZE); 512 if (decodeSize < 0) EXM_THROW(52, "Decoding Failed ! Corrupted input detected !"); 513 filesize += decodeSize; 514 515 /* Write Block */ 516 sizeCheck = fwrite(out_buff, 1, decodeSize, foutput); 517 if (sizeCheck != (size_t)decodeSize) EXM_THROW(53, "Write error : cannot write decoded block into output\n"); 518 } 519 520 /* Free */ 521 free(in_buff); 522 free(out_buff); 523 524 return filesize; 525 } 526 527 528 static unsigned long long decodeLZ4S(FILE* finput, FILE* foutput) 529 { 530 unsigned long long filesize = 0; 531 char* inBuff; 532 char* outBuff; 533 # define HEADERMAX 20 534 char headerBuff[HEADERMAX]; 535 size_t sizeCheck, nextToRead, outBuffSize, inBuffSize; 536 LZ4F_decompressionContext_t ctx; 537 LZ4F_errorCode_t errorCode; 538 LZ4F_frameInfo_t frameInfo; 539 540 /* init */ 541 errorCode = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); 542 if (LZ4F_isError(errorCode)) EXM_THROW(60, "Allocation error : can't create context : %s", LZ4F_getErrorName(errorCode)); 543 LZ4IO_writeLE32(headerBuff, LZ4S_MAGICNUMBER); /* regenerated here, as it was already read from finput */ 544 545 /* Decode stream descriptor */ 546 outBuffSize = 0; inBuffSize = 0; sizeCheck = MAGICNUMBER_SIZE; 547 nextToRead = LZ4F_decompress(ctx, NULL, &outBuffSize, headerBuff, &sizeCheck, NULL); 548 if (LZ4F_isError(nextToRead)) EXM_THROW(61, "Decompression error : %s", LZ4F_getErrorName(nextToRead)); 549 if (nextToRead > HEADERMAX) EXM_THROW(62, "Header too large (%i>%i)", (int)nextToRead, HEADERMAX); 550 sizeCheck = fread(headerBuff, 1, nextToRead, finput); 551 if (sizeCheck!=nextToRead) EXM_THROW(63, "Read error "); 552 nextToRead = LZ4F_decompress(ctx, NULL, &outBuffSize, headerBuff, &sizeCheck, NULL); 553 errorCode = LZ4F_getFrameInfo(ctx, &frameInfo, NULL, &inBuffSize); 554 if (LZ4F_isError(errorCode)) EXM_THROW(64, "can't decode frame header : %s", LZ4F_getErrorName(errorCode)); 555 556 /* Allocate Memory */ 557 outBuffSize = LZ4IO_setBlockSizeID(frameInfo.blockSizeID); 558 inBuffSize = outBuffSize + 4; 559 inBuff = (char*)malloc(inBuffSize); 560 outBuff = (char*)malloc(outBuffSize); 561 if (!inBuff || !outBuff) EXM_THROW(65, "Allocation error : not enough memory"); 562 563 /* Main Loop */ 564 while (nextToRead != 0) 565 { 566 size_t decodedBytes = outBuffSize; 567 568 /* Read Block */ 569 sizeCheck = fread(inBuff, 1, nextToRead, finput); 570 if (sizeCheck!=nextToRead) EXM_THROW(66, "Read error "); 571 572 /* Decode Block */ 573 errorCode = LZ4F_decompress(ctx, outBuff, &decodedBytes, inBuff, &sizeCheck, NULL); 574 if (LZ4F_isError(errorCode)) EXM_THROW(67, "Decompression error : %s", LZ4F_getErrorName(errorCode)); 575 if (sizeCheck!=nextToRead) EXM_THROW(67, "Synchronization error"); 576 nextToRead = errorCode; 577 filesize += decodedBytes; 578 579 /* Write Block */ 580 sizeCheck = fwrite(outBuff, 1, decodedBytes, foutput); 581 if (sizeCheck != decodedBytes) EXM_THROW(68, "Write error : cannot write decoded block\n"); 582 } 583 584 /* Free */ 585 free(inBuff); 586 free(outBuff); 587 errorCode = LZ4F_freeDecompressionContext(ctx); 588 if (LZ4F_isError(errorCode)) EXM_THROW(69, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode)); 589 590 return filesize; 591 } 592 593 594 #define ENDOFSTREAM ((unsigned long long)-1) 595 static unsigned long long selectDecoder( FILE* finput, FILE* foutput) 596 { 597 unsigned char U32store[MAGICNUMBER_SIZE]; 598 unsigned magicNumber, size; 599 int errorNb; 600 size_t nbReadBytes; 601 602 /* Check Archive Header */ 603 nbReadBytes = fread(U32store, 1, MAGICNUMBER_SIZE, finput); 604 if (nbReadBytes==0) return ENDOFSTREAM; /* EOF */ 605 if (nbReadBytes != MAGICNUMBER_SIZE) EXM_THROW(40, "Unrecognized header : Magic Number unreadable"); 606 magicNumber = LZ4IO_readLE32(U32store); /* Little Endian format */ 607 if (LZ4S_isSkippableMagicNumber(magicNumber)) magicNumber = LZ4S_SKIPPABLE0; /* fold skippable magic numbers */ 608 609 switch(magicNumber) 610 { 611 case LZ4S_MAGICNUMBER: 612 return DEFAULT_DECOMPRESSOR(finput, foutput); 613 case LEGACY_MAGICNUMBER: 614 DISPLAYLEVEL(4, "Detected : Legacy format \n"); 615 return decodeLegacyStream(finput, foutput); 616 case LZ4S_SKIPPABLE0: 617 DISPLAYLEVEL(4, "Skipping detected skippable area \n"); 618 nbReadBytes = fread(U32store, 1, 4, finput); 619 if (nbReadBytes != 4) EXM_THROW(42, "Stream error : skippable size unreadable"); 620 size = LZ4IO_readLE32(U32store); /* Little Endian format */ 621 errorNb = fseek(finput, size, SEEK_CUR); 622 if (errorNb != 0) EXM_THROW(43, "Stream error : cannot skip skippable area"); 623 return selectDecoder(finput, foutput); 624 EXTENDED_FORMAT; 625 default: 626 if (ftell(finput) == MAGICNUMBER_SIZE) EXM_THROW(44,"Unrecognized header : file cannot be decoded"); /* Wrong magic number at the beginning of 1st stream */ 627 DISPLAYLEVEL(2, "Stream followed by unrecognized data\n"); 628 return ENDOFSTREAM; 629 } 630 } 631 632 633 int LZ4IO_decompressFilename(char* input_filename, char* output_filename) 634 { 635 unsigned long long filesize = 0, decodedSize=0; 636 FILE* finput; 637 FILE* foutput; 638 clock_t start, end; 639 640 641 /* Init */ 642 start = clock(); 643 get_fileHandle(input_filename, output_filename, &finput, &foutput); 644 645 /* Loop over multiple streams */ 646 do 647 { 648 decodedSize = selectDecoder(finput, foutput); 649 if (decodedSize != ENDOFSTREAM) 650 filesize += decodedSize; 651 } while (decodedSize != ENDOFSTREAM); 652 653 /* Final Status */ 654 end = clock(); 655 DISPLAYLEVEL(2, "\r%79s\r", ""); 656 DISPLAYLEVEL(2, "Successfully decoded %llu bytes \n", filesize); 657 { 658 double seconds = (double)(end - start)/CLOCKS_PER_SEC; 659 DISPLAYLEVEL(4, "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024); 660 } 661 662 /* Close */ 663 fclose(finput); 664 fclose(foutput); 665 666 /* Error status = OK */ 667 return 0; 668 } 669 670