Updated : compress multiple files
authorYann Collet <yann.collet.73@gmail.com>
Sat, 7 Mar 2015 12:23:00 +0000 (13:23 +0100)
committerYann Collet <yann.collet.73@gmail.com>
Sat, 7 Mar 2015 12:23:00 +0000 (13:23 +0100)
NEWS
programs/Makefile
programs/bench.c
programs/bench.h
programs/lz4cli.c
programs/lz4io.c
programs/lz4io.h

diff --git a/NEWS b/NEWS
index 8aeab1b..05ac6b5 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+r128:
+New   : command -m, to compress multiple files in a single command
+Fixed : Restored lz4hc compression ratio (slightly lower since r124)
+Added : Visual project directory
+
 r126:
 New : lz4frame API is now integrated into liblz4
 Fixed : GCC 4.9 bug on highest performance settings, reported by Greg Slazinski
index 076d3bf..1070f40 100644 (file)
@@ -31,7 +31,7 @@
 # fullbench32: Same as fullbench, but forced to compile in 32-bits mode
 # ##########################################################################
 
-RELEASE?= r126
+RELEASE?= r128
 
 DESTDIR?=
 PREFIX ?= /usr
@@ -158,6 +158,12 @@ test-lz4: lz4 datagen
        @rm *.test
        @echo frame concatenation test completed
 # test frame concatenation with null-length frame
+       @echo test multiple input files
+       @./datagen -s1 > file1
+       @./datagen -s2 > file2
+       @./datagen -s3 > file3
+       ./lz4 -f -m file1 file2 file3
+       @rm file1 file2 file3 file1.lz4 file2.lz4 file3.lz4
 
 
 test-lz4c: lz4c datagen
index 02e56c9..77120f2 100644 (file)
@@ -1,6 +1,6 @@
 /*
     bench.c - Demo program to benchmark open-source compression algorithm
-    Copyright (C) Yann Collet 2012-2014
+    Copyright (C) Yann Collet 2012-2015
     GPL v2 License
 
     This program is free software; you can redistribute it and/or modify
@@ -215,7 +215,7 @@ static size_t BMK_findMaxMem(U64 requiredMem)
 }
 
 
-static U64 BMK_GetFileSize(char* infilename)
+static U64 BMK_GetFileSize(const char* infilename)
 {
     int r;
 #if defined(_MSC_VER)
@@ -234,7 +234,7 @@ static U64 BMK_GetFileSize(char* infilename)
 *  Public function
 **********************************************************/
 
-int BMK_benchFile(char** fileNamesTable, int nbFiles, int cLevel)
+int BMK_benchFile(const char** fileNamesTable, int nbFiles, int cLevel)
 {
   int fileIdx=0;
   char* orig_buff;
@@ -265,7 +265,7 @@ int BMK_benchFile(char** fileNamesTable, int nbFiles, int cLevel)
   while (fileIdx<nbFiles)
   {
       FILE*  inFile;
-      char*  inFileName;
+      const char*  inFileName;
       U64    inFileSize;
       size_t benchedSize;
       int nbChunks;
index d42df68..2a20cdb 100644 (file)
@@ -1,6 +1,6 @@
 /*
     bench.h - Demo program to benchmark open-source compression algorithm
-    Copyright (C) Yann Collet 2012-2014
+    Copyright (C) Yann Collet 2012-2015
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@ extern "C" {
 
 
 /* Main function */
-int BMK_benchFile(char** fileNamesTable, int nbFiles, int cLevel);
+int BMK_benchFile(const char** fileNamesTable, int nbFiles, int cLevel);
 
 /* Set Parameters */
 void BMK_SetBlocksize(int bsize);
index 7524947..ef3ef65 100644 (file)
@@ -1,6 +1,6 @@
 /*
   LZ4cli - LZ4 Command Line Interface
-  Copyright (C) Yann Collet 2011-2014
+  Copyright (C) Yann Collet 2011-2015
 
   GPL v2 License
 
@@ -85,7 +85,7 @@
 ******************************/
 #define COMPRESSOR_NAME "LZ4 command line interface"
 #ifndef LZ4_VERSION
-#  define LZ4_VERSION "r126"
+#  define LZ4_VERSION "r128"
 #endif
 #define AUTHOR "Yann Collet"
 #define WELCOME_MESSAGE "*** %s %i-bits %s, by %s (%s) ***\n", COMPRESSOR_NAME, (int)(sizeof(void*)*8), LZ4_VERSION, AUTHOR, __DATE__
@@ -136,7 +136,7 @@ static char* programName;
 #define EXTENDED_FORMAT
 #define DEFAULT_COMPRESSOR   LZ4IO_compressFilename
 #define DEFAULT_DECOMPRESSOR LZ4IO_decompressFilename
-int LZ4IO_compressFilename_Legacy(char* input_filename, char* output_filename, int compressionlevel);   /* hidden function */
+int LZ4IO_compressFilename_Legacy(const char* input_filename, const char* output_filename, int compressionlevel);   /* hidden function */
 
 
 /****************************
@@ -156,7 +156,7 @@ static int usage(void)
     DISPLAY( " -z     : force compression\n");
     DISPLAY( " -f     : overwrite output without prompting \n");
     DISPLAY( " -h/-H  : display help/long help and exit\n");
-    DISPLAY( " -m     : allow mulitple input files (implies automatic output filenames)");
+    DISPLAY( " -m     : multiple input files (implies automatic output filenames)\n");
     return 0;
 }
 
@@ -262,32 +262,31 @@ int main(int argc, char** argv)
         cLevel=0,
         decode=0,
         bench=0,
-        filenamesStart=2,
         legacy_format=0,
         forceStdout=0,
         forceCompress=0,
         main_pause=0,
         multiple_inputs=0;
-    char* input_filename=0;
+    const char* input_filename=0;
     char* output_filename=0;
     char* dynNameSpace=0;
-    char *input_filenames[argc];
+    const char** inFileNames = NULL;
+    unsigned ifnIdx=0;
     char nullOutput[] = NULL_OUTPUT;
     char extension[] = LZ4_EXTENSION;
-    int blockSize;
+    int  blockSize;
 
     /* Init */
     programName = argv[0];
     LZ4IO_setOverwrite(0);
     blockSize = LZ4IO_setBlockSizeID(LZ4_BLOCKSIZEID_DEFAULT);
 
-    /* lz4cat behavior */
+    /* lz4cat predefined behavior */
     if (!strcmp(programName, LZ4_CAT)) { decode=1; forceStdout=1; output_filename=stdoutmark; displayLevel=1; }
 
     /* command switches */
     for(i=1; i<argc; i++)
     {
-        input_filenames[i]=0;
         char* argument = argv[i];
 
         if(!argument) continue;   /* Protection if argument empty */
@@ -381,7 +380,7 @@ int main(int argc, char** argv)
                             break;
                         }
                         case 'D': LZ4IO_setBlockMode(LZ4IO_blockLinked); argument++; break;
-                        case 'X': LZ4IO_setBlockChecksumMode(1); argument ++; break;   /* currently disables */
+                        case 'X': LZ4IO_setBlockChecksumMode(1); argument ++; break;   /* currently disabled */
                         default : exitBlockProperties=1;
                         }
                         if (exitBlockProperties) break;
@@ -392,10 +391,16 @@ int main(int argc, char** argv)
                 case 'S': if (argument[1]=='x') { LZ4IO_setStreamChecksumMode(0); argument++; break; } else { badusage(); }
 
                     /* Benchmark */
-                case 'b': bench=1; break;
+                case 'b': bench=1; multiple_inputs=1;
+                    if (inFileNames == NULL)
+                        inFileNames = malloc(argc * sizeof(char*));
+                    break;
 
-                    /* Treat non-option args as input files.  See Issue 151 in Google Code */
-                case 'm': multiple_inputs=1; break;
+                    /* Treat non-option args as input files.  See https://code.google.com/p/lz4/issues/detail?id=151 */
+                case 'm': multiple_inputs=1;
+                    if (inFileNames == NULL)
+                        inFileNames = malloc(argc * sizeof(char*));
+                    break;
 
                     /* Modify Nb Iterations (benchmark only) */
                 case 'i':
@@ -420,11 +425,11 @@ int main(int argc, char** argv)
             continue;
         }
 
-        /* Input files are a wonderful thing.  Store in *input_filenames[] if -m is used. */
-        if (multiple_inputs) { input_filenames[i]=argument; continue; }
+        /* Store in *inFileNames[] if -m is used. */
+        if (multiple_inputs) { inFileNames[ifnIdx++]=argument; continue; }
 
         /* Store first non-option arg in input_filename to preserve original cli logic. */
-        if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }
+        if (!input_filename) { input_filename=argument; continue; }
 
         /* Second non-option arg in output_filename to preserve original cli logic. */
         if (!output_filename)
@@ -433,95 +438,87 @@ int main(int argc, char** argv)
             if (!strcmp (output_filename, nullOutput)) output_filename = nulmark;
             continue;
         }
-    }
 
-    /* If we are not using multiple_inputs, clear *input_filesnames[] (just in case) and set element 0 to input_filename. */
-    if (!multiple_inputs)
-    {
-        for (i=0; i<argc; i++) input_filenames[i]=0;
+        /* 3rd non-option arg should not exist */
+        DISPLAYLEVEL(1, "Warning : %s won't be used ! Do you want multiple input files (-m) ? \n", argument);
     }
-    input_filenames[0]=input_filename;
 
-    /* Loop through all the input_filenames[], which should only be 1 if we're using the old (original, non -m) cli method.
-       This is a shamelessly-stolen loop over the original logic. */
-    for (i=0; i<argc; i++)
-    {
-        if (!input_filenames[i]) continue;
-        input_filename=input_filenames[i];
-        DISPLAYLEVEL(3, WELCOME_MESSAGE);
-        if (!decode) DISPLAYLEVEL(4, "Blocks size : %i KB\n", blockSize>>10);
+    DISPLAYLEVEL(3, WELCOME_MESSAGE);
+    if (!decode) DISPLAYLEVEL(4, "Blocks size : %i KB\n", blockSize>>10);
 
-        /* No input filename ==> use stdin */
-        if(!input_filename) { input_filename=stdinmark; }
+    /* No input filename ==> use stdin */
+    if (multiple_inputs) input_filename = inFileNames[0], output_filename = (char*)(inFileNames[0]);
+    if(!input_filename) { input_filename=stdinmark; }
 
-        /* Check if input or output are defined as console; trigger an error in this case */
-        if (!strcmp(input_filename, stdinmark) && IS_CONSOLE(stdin) ) badusage();
+    /* Check if input or output are defined as console; trigger an error in this case */
+    if (!strcmp(input_filename, stdinmark) && IS_CONSOLE(stdin) ) badusage();
 
-        /* Check if benchmark is selected */
-        if (bench) return BMK_benchFile(argv+filenamesStart, argc-filenamesStart, cLevel);
+    /* Check if benchmark is selected */
+    if (bench) return BMK_benchFile(inFileNames, ifnIdx, cLevel);
 
-        /* No output filename ==> try to select one automatically (when possible) */
-        while (!output_filename)
+    /* No output filename ==> try to select one automatically (when possible) */
+    while (!output_filename)
+    {
+        if (!IS_CONSOLE(stdout)) { output_filename=stdoutmark; break; }   /* Default to stdout whenever possible (i.e. not a console) */
+        if ((!decode) && !(forceCompress))   /* auto-determine compression or decompression, based on file extension */
         {
-            if (!IS_CONSOLE(stdout)) { output_filename=stdoutmark; break; }   /* Default to stdout whenever possible (i.e. not a console) */
-            if ((!decode) && !(forceCompress))   /* auto-determine compression or decompression, based on file extension */
-            {
-                size_t l = strlen(input_filename);
-                if (!strcmp(input_filename+(l-4), LZ4_EXTENSION)) decode=1;
-            }
-            if (!decode)   /* compression to file */
-            {
-                size_t l = strlen(input_filename);
-                dynNameSpace = (char*)calloc(1,l+5);
-                output_filename = dynNameSpace;
-                strcpy(output_filename, input_filename);
-                strcpy(output_filename+l, LZ4_EXTENSION);
-                DISPLAYLEVEL(2, "Compressed filename will be : %s \n", output_filename);
-                break;
-            }
-            /* decompression to file (automatic name will work only if input filename has correct format extension) */
-            {
-                size_t outl;
-                size_t inl = strlen(input_filename);
-                dynNameSpace = (char*)calloc(1,inl+1);
-                output_filename = dynNameSpace;
-                strcpy(output_filename, input_filename);
-                outl = inl;
-                if (inl>4)
-                    while ((outl >= inl-4) && (input_filename[outl] ==  extension[outl-inl+4])) output_filename[outl--]=0;
-                if (outl != inl-5) { DISPLAYLEVEL(1, "Cannot determine an output filename\n"); badusage(); }
-                DISPLAYLEVEL(2, "Decoding file %s \n", output_filename);
-            }
+            size_t l = strlen(input_filename);
+            if (!strcmp(input_filename+(l-4), LZ4_EXTENSION)) decode=1;
         }
+        if (!decode)   /* compression to file */
+        {
+            size_t l = strlen(input_filename);
+            dynNameSpace = (char*)calloc(1,l+5);
+            output_filename = dynNameSpace;
+            strcpy(output_filename, input_filename);
+            strcat(output_filename, LZ4_EXTENSION);
+            DISPLAYLEVEL(2, "Compressed filename will be : %s \n", output_filename);
+            break;
+        }
+        /* decompression to file (automatic name will work only if input filename has correct format extension) */
+        {
+            size_t outl;
+            size_t inl = strlen(input_filename);
+            dynNameSpace = (char*)calloc(1,inl+1);
+            output_filename = dynNameSpace;
+            strcpy(output_filename, input_filename);
+            outl = inl;
+            if (inl>4)
+                while ((outl >= inl-4) && (input_filename[outl] ==  extension[outl-inl+4])) output_filename[outl--]=0;
+            if (outl != inl-5) { DISPLAYLEVEL(1, "Cannot determine an output filename\n"); badusage(); }
+            DISPLAYLEVEL(2, "Decoding file %s \n", output_filename);
+        }
+    }
 
-        /* Check if output is defined as console; trigger an error in this case */
-        if (!strcmp(output_filename,stdoutmark) && IS_CONSOLE(stdout) && !forceStdout) badusage();
+    /* Check if output is defined as console; trigger an error in this case */
+    if (!strcmp(output_filename,stdoutmark) && IS_CONSOLE(stdout) && !forceStdout) badusage();
 
-        /* No warning message in pure pipe mode (stdin + stdout) */
-        if (!strcmp(input_filename, stdinmark) && !strcmp(output_filename,stdoutmark) && (displayLevel==2)) displayLevel=1;
+    /* No warning message in pure pipe mode (stdin + stdout) */
+    if (!strcmp(input_filename, stdinmark) && !strcmp(output_filename,stdoutmark) && (displayLevel==2)) displayLevel=1;
 
 
-        /* IO Stream/File */
-        LZ4IO_setNotificationLevel(displayLevel);
-        if (decode) DEFAULT_DECOMPRESSOR(input_filename, output_filename);
+    /* IO Stream/File */
+    LZ4IO_setNotificationLevel(displayLevel);
+    if (decode) DEFAULT_DECOMPRESSOR(input_filename, output_filename);
+    else
+    {
+        /* compression is default action */
+        if (legacy_format)
+        {
+            DISPLAYLEVEL(3, "! Generating compressed LZ4 using Legacy format (deprecated) ! \n");
+            LZ4IO_compressFilename_Legacy(input_filename, output_filename, cLevel);
+        }
         else
         {
-            /* compression is default action */
-            if (legacy_format)
-            {
-                DISPLAYLEVEL(3, "! Generating compressed LZ4 using Legacy format (deprecated) ! \n");
-                LZ4IO_compressFilename_Legacy(input_filename, output_filename, cLevel);
-            }
+            if (multiple_inputs)
+                LZ4IO_compressMultipleFilenames(inFileNames, ifnIdx, LZ4_EXTENSION, cLevel);
             else
-            {
                 DEFAULT_COMPRESSOR(input_filename, output_filename, cLevel);
-            }
         }
-        /* Clear the output_filename here in case there are other files to process. */
-        output_filename=0;
     }
 
     if (main_pause) waitEnter();
     free(dynNameSpace);
+    free((void*)inFileNames);
     return 0;
 }
index 023824e..bc8f3ff 100644 (file)
@@ -1,6 +1,6 @@
 /*
   LZ4io.c - LZ4 File/Stream Interface
-  Copyright (C) Yann Collet 2011-2014
+  Copyright (C) Yann Collet 2011-2015
   GPL v2 License
 
   This program is free software; you can redistribute it and/or modify
@@ -226,7 +226,7 @@ static int          LZ4S_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (
 static int          LZ4S_isSkippableMagicNumber(unsigned int magic) { return (magic & LZ4S_SKIPPABLEMASK) == LZ4S_SKIPPABLE0; }
 
 
-static int get_fileHandle(char* input_filename, char* output_filename, FILE** pfinput, FILE** pfoutput)
+static int get_fileHandle(const char* input_filename, const char* output_filename, FILE** pfinput, FILE** pfoutput)
 {
 
     if (!strcmp (input_filename, stdinmark))
@@ -293,7 +293,7 @@ static void LZ4IO_writeLE32 (void* p, unsigned value32)
 /* LZ4IO_compressFilename_Legacy :
  * This function is intentionally "hidden" (not published in .h)
  * It generates compressed streams using the old 'legacy' format */
-int LZ4IO_compressFilename_Legacy(char* input_filename, char* output_filename, int compressionlevel)
+int LZ4IO_compressFilename_Legacy(const char* input_filename, const char* output_filename, int compressionlevel)
 {
     int (*compressionFunction)(const char*, char*, int);
     unsigned long long filesize = 0;
@@ -367,7 +367,7 @@ int LZ4IO_compressFilename_Legacy(char* input_filename, char* output_filename, i
  *   Compression using Frame format
  * ********************************************/
 
-int LZ4IO_compressFilename(char* input_filename, char* output_filename, int compressionLevel)
+int LZ4IO_compressFilename(const char* input_filename, const char* output_filename, int compressionLevel)
 {
     unsigned long long filesize = 0;
     unsigned long long compressedfilesize = 0;
@@ -465,8 +465,29 @@ int LZ4IO_compressFilename(char* input_filename, char* output_filename, int comp
 }
 
 
+int LZ4IO_compressMultipleFilenames(const char** inFileNamesTable, int ifntSize, const char* suffix, int compressionlevel)
+{
+    int i;
+    char* outFileName = NULL;
+    size_t ofnSize = 0;
+    const size_t suffixSize = strlen(suffix);
+
+    for (i=0; i<ifntSize; i++)
+    {
+        size_t ifnSize = strlen(inFileNamesTable[i]);
+        if (ofnSize <= ifnSize+suffixSize+1) { free(outFileName); ofnSize = ifnSize + 20; outFileName = malloc(ofnSize); }
+        strcpy(outFileName, inFileNamesTable[i]);
+        strcat(outFileName, suffix);
+        LZ4IO_compressFilename(inFileNamesTable[i], outFileName, compressionlevel);
+    }
+    free(outFileName);
+    return 0;
+}
+
+
+
 /* ********************************************************************* */
-/* ********************** LZ4 File / Stream decoding ******************* */
+/* ********************** LZ4 file-stream Decompression **************** */
 /* ********************************************************************* */
 
 static unsigned LZ4IO_readLE32 (const void* s)
@@ -633,7 +654,7 @@ static unsigned long long selectDecoder( FILE* finput,  FILE* foutput)
 }
 
 
-int LZ4IO_decompressFilename(char* input_filename, char* output_filename)
+int LZ4IO_decompressFilename(const char* input_filename, const char* output_filename)
 {
     unsigned long long filesize = 0, decodedSize=0;
     FILE* finput;
index 7869a43..2270c65 100644 (file)
@@ -1,6 +1,6 @@
 /*
   LZ4io.h - LZ4 File/Stream Interface
-  Copyright (C) Yann Collet 2011-2014
+  Copyright (C) Yann Collet 2011-2015
   GPL v2 License
 
   This program is free software; you can redistribute it and/or modify
@@ -47,8 +47,10 @@ static char nulmark[] = "/dev/null";
 /* ****************** Functions ********************* */
 /* ************************************************** */
 
-int LZ4IO_compressFilename  (char* input_filename, char* output_filename, int compressionlevel);
-int LZ4IO_decompressFilename(char* input_filename, char* output_filename);
+int LZ4IO_compressFilename  (const char* input_filename, const char* output_filename, int compressionlevel);
+int LZ4IO_decompressFilename(const char* input_filename, const char* output_filename);
+
+int LZ4IO_compressMultipleFilenames(const char** inFileNamesTable, int ifntSize, const char* suffix, int compressionlevel);
 
 
 /* ************************************************** */