kusano 7d535a
/*
kusano 7d535a
  LZ4io.c - LZ4 File/Stream Interface
kusano 7d535a
  Copyright (C) Yann Collet 2011-2015
kusano 7d535a
kusano 7d535a
  GPL v2 License
kusano 7d535a
kusano 7d535a
  This program is free software; you can redistribute it and/or modify
kusano 7d535a
  it under the terms of the GNU General Public License as published by
kusano 7d535a
  the Free Software Foundation; either version 2 of the License, or
kusano 7d535a
  (at your option) any later version.
kusano 7d535a
kusano 7d535a
  This program is distributed in the hope that it will be useful,
kusano 7d535a
  but WITHOUT ANY WARRANTY; without even the implied warranty of
kusano 7d535a
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
kusano 7d535a
  GNU General Public License for more details.
kusano 7d535a
kusano 7d535a
  You should have received a copy of the GNU General Public License along
kusano 7d535a
  with this program; if not, write to the Free Software Foundation, Inc.,
kusano 7d535a
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
kusano 7d535a
kusano 7d535a
  You can contact the author at :
kusano 7d535a
  - LZ4 source repository : https://github.com/Cyan4973/lz4
kusano 7d535a
  - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
kusano 7d535a
*/
kusano 7d535a
/*
kusano 7d535a
  Note : this is stand-alone program.
kusano 7d535a
  It is not part of LZ4 compression library, it is a user code of the LZ4 library.
kusano 7d535a
  - The license of LZ4 library is BSD.
kusano 7d535a
  - The license of xxHash library is BSD.
kusano 7d535a
  - The license of this source file is GPLv2.
kusano 7d535a
*/
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Compiler Options
kusano 7d535a
**************************************/
kusano 7d535a
#ifdef _MSC_VER    /* Visual Studio */
kusano 7d535a
#  define _CRT_SECURE_NO_WARNINGS
kusano 7d535a
#  define _CRT_SECURE_NO_DEPRECATE     /* VS2005 */
kusano 7d535a
#  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */
kusano 7d535a
#endif
kusano 7d535a
kusano 7d535a
#define _LARGE_FILES           /* Large file support on 32-bits AIX */
kusano 7d535a
#define _FILE_OFFSET_BITS 64   /* Large file support on 32-bits unix */
kusano 7d535a
kusano 7d535a
kusano 7d535a
/*****************************
kusano 7d535a
*  Includes
kusano 7d535a
*****************************/
kusano 7d535a
#include <stdio.h>     /* fprintf, fopen, fread, stdin, stdout, fflush, getchar */</stdio.h>
kusano 7d535a
#include <stdlib.h>    /* malloc, free */</stdlib.h>
kusano 7d535a
#include <string.h>    /* strcmp, strlen */</string.h>
kusano 7d535a
#include <time.h>      /* clock */</time.h>
kusano 7d535a
#include <sys types.h=""> /* stat64 */</sys>
kusano 7d535a
#include <sys stat.h="">  /* stat64 */</sys>
kusano 7d535a
#include "lz4io.h"
kusano 7d535a
#include "lz4.h"       /* still required for legacy format */
kusano 7d535a
#include "lz4hc.h"     /* still required for legacy format */
kusano 7d535a
#include "lz4frame.h"
kusano 7d535a
kusano 7d535a
kusano 7d535a
/******************************
kusano 7d535a
*  OS-specific Includes
kusano 7d535a
******************************/
kusano 7d535a
#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32)
kusano 7d535a
#  include <fcntl.h>   /* _O_BINARY */</fcntl.h>
kusano 7d535a
#  include <io.h>      /* _setmode, _fileno, _get_osfhandle */</io.h>
kusano 7d535a
#  if !defined(__DJGPP__)
kusano 7d535a
#    define SET_BINARY_MODE(file) { int unused=_setmode(_fileno(file), _O_BINARY); (void)unused; }
kusano 7d535a
#    include <windows.h> /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */</windows.h>
kusano 7d535a
#    define SET_SPARSE_FILE_MODE(file) { DWORD dw; DeviceIoControl((HANDLE) _get_osfhandle(_fileno(file)), FSCTL_SET_SPARSE, 0, 0, 0, 0, &dw, 0); }
kusano 7d535a
#    if defined(_MSC_VER) && (_MSC_VER >= 1400)  /* Avoid MSVC fseek()'s 2GiB barrier */
kusano 7d535a
#      define fseek _fseeki64
kusano 7d535a
#    endif
kusano 7d535a
#  else
kusano 7d535a
#    define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
kusano 7d535a
#    define SET_SPARSE_FILE_MODE(file)
kusano 7d535a
#  endif
kusano 7d535a
#else
kusano 7d535a
#  define SET_BINARY_MODE(file)
kusano 7d535a
#  define SET_SPARSE_FILE_MODE(file)
kusano 7d535a
#endif
kusano 7d535a
kusano 7d535a
#if !defined(S_ISREG)
kusano 7d535a
#  define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
kusano 7d535a
#endif
kusano 7d535a
kusano 7d535a
kusano 7d535a
/*****************************
kusano 7d535a
*  Constants
kusano 7d535a
*****************************/
kusano 7d535a
#define KB *(1 <<10)
kusano 7d535a
#define MB *(1 <<20)
kusano 7d535a
#define GB *(1U<<30)
kusano 7d535a
kusano 7d535a
#define _1BIT  0x01
kusano 7d535a
#define _2BITS 0x03
kusano 7d535a
#define _3BITS 0x07
kusano 7d535a
#define _4BITS 0x0F
kusano 7d535a
#define _8BITS 0xFF
kusano 7d535a
kusano 7d535a
#define MAGICNUMBER_SIZE    4
kusano 7d535a
#define LZ4IO_MAGICNUMBER   0x184D2204
kusano 7d535a
#define LZ4IO_SKIPPABLE0    0x184D2A50
kusano 7d535a
#define LZ4IO_SKIPPABLEMASK 0xFFFFFFF0
kusano 7d535a
#define LEGACY_MAGICNUMBER  0x184C2102
kusano 7d535a
kusano 7d535a
#define CACHELINE 64
kusano 7d535a
#define LEGACY_BLOCKSIZE   (8 MB)
kusano 7d535a
#define MIN_STREAM_BUFSIZE (192 KB)
kusano 7d535a
#define LZ4IO_BLOCKSIZEID_DEFAULT 7
kusano 7d535a
kusano 7d535a
#define sizeT sizeof(size_t)
kusano 7d535a
#define maskT (sizeT - 1)
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Macros
kusano 7d535a
**************************************/
kusano 7d535a
#define DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
kusano 7d535a
#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
kusano 7d535a
static int g_displayLevel = 0;   /* 0 : no display  ; 1: errors  ; 2 : + result + interaction + warnings ; 3 : + progression; 4 : + information */
kusano 7d535a
kusano 7d535a
#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
kusano 7d535a
            if ((LZ4IO_GetMilliSpan(g_time) > refreshRate) || (g_displayLevel>=4)) \
kusano 7d535a
            { g_time = clock(); DISPLAY(__VA_ARGS__); \
kusano 7d535a
            if (g_displayLevel>=4) fflush(stdout); } }
kusano 7d535a
static const unsigned refreshRate = 150;
kusano 7d535a
static clock_t g_time = 0;
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Local Parameters
kusano 7d535a
**************************************/
kusano 7d535a
static int g_overwrite = 1;
kusano 7d535a
static int g_blockSizeId = LZ4IO_BLOCKSIZEID_DEFAULT;
kusano 7d535a
static int g_blockChecksum = 0;
kusano 7d535a
static int g_streamChecksum = 1;
kusano 7d535a
static int g_blockIndependence = 1;
kusano 7d535a
static int g_sparseFileSupport = 1;
kusano 7d535a
static int g_contentSizeFlag = 0;
kusano 7d535a
kusano 7d535a
static const int minBlockSizeID = 4;
kusano 7d535a
static const int maxBlockSizeID = 7;
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Exceptions
kusano 7d535a
***************************************/
kusano 7d535a
#define DEBUG 0
kusano 7d535a
#define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
kusano 7d535a
#define EXM_THROW(error, ...)                                             \
kusano 7d535a
{                                                                         \
kusano 7d535a
    DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
kusano 7d535a
    DISPLAYLEVEL(1, "Error %i : ", error);                                \
kusano 7d535a
    DISPLAYLEVEL(1, __VA_ARGS__);                                         \
kusano 7d535a
    DISPLAYLEVEL(1, "\n");                                                \
kusano 7d535a
    exit(error);                                                          \
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Version modifiers
kusano 7d535a
**************************************/
kusano 7d535a
#define EXTENDED_ARGUMENTS
kusano 7d535a
#define EXTENDED_HELP
kusano 7d535a
#define EXTENDED_FORMAT
kusano 7d535a
#define DEFAULT_DECOMPRESSOR LZ4IO_decompressLZ4F
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* ************************************************** */
kusano 7d535a
/* ****************** Parameters ******************** */
kusano 7d535a
/* ************************************************** */
kusano 7d535a
kusano 7d535a
/* Default setting : overwrite = 1; return : overwrite mode (0/1) */
kusano 7d535a
int LZ4IO_setOverwrite(int yes)
kusano 7d535a
{
kusano 7d535a
   g_overwrite = (yes!=0);
kusano 7d535a
   return g_overwrite;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/* blockSizeID : valid values : 4-5-6-7 */
kusano 7d535a
int LZ4IO_setBlockSizeID(int bsid)
kusano 7d535a
{
kusano 7d535a
    static const int blockSizeTable[] = { 64 KB, 256 KB, 1 MB, 4 MB };
kusano 7d535a
    if ((bsid < minBlockSizeID) || (bsid > maxBlockSizeID)) return -1;
kusano 7d535a
    g_blockSizeId = bsid;
kusano 7d535a
    return blockSizeTable[g_blockSizeId-minBlockSizeID];
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
int LZ4IO_setBlockMode(LZ4IO_blockMode_t blockMode)
kusano 7d535a
{
kusano 7d535a
    g_blockIndependence = (blockMode == LZ4IO_blockIndependent);
kusano 7d535a
    return g_blockIndependence;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/* Default setting : no checksum */
kusano 7d535a
int LZ4IO_setBlockChecksumMode(int xxhash)
kusano 7d535a
{
kusano 7d535a
    g_blockChecksum = (xxhash != 0);
kusano 7d535a
    return g_blockChecksum;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/* Default setting : checksum enabled */
kusano 7d535a
int LZ4IO_setStreamChecksumMode(int xxhash)
kusano 7d535a
{
kusano 7d535a
    g_streamChecksum = (xxhash != 0);
kusano 7d535a
    return g_streamChecksum;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/* Default setting : 0 (no notification) */
kusano 7d535a
int LZ4IO_setNotificationLevel(int level)
kusano 7d535a
{
kusano 7d535a
    g_displayLevel = level;
kusano 7d535a
    return g_displayLevel;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/* Default setting : 0 (disabled) */
kusano 7d535a
int LZ4IO_setSparseFile(int enable)
kusano 7d535a
{
kusano 7d535a
    g_sparseFileSupport = (enable!=0);
kusano 7d535a
    return g_sparseFileSupport;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/* Default setting : 0 (disabled) */
kusano 7d535a
int LZ4IO_setContentSize(int enable)
kusano 7d535a
{
kusano 7d535a
    g_contentSizeFlag = (enable!=0);
kusano 7d535a
    return g_contentSizeFlag;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static unsigned LZ4IO_GetMilliSpan(clock_t nPrevious)
kusano 7d535a
{
kusano 7d535a
    clock_t nCurrent = clock();
kusano 7d535a
    unsigned nSpan = (unsigned)(((nCurrent - nPrevious) * 1000) / CLOCKS_PER_SEC);
kusano 7d535a
    return nSpan;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static unsigned long long LZ4IO_GetFileSize(const char* infilename)
kusano 7d535a
{
kusano 7d535a
    int r;
kusano 7d535a
#if defined(_MSC_VER)
kusano 7d535a
    struct _stat64 statbuf;
kusano 7d535a
    r = _stat64(infilename, &statbuf);
kusano 7d535a
#else
kusano 7d535a
    struct stat statbuf;
kusano 7d535a
    r = stat(infilename, &statbuf);
kusano 7d535a
#endif
kusano 7d535a
    if (r || !S_ISREG(statbuf.st_mode)) return 0;   /* failure, or is not a regular file */
kusano 7d535a
    return (unsigned long long)statbuf.st_size;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* ************************************************************************ **
kusano 7d535a
** ********************** LZ4 File / Pipe compression ********************* **
kusano 7d535a
** ************************************************************************ */
kusano 7d535a
kusano 7d535a
static int LZ4IO_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }
kusano 7d535a
static int LZ4IO_isSkippableMagicNumber(unsigned int magic) { return (magic & LZ4IO_SKIPPABLEMASK) == LZ4IO_SKIPPABLE0; }
kusano 7d535a
kusano 7d535a
kusano 7d535a
static int LZ4IO_getFiles(const char* input_filename, const char* output_filename, FILE** pfinput, FILE** pfoutput)
kusano 7d535a
{
kusano 7d535a
kusano 7d535a
    if (!strcmp (input_filename, stdinmark))
kusano 7d535a
    {
kusano 7d535a
        DISPLAYLEVEL(4,"Using stdin for input\n");
kusano 7d535a
        *pfinput = stdin;
kusano 7d535a
        SET_BINARY_MODE(stdin);
kusano 7d535a
    }
kusano 7d535a
    else
kusano 7d535a
    {
kusano 7d535a
        *pfinput = fopen(input_filename, "rb");
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if ( *pfinput==0 )
kusano 7d535a
    {
kusano 7d535a
        DISPLAYLEVEL(1, "Unable to access file for processing: %s\n", input_filename);
kusano 7d535a
        return 1;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if (!strcmp (output_filename, stdoutmark))
kusano 7d535a
    {
kusano 7d535a
        DISPLAYLEVEL(4,"Using stdout for output\n");
kusano 7d535a
        *pfoutput = stdout;
kusano 7d535a
        SET_BINARY_MODE(stdout);
kusano 7d535a
        if (g_sparseFileSupport==1)
kusano 7d535a
        {
kusano 7d535a
            g_sparseFileSupport = 0;
kusano 7d535a
            DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
    else
kusano 7d535a
    {
kusano 7d535a
        /* Check if destination file already exists */
kusano 7d535a
        *pfoutput=0;
kusano 7d535a
        if (output_filename != nulmark) *pfoutput = fopen( output_filename, "rb" );
kusano 7d535a
        if (*pfoutput!=0)
kusano 7d535a
        {
kusano 7d535a
            fclose(*pfoutput);
kusano 7d535a
            if (!g_overwrite)
kusano 7d535a
            {
kusano 7d535a
                int ch = 'Y';
kusano 7d535a
                DISPLAYLEVEL(2, "Warning : %s already exists\n", output_filename);
kusano 7d535a
                if ((g_displayLevel <= 1) || (*pfinput == stdin))
kusano 7d535a
                    EXM_THROW(11, "Operation aborted : %s already exists", output_filename);   /* No interaction possible */
kusano 7d535a
                DISPLAYLEVEL(2, "Overwrite ? (Y/n) : ");
kusano 7d535a
                while((ch = getchar()) != '\n' && ch != EOF)   /* flush integrated */
kusano 7d535a
                if ((ch!='Y') && (ch!='y')) EXM_THROW(12, "No. Operation aborted : %s already exists", output_filename);
kusano 7d535a
            }
kusano 7d535a
        }
kusano 7d535a
        *pfoutput = fopen( output_filename, "wb" );
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if (*pfoutput==0) EXM_THROW(13, "Pb opening %s", output_filename);
kusano 7d535a
kusano 7d535a
    return 0;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
kusano 7d535a
/***************************************
kusano 7d535a
*   Legacy Compression
kusano 7d535a
***************************************/
kusano 7d535a
kusano 7d535a
/* unoptimized version; solves endianess & alignment issues */
kusano 7d535a
static void LZ4IO_writeLE32 (void* p, unsigned value32)
kusano 7d535a
{
kusano 7d535a
    unsigned char* dstPtr = (unsigned char*)p;
kusano 7d535a
    dstPtr[0] = (unsigned char)value32;
kusano 7d535a
    dstPtr[1] = (unsigned char)(value32 >> 8);
kusano 7d535a
    dstPtr[2] = (unsigned char)(value32 >> 16);
kusano 7d535a
    dstPtr[3] = (unsigned char)(value32 >> 24);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static int LZ4IO_LZ4_compress(const char* src, char* dst, int srcSize, int dstSize, int cLevel)
kusano 7d535a
{
kusano 7d535a
    (void)cLevel;
kusano 7d535a
    return LZ4_compress_fast(src, dst, srcSize, dstSize, 1);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/* LZ4IO_compressFilename_Legacy :
kusano 7d535a
 * This function is intentionally "hidden" (not published in .h)
kusano 7d535a
 * It generates compressed streams using the old 'legacy' format */
kusano 7d535a
int LZ4IO_compressFilename_Legacy(const char* input_filename, const char* output_filename, int compressionlevel)
kusano 7d535a
{
kusano 7d535a
    int (*compressionFunction)(const char* src, char* dst, int srcSize, int dstSize, int cLevel);
kusano 7d535a
    unsigned long long filesize = 0;
kusano 7d535a
    unsigned long long compressedfilesize = MAGICNUMBER_SIZE;
kusano 7d535a
    char* in_buff;
kusano 7d535a
    char* out_buff;
kusano 7d535a
    const int outBuffSize = LZ4_compressBound(LEGACY_BLOCKSIZE);
kusano 7d535a
    FILE* finput;
kusano 7d535a
    FILE* foutput;
kusano 7d535a
    clock_t start, end;
kusano 7d535a
    size_t sizeCheck;
kusano 7d535a
kusano 7d535a
kusano 7d535a
    /* Init */
kusano 7d535a
    start = clock();
kusano 7d535a
    if (compressionlevel < 3) compressionFunction = LZ4IO_LZ4_compress; else compressionFunction = LZ4_compress_HC;
kusano 7d535a
kusano 7d535a
    if (LZ4IO_getFiles(input_filename, output_filename, &finput, &foutput))
kusano 7d535a
        EXM_THROW(20, "File error");
kusano 7d535a
kusano 7d535a
    /* Allocate Memory */
kusano 7d535a
    in_buff = (char*)malloc(LEGACY_BLOCKSIZE);
kusano 7d535a
    out_buff = (char*)malloc(outBuffSize);
kusano 7d535a
    if (!in_buff || !out_buff) EXM_THROW(21, "Allocation error : not enough memory");
kusano 7d535a
kusano 7d535a
    /* Write Archive Header */
kusano 7d535a
    LZ4IO_writeLE32(out_buff, LEGACY_MAGICNUMBER);
kusano 7d535a
    sizeCheck = fwrite(out_buff, 1, MAGICNUMBER_SIZE, foutput);
kusano 7d535a
    if (sizeCheck!=MAGICNUMBER_SIZE) EXM_THROW(22, "Write error : cannot write header");
kusano 7d535a
kusano 7d535a
    /* Main Loop */
kusano 7d535a
    while (1)
kusano 7d535a
    {
kusano 7d535a
        unsigned int outSize;
kusano 7d535a
        /* Read Block */
kusano 7d535a
        int inSize = (int) fread(in_buff, (size_t)1, (size_t)LEGACY_BLOCKSIZE, finput);
kusano 7d535a
        if( inSize<=0 ) break;
kusano 7d535a
        filesize += inSize;
kusano 7d535a
kusano 7d535a
        /* Compress Block */
kusano 7d535a
        outSize = compressionFunction(in_buff, out_buff+4, inSize, outBuffSize, compressionlevel);
kusano 7d535a
        compressedfilesize += outSize+4;
kusano 7d535a
        DISPLAYUPDATE(2, "\rRead : %i MB  ==> %.2f%%   ", (int)(filesize>>20), (double)compressedfilesize/filesize*100);
kusano 7d535a
kusano 7d535a
        /* Write Block */
kusano 7d535a
        LZ4IO_writeLE32(out_buff, outSize);
kusano 7d535a
        sizeCheck = fwrite(out_buff, 1, outSize+4, foutput);
kusano 7d535a
        if (sizeCheck!=(size_t)(outSize+4)) EXM_THROW(23, "Write error : cannot write compressed block");
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* Status */
kusano 7d535a
    end = clock();
kusano 7d535a
    DISPLAYLEVEL(2, "\r%79s\r", "");
kusano 7d535a
    filesize += !filesize;   /* avoid divide by zero */
kusano 7d535a
    DISPLAYLEVEL(2,"Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
kusano 7d535a
        (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);
kusano 7d535a
    {
kusano 7d535a
        double seconds = (double)(end - start)/CLOCKS_PER_SEC;
kusano 7d535a
        DISPLAYLEVEL(4,"Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* Close & Free */
kusano 7d535a
    free(in_buff);
kusano 7d535a
    free(out_buff);
kusano 7d535a
    fclose(finput);
kusano 7d535a
    fclose(foutput);
kusano 7d535a
kusano 7d535a
    return 0;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/*********************************************
kusano 7d535a
*  Compression using Frame format
kusano 7d535a
*********************************************/
kusano 7d535a
kusano 7d535a
typedef struct {
kusano 7d535a
    void*  srcBuffer;
kusano 7d535a
    size_t srcBufferSize;
kusano 7d535a
    void*  dstBuffer;
kusano 7d535a
    size_t dstBufferSize;
kusano 7d535a
    LZ4F_compressionContext_t ctx;
kusano 7d535a
} cRess_t;
kusano 7d535a
kusano 7d535a
static cRess_t LZ4IO_createCResources(void)
kusano 7d535a
{
kusano 7d535a
    const size_t blockSize = (size_t)LZ4IO_GetBlockSize_FromBlockId (g_blockSizeId);
kusano 7d535a
    cRess_t ress;
kusano 7d535a
    LZ4F_errorCode_t errorCode;
kusano 7d535a
kusano 7d535a
    errorCode = LZ4F_createCompressionContext(&(ress.ctx), LZ4F_VERSION);
kusano 7d535a
    if (LZ4F_isError(errorCode)) EXM_THROW(30, "Allocation error : can't create LZ4F context : %s", LZ4F_getErrorName(errorCode));
kusano 7d535a
kusano 7d535a
    /* Allocate Memory */
kusano 7d535a
    ress.srcBuffer = malloc(blockSize);
kusano 7d535a
    ress.srcBufferSize = blockSize;
kusano 7d535a
    ress.dstBufferSize = LZ4F_compressFrameBound(blockSize, NULL);   /* cover worst case */
kusano 7d535a
    ress.dstBuffer = malloc(ress.dstBufferSize);
kusano 7d535a
    if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(31, "Allocation error : not enough memory");
kusano 7d535a
kusano 7d535a
    return ress;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static void LZ4IO_freeCResources(cRess_t ress)
kusano 7d535a
{
kusano 7d535a
    LZ4F_errorCode_t errorCode;
kusano 7d535a
    free(ress.srcBuffer);
kusano 7d535a
    free(ress.dstBuffer);
kusano 7d535a
    errorCode = LZ4F_freeCompressionContext(ress.ctx);
kusano 7d535a
    if (LZ4F_isError(errorCode)) EXM_THROW(38, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode));
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
/*
kusano 7d535a
 * LZ4IO_compressFilename_extRess()
kusano 7d535a
 * result : 0 : compression completed correctly
kusano 7d535a
 *          1 : missing or pb opening srcFileName
kusano 7d535a
 */
kusano 7d535a
static int LZ4IO_compressFilename_extRess(cRess_t ress, const char* srcFileName, const char* dstFileName, int compressionLevel)
kusano 7d535a
{
kusano 7d535a
    unsigned long long filesize = 0;
kusano 7d535a
    unsigned long long compressedfilesize = 0;
kusano 7d535a
    FILE* srcFile;
kusano 7d535a
    FILE* dstFile;
kusano 7d535a
    void* const srcBuffer = ress.srcBuffer;
kusano 7d535a
    void* const dstBuffer = ress.dstBuffer;
kusano 7d535a
    const size_t dstBufferSize = ress.dstBufferSize;
kusano 7d535a
    const size_t blockSize = (size_t)LZ4IO_GetBlockSize_FromBlockId (g_blockSizeId);
kusano 7d535a
    size_t sizeCheck, headerSize, readSize;
kusano 7d535a
    LZ4F_compressionContext_t ctx = ress.ctx;   /* just a pointer */
kusano 7d535a
    LZ4F_preferences_t prefs;
kusano 7d535a
kusano 7d535a
kusano 7d535a
    /* Init */
kusano 7d535a
    memset(&prefs, 0, sizeof(prefs));
kusano 7d535a
kusano 7d535a
    /* File check */
kusano 7d535a
    if (LZ4IO_getFiles(srcFileName, dstFileName, &srcFile, &dstFile)) return 1;
kusano 7d535a
kusano 7d535a
    /* Set compression parameters */
kusano 7d535a
    prefs.autoFlush = 1;
kusano 7d535a
    prefs.compressionLevel = compressionLevel;
kusano 7d535a
    prefs.frameInfo.blockMode = (LZ4F_blockMode_t)g_blockIndependence;
kusano 7d535a
    prefs.frameInfo.blockSizeID = (LZ4F_blockSizeID_t)g_blockSizeId;
kusano 7d535a
    prefs.frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)g_streamChecksum;
kusano 7d535a
    if (g_contentSizeFlag)
kusano 7d535a
    {
kusano 7d535a
      unsigned long long fileSize = LZ4IO_GetFileSize(srcFileName);
kusano 7d535a
      prefs.frameInfo.contentSize = fileSize;   /* == 0 if input == stdin */
kusano 7d535a
      if (fileSize==0)
kusano 7d535a
          DISPLAYLEVEL(3, "Warning : cannot determine uncompressed frame content size \n");
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* read first block */
kusano 7d535a
    readSize  = fread(srcBuffer, (size_t)1, blockSize, srcFile);
kusano 7d535a
    filesize += readSize;
kusano 7d535a
kusano 7d535a
    /* single-block file */
kusano 7d535a
    if (readSize < blockSize)
kusano 7d535a
    {
kusano 7d535a
        /* Compress in single pass */
kusano 7d535a
        size_t cSize = LZ4F_compressFrame(dstBuffer, dstBufferSize, srcBuffer, readSize, &prefs);
kusano 7d535a
        if (LZ4F_isError(cSize)) EXM_THROW(34, "Compression failed : %s", LZ4F_getErrorName(cSize));
kusano 7d535a
        compressedfilesize += cSize;
kusano 7d535a
        DISPLAYUPDATE(2, "\rRead : %u MB   ==> %.2f%%   ",
kusano 7d535a
                      (unsigned)(filesize>>20), (double)compressedfilesize/(filesize+!filesize)*100);   /* avoid division by zero */
kusano 7d535a
kusano 7d535a
        /* Write Block */
kusano 7d535a
        sizeCheck = fwrite(dstBuffer, 1, cSize, dstFile);
kusano 7d535a
        if (sizeCheck!=cSize) EXM_THROW(35, "Write error : cannot write compressed block");
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    else
kusano 7d535a
kusano 7d535a
    /* multiple-blocks file */
kusano 7d535a
    {
kusano 7d535a
        /* Write Archive Header */
kusano 7d535a
        headerSize = LZ4F_compressBegin(ctx, dstBuffer, dstBufferSize, &prefs);
kusano 7d535a
        if (LZ4F_isError(headerSize)) EXM_THROW(32, "File header generation failed : %s", LZ4F_getErrorName(headerSize));
kusano 7d535a
        sizeCheck = fwrite(dstBuffer, 1, headerSize, dstFile);
kusano 7d535a
        if (sizeCheck!=headerSize) EXM_THROW(33, "Write error : cannot write header");
kusano 7d535a
        compressedfilesize += headerSize;
kusano 7d535a
kusano 7d535a
        /* Main Loop */
kusano 7d535a
        while (readSize>0)
kusano 7d535a
        {
kusano 7d535a
            size_t outSize;
kusano 7d535a
kusano 7d535a
            /* Compress Block */
kusano 7d535a
            outSize = LZ4F_compressUpdate(ctx, dstBuffer, dstBufferSize, srcBuffer, readSize, NULL);
kusano 7d535a
            if (LZ4F_isError(outSize)) EXM_THROW(34, "Compression failed : %s", LZ4F_getErrorName(outSize));
kusano 7d535a
            compressedfilesize += outSize;
kusano 7d535a
            DISPLAYUPDATE(2, "\rRead : %u MB   ==> %.2f%%   ", (unsigned)(filesize>>20), (double)compressedfilesize/filesize*100);
kusano 7d535a
kusano 7d535a
            /* Write Block */
kusano 7d535a
            sizeCheck = fwrite(dstBuffer, 1, outSize, dstFile);
kusano 7d535a
            if (sizeCheck!=outSize) EXM_THROW(35, "Write error : cannot write compressed block");
kusano 7d535a
kusano 7d535a
            /* Read next block */
kusano 7d535a
            readSize  = fread(srcBuffer, (size_t)1, (size_t)blockSize, srcFile);
kusano 7d535a
            filesize += readSize;
kusano 7d535a
        }
kusano 7d535a
kusano 7d535a
        /* End of Stream mark */
kusano 7d535a
        headerSize = LZ4F_compressEnd(ctx, dstBuffer, dstBufferSize, NULL);
kusano 7d535a
        if (LZ4F_isError(headerSize)) EXM_THROW(36, "End of file generation failed : %s", LZ4F_getErrorName(headerSize));
kusano 7d535a
kusano 7d535a
        sizeCheck = fwrite(dstBuffer, 1, headerSize, dstFile);
kusano 7d535a
        if (sizeCheck!=headerSize) EXM_THROW(37, "Write error : cannot write end of stream");
kusano 7d535a
        compressedfilesize += headerSize;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* Release files */
kusano 7d535a
    fclose (srcFile);
kusano 7d535a
    fclose (dstFile);
kusano 7d535a
kusano 7d535a
    /* Final Status */
kusano 7d535a
    DISPLAYLEVEL(2, "\r%79s\r", "");
kusano 7d535a
    DISPLAYLEVEL(2, "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
kusano 7d535a
        filesize, compressedfilesize, (double)compressedfilesize/(filesize + !filesize)*100);   /* avoid division by zero */
kusano 7d535a
kusano 7d535a
    return 0;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
int LZ4IO_compressFilename(const char* srcFileName, const char* dstFileName, int compressionLevel)
kusano 7d535a
{
kusano 7d535a
    clock_t start, end;
kusano 7d535a
    cRess_t ress;
kusano 7d535a
    int issueWithSrcFile = 0;
kusano 7d535a
kusano 7d535a
    /* Init */
kusano 7d535a
    start = clock();
kusano 7d535a
    ress = LZ4IO_createCResources();
kusano 7d535a
kusano 7d535a
    /* Compress File */
kusano 7d535a
    issueWithSrcFile += LZ4IO_compressFilename_extRess(ress, srcFileName, dstFileName, compressionLevel);
kusano 7d535a
kusano 7d535a
    /* Free resources */
kusano 7d535a
    LZ4IO_freeCResources(ress);
kusano 7d535a
kusano 7d535a
    /* Final Status */
kusano 7d535a
    end = clock();
kusano 7d535a
    {
kusano 7d535a
        double seconds = (double)(end - start) / CLOCKS_PER_SEC;
kusano 7d535a
        DISPLAYLEVEL(4, "Completed in %.2f sec \n", seconds);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    return issueWithSrcFile;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
#define FNSPACE 30
kusano 7d535a
int LZ4IO_compressMultipleFilenames(const char** inFileNamesTable, int ifntSize, const char* suffix, int compressionLevel)
kusano 7d535a
{
kusano 7d535a
    int i;
kusano 7d535a
    int missed_files = 0;
kusano 7d535a
    char* dstFileName = (char*)malloc(FNSPACE);
kusano 7d535a
    size_t ofnSize = FNSPACE;
kusano 7d535a
    const size_t suffixSize = strlen(suffix);
kusano 7d535a
    cRess_t ress;
kusano 7d535a
kusano 7d535a
    /* init */
kusano 7d535a
    ress = LZ4IO_createCResources();
kusano 7d535a
kusano 7d535a
    /* loop on each file */
kusano 7d535a
    for (i=0; i
kusano 7d535a
    {
kusano 7d535a
        size_t ifnSize = strlen(inFileNamesTable[i]);
kusano 7d535a
        if (ofnSize <= ifnSize+suffixSize+1) { free(dstFileName); ofnSize = ifnSize + 20; dstFileName = (char*)malloc(ofnSize); }
kusano 7d535a
        strcpy(dstFileName, inFileNamesTable[i]);
kusano 7d535a
        strcat(dstFileName, suffix);
kusano 7d535a
kusano 7d535a
        missed_files += LZ4IO_compressFilename_extRess(ress, inFileNamesTable[i], dstFileName, compressionLevel);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* Close & Free */
kusano 7d535a
    LZ4IO_freeCResources(ress);
kusano 7d535a
    free(dstFileName);
kusano 7d535a
kusano 7d535a
    return missed_files;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* ********************************************************************* */
kusano 7d535a
/* ********************** LZ4 file-stream Decompression **************** */
kusano 7d535a
/* ********************************************************************* */
kusano 7d535a
kusano 7d535a
static unsigned LZ4IO_readLE32 (const void* s)
kusano 7d535a
{
kusano 7d535a
    const unsigned char* srcPtr = (const unsigned char*)s;
kusano 7d535a
    unsigned value32 = srcPtr[0];
kusano 7d535a
    value32 += (srcPtr[1]<<8);
kusano 7d535a
    value32 += (srcPtr[2]<<16);
kusano 7d535a
    value32 += ((unsigned)srcPtr[3])<<24;
kusano 7d535a
    return value32;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static unsigned LZ4IO_fwriteSparse(FILE* file, const void* buffer, size_t bufferSize, unsigned storedSkips)
kusano 7d535a
{
kusano 7d535a
    const size_t* const bufferT = (const size_t*)buffer;   /* Buffer is supposed malloc'ed, hence aligned on size_t */
kusano 7d535a
    const size_t* ptrT = bufferT;
kusano 7d535a
    size_t  bufferSizeT = bufferSize / sizeT;
kusano 7d535a
    const size_t* const bufferTEnd = bufferT + bufferSizeT;
kusano 7d535a
    static const size_t segmentSizeT = (32 KB) / sizeT;
kusano 7d535a
kusano 7d535a
    if (!g_sparseFileSupport)   /* normal write */
kusano 7d535a
    {
kusano 7d535a
        size_t sizeCheck = fwrite(buffer, 1, bufferSize, file);
kusano 7d535a
        if (sizeCheck != bufferSize) EXM_THROW(70, "Write error : cannot write decoded block");
kusano 7d535a
        return 0;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* avoid int overflow */
kusano 7d535a
    if (storedSkips > 1 GB)
kusano 7d535a
    {
kusano 7d535a
        int seekResult = fseek(file, 1 GB, SEEK_CUR);
kusano 7d535a
        if (seekResult != 0) EXM_THROW(71, "1 GB skip error (sparse file support)");
kusano 7d535a
        storedSkips -= 1 GB;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    while (ptrT < bufferTEnd)
kusano 7d535a
    {
kusano 7d535a
        size_t seg0SizeT = segmentSizeT;
kusano 7d535a
        size_t nb0T;
kusano 7d535a
        int seekResult;
kusano 7d535a
kusano 7d535a
        /* count leading zeros */
kusano 7d535a
        if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
kusano 7d535a
        bufferSizeT -= seg0SizeT;
kusano 7d535a
        for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
kusano 7d535a
        storedSkips += (unsigned)(nb0T * sizeT);
kusano 7d535a
kusano 7d535a
        if (nb0T != seg0SizeT)   /* not all 0s */
kusano 7d535a
        {
kusano 7d535a
            size_t sizeCheck;
kusano 7d535a
            seekResult = fseek(file, storedSkips, SEEK_CUR);
kusano 7d535a
            if (seekResult) EXM_THROW(72, "Sparse skip error ; try --no-sparse");
kusano 7d535a
            storedSkips = 0;
kusano 7d535a
            seg0SizeT -= nb0T;
kusano 7d535a
            ptrT += nb0T;
kusano 7d535a
            sizeCheck = fwrite(ptrT, sizeT, seg0SizeT, file);
kusano 7d535a
            if (sizeCheck != seg0SizeT) EXM_THROW(73, "Write error : cannot write decoded block");
kusano 7d535a
        }
kusano 7d535a
        ptrT += seg0SizeT;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if (bufferSize & maskT)   /* size not multiple of sizeT : implies end of block */
kusano 7d535a
    {
kusano 7d535a
        const char* const restStart = (const char*)bufferTEnd;
kusano 7d535a
        const char* restPtr = restStart;
kusano 7d535a
        size_t  restSize =  bufferSize & maskT;
kusano 7d535a
        const char* const restEnd = restStart + restSize;
kusano 7d535a
        for (; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
kusano 7d535a
        storedSkips += (unsigned) (restPtr - restStart);
kusano 7d535a
        if (restPtr != restEnd)
kusano 7d535a
        {
kusano 7d535a
            size_t sizeCheck;
kusano 7d535a
            int seekResult = fseek(file, storedSkips, SEEK_CUR);
kusano 7d535a
            if (seekResult) EXM_THROW(74, "Sparse skip error ; try --no-sparse");
kusano 7d535a
            storedSkips = 0;
kusano 7d535a
            sizeCheck = fwrite(restPtr, 1, restEnd - restPtr, file);
kusano 7d535a
            if (sizeCheck != (size_t)(restEnd - restPtr)) EXM_THROW(75, "Write error : cannot write decoded end of block");
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    return storedSkips;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static void LZ4IO_fwriteSparseEnd(FILE* file, unsigned storedSkips)
kusano 7d535a
{
kusano 7d535a
    char lastZeroByte[1] = { 0 };
kusano 7d535a
kusano 7d535a
    if (storedSkips>0)   /* implies g_sparseFileSupport */
kusano 7d535a
    {
kusano 7d535a
        int seekResult;
kusano 7d535a
        size_t sizeCheck;
kusano 7d535a
        storedSkips --;
kusano 7d535a
        seekResult = fseek(file, storedSkips, SEEK_CUR);
kusano 7d535a
        if (seekResult != 0) EXM_THROW(69, "Final skip error (sparse file)\n");
kusano 7d535a
        sizeCheck = fwrite(lastZeroByte, 1, 1, file);
kusano 7d535a
        if (sizeCheck != 1) EXM_THROW(69, "Write error : cannot write last zero\n");
kusano 7d535a
    }
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
static unsigned g_magicRead = 0;
kusano 7d535a
static unsigned long long LZ4IO_decodeLegacyStream(FILE* finput, FILE* foutput)
kusano 7d535a
{
kusano 7d535a
    unsigned long long filesize = 0;
kusano 7d535a
    char* in_buff;
kusano 7d535a
    char* out_buff;
kusano 7d535a
    unsigned storedSkips = 0;
kusano 7d535a
kusano 7d535a
    /* Allocate Memory */
kusano 7d535a
    in_buff = (char*)malloc(LZ4_compressBound(LEGACY_BLOCKSIZE));
kusano 7d535a
    out_buff = (char*)malloc(LEGACY_BLOCKSIZE);
kusano 7d535a
    if (!in_buff || !out_buff) EXM_THROW(51, "Allocation error : not enough memory");
kusano 7d535a
kusano 7d535a
    /* Main Loop */
kusano 7d535a
    while (1)
kusano 7d535a
    {
kusano 7d535a
        int decodeSize;
kusano 7d535a
        size_t sizeCheck;
kusano 7d535a
        unsigned int blockSize;
kusano 7d535a
kusano 7d535a
        /* Block Size */
kusano 7d535a
        sizeCheck = fread(in_buff, 1, 4, finput);
kusano 7d535a
        if (sizeCheck==0) break;                   /* Nothing to read : file read is completed */
kusano 7d535a
        blockSize = LZ4IO_readLE32(in_buff);       /* Convert to Little Endian */
kusano 7d535a
        if (blockSize > LZ4_COMPRESSBOUND(LEGACY_BLOCKSIZE))
kusano 7d535a
        {   /* Cannot read next block : maybe new stream ? */
kusano 7d535a
            g_magicRead = blockSize;
kusano 7d535a
            break;
kusano 7d535a
        }
kusano 7d535a
kusano 7d535a
        /* Read Block */
kusano 7d535a
        sizeCheck = fread(in_buff, 1, blockSize, finput);
kusano 7d535a
        if (sizeCheck!=blockSize) EXM_THROW(52, "Read error : cannot access compressed block !");
kusano 7d535a
kusano 7d535a
        /* Decode Block */
kusano 7d535a
        decodeSize = LZ4_decompress_safe(in_buff, out_buff, blockSize, LEGACY_BLOCKSIZE);
kusano 7d535a
        if (decodeSize < 0) EXM_THROW(53, "Decoding Failed ! Corrupted input detected !");
kusano 7d535a
        filesize += decodeSize;
kusano 7d535a
kusano 7d535a
        /* Write Block */
kusano 7d535a
        storedSkips = LZ4IO_fwriteSparse(foutput, out_buff, decodeSize, storedSkips);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    LZ4IO_fwriteSparseEnd(foutput, storedSkips);
kusano 7d535a
kusano 7d535a
    /* Free */
kusano 7d535a
    free(in_buff);
kusano 7d535a
    free(out_buff);
kusano 7d535a
kusano 7d535a
    return filesize;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
kusano 7d535a
typedef struct {
kusano 7d535a
    void*  srcBuffer;
kusano 7d535a
    size_t srcBufferSize;
kusano 7d535a
    void*  dstBuffer;
kusano 7d535a
    size_t dstBufferSize;
kusano 7d535a
    LZ4F_decompressionContext_t dCtx;
kusano 7d535a
} dRess_t;
kusano 7d535a
kusano 7d535a
static const size_t LZ4IO_dBufferSize = 64 KB;
kusano 7d535a
kusano 7d535a
static dRess_t LZ4IO_createDResources(void)
kusano 7d535a
{
kusano 7d535a
    dRess_t ress;
kusano 7d535a
    LZ4F_errorCode_t errorCode;
kusano 7d535a
kusano 7d535a
    /* init */
kusano 7d535a
    errorCode = LZ4F_createDecompressionContext(&ress.dCtx, LZ4F_VERSION);
kusano 7d535a
    if (LZ4F_isError(errorCode)) EXM_THROW(60, "Can't create LZ4F context : %s", LZ4F_getErrorName(errorCode));
kusano 7d535a
kusano 7d535a
    /* Allocate Memory */
kusano 7d535a
    ress.srcBufferSize = LZ4IO_dBufferSize;
kusano 7d535a
    ress.srcBuffer = malloc(ress.srcBufferSize);
kusano 7d535a
    ress.dstBufferSize = LZ4IO_dBufferSize;
kusano 7d535a
    ress.dstBuffer = malloc(ress.dstBufferSize);
kusano 7d535a
    if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(61, "Allocation error : not enough memory");
kusano 7d535a
kusano 7d535a
    return ress;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static void LZ4IO_freeDResources(dRess_t ress)
kusano 7d535a
{
kusano 7d535a
    LZ4F_errorCode_t errorCode = LZ4F_freeDecompressionContext(ress.dCtx);
kusano 7d535a
    if (LZ4F_isError(errorCode)) EXM_THROW(69, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode));
kusano 7d535a
    free(ress.srcBuffer);
kusano 7d535a
    free(ress.dstBuffer);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
static unsigned long long LZ4IO_decompressLZ4F(dRess_t ress, FILE* srcFile, FILE* dstFile)
kusano 7d535a
{
kusano 7d535a
    unsigned long long filesize = 0;
kusano 7d535a
    LZ4F_errorCode_t nextToLoad;
kusano 7d535a
    unsigned storedSkips = 0;
kusano 7d535a
kusano 7d535a
    /* Init feed with magic number (already consumed from FILE*  sFile) */
kusano 7d535a
    {
kusano 7d535a
        size_t inSize = MAGICNUMBER_SIZE;
kusano 7d535a
        size_t outSize= 0;
kusano 7d535a
        LZ4IO_writeLE32(ress.srcBuffer, LZ4IO_MAGICNUMBER);
kusano 7d535a
        nextToLoad = LZ4F_decompress(ress.dCtx, ress.dstBuffer, &outSize, ress.srcBuffer, &inSize, NULL);
kusano 7d535a
        if (LZ4F_isError(nextToLoad)) EXM_THROW(62, "Header error : %s", LZ4F_getErrorName(nextToLoad));
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* Main Loop */
kusano 7d535a
    for (;nextToLoad;)
kusano 7d535a
    {
kusano 7d535a
        size_t readSize;
kusano 7d535a
        size_t pos = 0;
kusano 7d535a
        size_t decodedBytes = ress.dstBufferSize;
kusano 7d535a
kusano 7d535a
        /* Read input */
kusano 7d535a
        if (nextToLoad > ress.srcBufferSize) nextToLoad = ress.srcBufferSize;
kusano 7d535a
        readSize = fread(ress.srcBuffer, 1, nextToLoad, srcFile);
kusano 7d535a
        if (!readSize)
kusano 7d535a
            break;   /* empty file or stream */
kusano 7d535a
kusano 7d535a
        while ((pos < readSize) || (decodedBytes == ress.dstBufferSize))   /* still to read, or still to flush */
kusano 7d535a
        {
kusano 7d535a
            /* Decode Input (at least partially) */
kusano 7d535a
            size_t remaining = readSize - pos;
kusano 7d535a
            decodedBytes = ress.dstBufferSize;
kusano 7d535a
            nextToLoad = LZ4F_decompress(ress.dCtx, ress.dstBuffer, &decodedBytes, (char*)(ress.srcBuffer)+pos, &remaining, NULL);
kusano 7d535a
            if (LZ4F_isError(nextToLoad)) EXM_THROW(66, "Decompression error : %s", LZ4F_getErrorName(nextToLoad));
kusano 7d535a
            pos += remaining;
kusano 7d535a
kusano 7d535a
            if (decodedBytes)
kusano 7d535a
            {
kusano 7d535a
                /* Write Block */
kusano 7d535a
                filesize += decodedBytes;
kusano 7d535a
                DISPLAYUPDATE(2, "\rDecompressed : %u MB  ", (unsigned)(filesize>>20));
kusano 7d535a
                storedSkips = LZ4IO_fwriteSparse(dstFile, ress.dstBuffer, decodedBytes, storedSkips);
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
            if (!nextToLoad) break;
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    LZ4IO_fwriteSparseEnd(dstFile, storedSkips);
kusano 7d535a
kusano 7d535a
    if (nextToLoad!=0)
kusano 7d535a
        EXM_THROW(67, "Unfinished stream");
kusano 7d535a
kusano 7d535a
    return filesize;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
#define PTSIZE  (64 KB)
kusano 7d535a
#define PTSIZET (PTSIZE / sizeof(size_t))
kusano 7d535a
static unsigned long long LZ4IO_passThrough(FILE* finput, FILE* foutput, unsigned char MNstore[MAGICNUMBER_SIZE])
kusano 7d535a
{
kusano 7d535a
	size_t buffer[PTSIZET];
kusano 7d535a
    size_t read = 1, sizeCheck;
kusano 7d535a
    unsigned long long total = MAGICNUMBER_SIZE;
kusano 7d535a
    unsigned storedSkips = 0;
kusano 7d535a
kusano 7d535a
    sizeCheck = fwrite(MNstore, 1, MAGICNUMBER_SIZE, foutput);
kusano 7d535a
    if (sizeCheck != MAGICNUMBER_SIZE) EXM_THROW(50, "Pass-through write error");
kusano 7d535a
kusano 7d535a
    while (read)
kusano 7d535a
    {
kusano 7d535a
        read = fread(buffer, 1, PTSIZE, finput);
kusano 7d535a
        total += read;
kusano 7d535a
        storedSkips = LZ4IO_fwriteSparse(foutput, buffer, read, storedSkips);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    LZ4IO_fwriteSparseEnd(foutput, storedSkips);
kusano 7d535a
    return total;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
#define ENDOFSTREAM ((unsigned long long)-1)
kusano 7d535a
static unsigned long long selectDecoder(dRess_t ress, FILE* finput, FILE* foutput)
kusano 7d535a
{
kusano 7d535a
    unsigned char MNstore[MAGICNUMBER_SIZE];
kusano 7d535a
    unsigned magicNumber, size;
kusano 7d535a
    int errorNb;
kusano 7d535a
    size_t nbReadBytes;
kusano 7d535a
    static unsigned nbCalls = 0;
kusano 7d535a
kusano 7d535a
    /* init */
kusano 7d535a
    nbCalls++;
kusano 7d535a
kusano 7d535a
    /* Check Archive Header */
kusano 7d535a
    if (g_magicRead)
kusano 7d535a
    {
kusano 7d535a
      magicNumber = g_magicRead;
kusano 7d535a
      g_magicRead = 0;
kusano 7d535a
    }
kusano 7d535a
    else
kusano 7d535a
    {
kusano 7d535a
      nbReadBytes = fread(MNstore, 1, MAGICNUMBER_SIZE, finput);
kusano 7d535a
      if (nbReadBytes==0) return ENDOFSTREAM;                  /* EOF */
kusano 7d535a
      if (nbReadBytes != MAGICNUMBER_SIZE) EXM_THROW(40, "Unrecognized header : Magic Number unreadable");
kusano 7d535a
      magicNumber = LZ4IO_readLE32(MNstore);   /* Little Endian format */
kusano 7d535a
    }
kusano 7d535a
    if (LZ4IO_isSkippableMagicNumber(magicNumber)) magicNumber = LZ4IO_SKIPPABLE0;  /* fold skippable magic numbers */
kusano 7d535a
kusano 7d535a
    switch(magicNumber)
kusano 7d535a
    {
kusano 7d535a
    case LZ4IO_MAGICNUMBER:
kusano 7d535a
        return LZ4IO_decompressLZ4F(ress, finput, foutput);
kusano 7d535a
    case LEGACY_MAGICNUMBER:
kusano 7d535a
        DISPLAYLEVEL(4, "Detected : Legacy format \n");
kusano 7d535a
        return LZ4IO_decodeLegacyStream(finput, foutput);
kusano 7d535a
    case LZ4IO_SKIPPABLE0:
kusano 7d535a
        DISPLAYLEVEL(4, "Skipping detected skippable area \n");
kusano 7d535a
        nbReadBytes = fread(MNstore, 1, 4, finput);
kusano 7d535a
        if (nbReadBytes != 4) EXM_THROW(42, "Stream error : skippable size unreadable");
kusano 7d535a
        size = LZ4IO_readLE32(MNstore);     /* Little Endian format */
kusano 7d535a
        errorNb = fseek(finput, size, SEEK_CUR);
kusano 7d535a
        if (errorNb != 0) EXM_THROW(43, "Stream error : cannot skip skippable area");
kusano 7d535a
        return selectDecoder(ress, finput, foutput);
kusano 7d535a
    EXTENDED_FORMAT;
kusano 7d535a
    default:
kusano 7d535a
        if (nbCalls == 1)   /* just started */
kusano 7d535a
        {
kusano 7d535a
            if (g_overwrite)
kusano 7d535a
                return LZ4IO_passThrough(finput, foutput, MNstore);
kusano 7d535a
            EXM_THROW(44,"Unrecognized header : file cannot be decoded");   /* Wrong magic number at the beginning of 1st stream */
kusano 7d535a
        }
kusano 7d535a
        DISPLAYLEVEL(2, "Stream followed by unrecognized data\n");
kusano 7d535a
        return ENDOFSTREAM;
kusano 7d535a
    }
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
static int LZ4IO_decompressFile_extRess(dRess_t ress, const char* input_filename, const char* output_filename)
kusano 7d535a
{
kusano 7d535a
    unsigned long long filesize = 0, decodedSize=0;
kusano 7d535a
    FILE* finput;
kusano 7d535a
    FILE* foutput;
kusano 7d535a
kusano 7d535a
kusano 7d535a
    /* Init */
kusano 7d535a
    if (LZ4IO_getFiles(input_filename, output_filename, &finput, &foutput))
kusano 7d535a
        return 1;
kusano 7d535a
kusano 7d535a
    /* sparse file */
kusano 7d535a
    if (g_sparseFileSupport) { SET_SPARSE_FILE_MODE(foutput); }
kusano 7d535a
kusano 7d535a
    /* Loop over multiple streams */
kusano 7d535a
    do
kusano 7d535a
    {
kusano 7d535a
        decodedSize = selectDecoder(ress, finput, foutput);
kusano 7d535a
        if (decodedSize != ENDOFSTREAM)
kusano 7d535a
            filesize += decodedSize;
kusano 7d535a
    } while (decodedSize != ENDOFSTREAM);
kusano 7d535a
kusano 7d535a
    /* Final Status */
kusano 7d535a
    DISPLAYLEVEL(2, "\r%79s\r", "");
kusano 7d535a
    DISPLAYLEVEL(2, "Successfully decoded %llu bytes \n", filesize);
kusano 7d535a
kusano 7d535a
    /* Close */
kusano 7d535a
    fclose(finput);
kusano 7d535a
    fclose(foutput);
kusano 7d535a
kusano 7d535a
    return 0;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
int LZ4IO_decompressFilename(const char* input_filename, const char* output_filename)
kusano 7d535a
{
kusano 7d535a
    dRess_t ress;
kusano 7d535a
    clock_t start, end;
kusano 7d535a
    int missingFiles = 0;
kusano 7d535a
kusano 7d535a
    start = clock();
kusano 7d535a
kusano 7d535a
    ress = LZ4IO_createDResources();
kusano 7d535a
    missingFiles += LZ4IO_decompressFile_extRess(ress, input_filename, output_filename);
kusano 7d535a
    LZ4IO_freeDResources(ress);
kusano 7d535a
kusano 7d535a
    end = clock();
kusano 7d535a
    if (end==start) end=start+1;
kusano 7d535a
    {
kusano 7d535a
        double seconds = (double)(end - start)/CLOCKS_PER_SEC;
kusano 7d535a
        DISPLAYLEVEL(4, "Done in %.2f sec  \n", seconds);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    return missingFiles;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
#define MAXSUFFIXSIZE 8
kusano 7d535a
int LZ4IO_decompressMultipleFilenames(const char** inFileNamesTable, int ifntSize, const char* suffix)
kusano 7d535a
{
kusano 7d535a
    int i;
kusano 7d535a
    int skippedFiles = 0;
kusano 7d535a
    int missingFiles = 0;
kusano 7d535a
    char* outFileName = (char*)malloc(FNSPACE);
kusano 7d535a
    size_t ofnSize = FNSPACE;
kusano 7d535a
    const size_t suffixSize = strlen(suffix);
kusano 7d535a
    const char* suffixPtr;
kusano 7d535a
    dRess_t ress;
kusano 7d535a
kusano 7d535a
	if (outFileName==NULL) exit(1);   /* not enough memory */
kusano 7d535a
    ress = LZ4IO_createDResources();
kusano 7d535a
kusano 7d535a
    for (i=0; i
kusano 7d535a
    {
kusano 7d535a
        size_t ifnSize = strlen(inFileNamesTable[i]);
kusano 7d535a
        suffixPtr = inFileNamesTable[i] + ifnSize - suffixSize;
kusano 7d535a
        if (ofnSize <= ifnSize-suffixSize+1) { free(outFileName); ofnSize = ifnSize + 20; outFileName = (char*)malloc(ofnSize); if (outFileName==NULL) exit(1); }
kusano 7d535a
        if (ifnSize <= suffixSize  ||  strcmp(suffixPtr, suffix) != 0)
kusano 7d535a
        {
kusano 7d535a
            DISPLAYLEVEL(1, "File extension doesn't match expected LZ4_EXTENSION (%4s); will not process file: %s\n", suffix, inFileNamesTable[i]);
kusano 7d535a
            skippedFiles++;
kusano 7d535a
            continue;
kusano 7d535a
        }
kusano 7d535a
        memcpy(outFileName, inFileNamesTable[i], ifnSize - suffixSize);
kusano 7d535a
        outFileName[ifnSize-suffixSize] = '\0';
kusano 7d535a
kusano 7d535a
        missingFiles += LZ4IO_decompressFile_extRess(ress, inFileNamesTable[i], outFileName);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    LZ4IO_freeDResources(ress);
kusano 7d535a
    free(outFileName);
kusano 7d535a
    return missingFiles + skippedFiles;
kusano 7d535a
}