Home | History | Annotate | Download | only in examples
      1 /* LZ4frame API example : compress a file
      2  * Modified from an example code by Zbigniew Jdrzejewski-Szmek
      3  *
      4  * This example streams an input file into an output file
      5  * using a bounded memory budget.
      6  * Input is read in chunks of IN_CHUNK_SIZE */
      7 
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <errno.h>
     12 #include <assert.h>
     13 
     14 #include <lz4frame.h>
     15 
     16 
     17 #define IN_CHUNK_SIZE  (16*1024)
     18 
     19 static const LZ4F_preferences_t kPrefs = {
     20     { LZ4F_max256KB, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame,
     21       0 /* unknown content size */, 0 /* no dictID */ , LZ4F_noBlockChecksum },
     22     0,   /* compression level; 0 == default */
     23     0,   /* autoflush */
     24     0,   /* favor decompression speed */
     25     { 0, 0, 0 },  /* reserved, must be set to 0 */
     26 };
     27 
     28 
     29 /* safe_fwrite() :
     30  * performs fwrite(), ensure operation success, or immediately exit() */
     31 static void safe_fwrite(void* buf, size_t eltSize, size_t nbElt, FILE* f)
     32 {
     33     size_t const writtenSize = fwrite(buf, eltSize, nbElt, f);
     34     size_t const expectedSize = eltSize * nbElt;
     35     assert(expectedSize / nbElt == eltSize);   /* check overflow */
     36     if (writtenSize < expectedSize) {
     37         if (ferror(f))  /* note : ferror() must follow fwrite */
     38             fprintf(stderr, "Write failed \n");
     39         else
     40             fprintf(stderr, "Short write \n");
     41         exit(1);
     42     }
     43 }
     44 
     45 
     46 /* ================================================= */
     47 /*     Streaming Compression example               */
     48 /* ================================================= */
     49 
     50 typedef struct {
     51     int error;
     52     unsigned long long size_in;
     53     unsigned long long size_out;
     54 } compressResult_t;
     55 
     56 static compressResult_t
     57 compress_file_internal(FILE* f_in, FILE* f_out,
     58                        LZ4F_compressionContext_t ctx,
     59                        void* inBuff,  size_t inChunkSize,
     60                        void* outBuff, size_t outCapacity)
     61 {
     62     compressResult_t result = { 1, 0, 0 };  /* result for an error */
     63     unsigned long long count_in = 0, count_out;
     64 
     65     assert(f_in != NULL); assert(f_out != NULL);
     66     assert(ctx != NULL);
     67     assert(outCapacity >= LZ4F_HEADER_SIZE_MAX);
     68     assert(outCapacity >= LZ4F_compressBound(inChunkSize, &kPrefs));
     69 
     70     /* write frame header */
     71     {   size_t const headerSize = LZ4F_compressBegin(ctx, outBuff, outCapacity, &kPrefs);
     72         if (LZ4F_isError(headerSize)) {
     73             printf("Failed to start compression: error %zu\n", headerSize);
     74             return result;
     75         }
     76         count_out = headerSize;
     77         printf("Buffer size is %zu bytes, header size %zu bytes\n", outCapacity, headerSize);
     78         safe_fwrite(outBuff, 1, headerSize, f_out);
     79     }
     80 
     81     /* stream file */
     82     for (;;) {
     83         size_t const readSize = fread(inBuff, 1, IN_CHUNK_SIZE, f_in);
     84         if (readSize == 0) break; /* nothing left to read from input file */
     85         count_in += readSize;
     86 
     87         size_t const compressedSize = LZ4F_compressUpdate(ctx,
     88                                                 outBuff, outCapacity,
     89                                                 inBuff, readSize,
     90                                                 NULL);
     91         if (LZ4F_isError(compressedSize)) {
     92             printf("Compression failed: error %zu\n", compressedSize);
     93             return result;
     94         }
     95 
     96         printf("Writing %zu bytes\n", compressedSize);
     97         safe_fwrite(outBuff, 1, compressedSize, f_out);
     98         count_out += compressedSize;
     99     }
    100 
    101     /* flush whatever remains within internal buffers */
    102     {   size_t const compressedSize = LZ4F_compressEnd(ctx,
    103                                                 outBuff, outCapacity,
    104                                                 NULL);
    105         if (LZ4F_isError(compressedSize)) {
    106             printf("Failed to end compression: error %zu\n", compressedSize);
    107             return result;
    108         }
    109 
    110         printf("Writing %zu bytes\n", compressedSize);
    111         safe_fwrite(outBuff, 1, compressedSize, f_out);
    112         count_out += compressedSize;
    113     }
    114 
    115     result.size_in = count_in;
    116     result.size_out = count_out;
    117     result.error = 0;
    118     return result;
    119 }
    120 
    121 static compressResult_t
    122 compress_file(FILE* f_in, FILE* f_out)
    123 {
    124     assert(f_in != NULL);
    125     assert(f_out != NULL);
    126 
    127     /* ressource allocation */
    128     LZ4F_compressionContext_t ctx;
    129     size_t const ctxCreation = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
    130     void* const src = malloc(IN_CHUNK_SIZE);
    131     size_t const outbufCapacity = LZ4F_compressBound(IN_CHUNK_SIZE, &kPrefs);   /* large enough for any input <= IN_CHUNK_SIZE */
    132     void* const outbuff = malloc(outbufCapacity);
    133 
    134     compressResult_t result = { 1, 0, 0 };  /* == error (default) */
    135     if (!LZ4F_isError(ctxCreation) && src && outbuff) {
    136         result = compress_file_internal(f_in, f_out,
    137                                         ctx,
    138                                         src, IN_CHUNK_SIZE,
    139                                         outbuff, outbufCapacity);
    140     } else {
    141         printf("error : ressource allocation failed \n");
    142     }
    143 
    144     LZ4F_freeCompressionContext(ctx);   /* supports free on NULL */
    145     free(src);
    146     free(outbuff);
    147     return result;
    148 }
    149 
    150 
    151 /* ================================================= */
    152 /*     Streaming decompression example               */
    153 /* ================================================= */
    154 
    155 static size_t get_block_size(const LZ4F_frameInfo_t* info) {
    156     switch (info->blockSizeID) {
    157         case LZ4F_default:
    158         case LZ4F_max64KB:  return 1 << 16;
    159         case LZ4F_max256KB: return 1 << 18;
    160         case LZ4F_max1MB:   return 1 << 20;
    161         case LZ4F_max4MB:   return 1 << 22;
    162         default:
    163             printf("Impossible with expected frame specification (<=v1.6.1)\n");
    164             exit(1);
    165     }
    166 }
    167 
    168 /* @return : 1==error, 0==success */
    169 static int
    170 decompress_file_internal(FILE* f_in, FILE* f_out,
    171                          LZ4F_dctx* dctx,
    172                          void* src, size_t srcCapacity, size_t filled, size_t alreadyConsumed,
    173                          void* dst, size_t dstCapacity)
    174 {
    175     int firstChunk = 1;
    176     size_t ret = 1;
    177 
    178     assert(f_in != NULL); assert(f_out != NULL);
    179     assert(dctx != NULL);
    180     assert(src != NULL); assert(srcCapacity > 0); assert(filled <= srcCapacity); assert(alreadyConsumed <= filled);
    181     assert(dst != NULL); assert(dstCapacity > 0);
    182 
    183     /* Decompression */
    184     while (ret != 0) {
    185         /* Load more input */
    186         size_t readSize = firstChunk ? filled : fread(src, 1, srcCapacity, f_in); firstChunk=0;
    187         const void* srcPtr = src + alreadyConsumed; alreadyConsumed=0;
    188         const void* const srcEnd = srcPtr + readSize;
    189         if (readSize == 0 || ferror(f_in)) {
    190             printf("Decompress: not enough input or error reading file\n");
    191             return 1;
    192         }
    193 
    194         /* Decompress:
    195          * Continue while there is more input to read (srcPtr != srcEnd)
    196          * and the frame isn't over (ret != 0)
    197          */
    198         while (srcPtr < srcEnd && ret != 0) {
    199             /* Any data within dst has been flushed at this stage */
    200             size_t dstSize = dstCapacity;
    201             size_t srcSize = srcEnd - srcPtr;
    202             ret = LZ4F_decompress(dctx, dst, &dstSize, srcPtr, &srcSize, /* LZ4F_decompressOptions_t */ NULL);
    203             if (LZ4F_isError(ret)) {
    204                 printf("Decompression error: %s\n", LZ4F_getErrorName(ret));
    205                 return 1;
    206             }
    207             /* Flush output */
    208             if (dstSize != 0) safe_fwrite(dst, 1, dstSize, f_out);
    209             /* Update input */
    210             srcPtr += srcSize;
    211         }
    212 
    213         assert(srcPtr <= srcEnd);
    214 
    215         /* Ensure all input data has been consumed.
    216          * It is valid to have multiple frames in the same file,
    217          * but this example only supports one frame.
    218          */
    219         if (srcPtr < srcEnd) {
    220             printf("Decompress: Trailing data left in file after frame\n");
    221             return 1;
    222         }
    223     }
    224 
    225     /* Check that there isn't trailing data in the file after the frame.
    226      * It is valid to have multiple frames in the same file,
    227      * but this example only supports one frame.
    228      */
    229     {   size_t const readSize = fread(src, 1, 1, f_in);
    230         if (readSize != 0 || !feof(f_in)) {
    231             printf("Decompress: Trailing data left in file after frame\n");
    232             return 1;
    233     }   }
    234 
    235     return 0;
    236 }
    237 
    238 
    239 /* @return : 1==error, 0==completed */
    240 static int
    241 decompress_file_allocDst(FILE* f_in, FILE* f_out,
    242                         LZ4F_dctx* dctx,
    243                         void* src, size_t srcCapacity)
    244 {
    245     assert(f_in != NULL); assert(f_out != NULL);
    246     assert(dctx != NULL);
    247     assert(src != NULL);
    248     assert(srcCapacity >= LZ4F_HEADER_SIZE_MAX);  /* ensure LZ4F_getFrameInfo() can read enough data */
    249 
    250     /* Read Frame header */
    251     size_t const readSize = fread(src, 1, srcCapacity, f_in);
    252     if (readSize == 0 || ferror(f_in)) {
    253         printf("Decompress: not enough input or error reading file\n");
    254         return 1;
    255     }
    256 
    257     LZ4F_frameInfo_t info;
    258     size_t consumedSize = readSize;
    259     {   size_t const fires = LZ4F_getFrameInfo(dctx, &info, src, &consumedSize);
    260         if (LZ4F_isError(fires)) {
    261             printf("LZ4F_getFrameInfo error: %s\n", LZ4F_getErrorName(fires));
    262             return 1;
    263     }   }
    264 
    265     /* Allocating enough space for an entire block isn't necessary for
    266      * correctness, but it allows some memcpy's to be elided.
    267      */
    268     size_t const dstCapacity = get_block_size(&info);
    269     void* const dst = malloc(dstCapacity);
    270     if (!dst) { perror("decompress_file(dst)"); return 1; }
    271 
    272     int const decompressionResult = decompress_file_internal(
    273                         f_in, f_out,
    274                         dctx,
    275                         src, srcCapacity, readSize-consumedSize, consumedSize,
    276                         dst, dstCapacity);
    277 
    278     free(dst);
    279     return decompressionResult;
    280 }
    281 
    282 
    283 /* @result : 1==error, 0==success */
    284 static int decompress_file(FILE* f_in, FILE* f_out)
    285 {
    286     assert(f_in != NULL); assert(f_out != NULL);
    287 
    288     /* Ressource allocation */
    289     void* const src = malloc(IN_CHUNK_SIZE);
    290     if (!src) { perror("decompress_file(src)"); return 1; }
    291 
    292     LZ4F_dctx* dctx;
    293     {   size_t const dctxStatus = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
    294         if (LZ4F_isError(dctxStatus)) {
    295             printf("LZ4F_dctx creation error: %s\n", LZ4F_getErrorName(dctxStatus));
    296     }   }
    297 
    298     int const result = !dctx ? 1 /* error */ :
    299                        decompress_file_allocDst(f_in, f_out, dctx, src, IN_CHUNK_SIZE);
    300 
    301     free(src);
    302     LZ4F_freeDecompressionContext(dctx);   /* note : free works on NULL */
    303     return result;
    304 }
    305 
    306 
    307 int compareFiles(FILE* fp0, FILE* fp1)
    308 {
    309     int result = 0;
    310 
    311     while (result==0) {
    312         char b0[1024];
    313         char b1[1024];
    314         size_t const r0 = fread(b0, 1, sizeof(b0), fp0);
    315         size_t const r1 = fread(b1, 1, sizeof(b1), fp1);
    316 
    317         result = (r0 != r1);
    318         if (!r0 || !r1) break;
    319         if (!result) result = memcmp(b0, b1, r0);
    320     }
    321 
    322     return result;
    323 }
    324 
    325 
    326 int main(int argc, const char **argv) {
    327     char inpFilename[256] = { 0 };
    328     char lz4Filename[256] = { 0 };
    329     char decFilename[256] = { 0 };
    330 
    331     if (argc < 2) {
    332         printf("Please specify input filename\n");
    333         return 0;
    334     }
    335 
    336     snprintf(inpFilename, 256, "%s", argv[1]);
    337     snprintf(lz4Filename, 256, "%s.lz4", argv[1]);
    338     snprintf(decFilename, 256, "%s.lz4.dec", argv[1]);
    339 
    340     printf("inp = [%s]\n", inpFilename);
    341     printf("lz4 = [%s]\n", lz4Filename);
    342     printf("dec = [%s]\n", decFilename);
    343 
    344     /* compress */
    345     {   FILE* const inpFp = fopen(inpFilename, "rb");
    346         FILE* const outFp = fopen(lz4Filename, "wb");
    347 
    348         printf("compress : %s -> %s\n", inpFilename, lz4Filename);
    349         compressResult_t const ret = compress_file(inpFp, outFp);
    350 
    351         fclose(outFp);
    352         fclose(inpFp);
    353 
    354         if (ret.error) {
    355             printf("compress : failed with code %i\n", ret.error);
    356             return ret.error;
    357         }
    358         printf("%s: %zu  %zu bytes, %.1f%%\n",
    359             inpFilename,
    360             (size_t)ret.size_in, (size_t)ret.size_out,  /* might overflow is size_t is 32 bits and size_{in,out} > 4 GB */
    361             (double)ret.size_out / ret.size_in * 100);
    362         printf("compress : done\n");
    363     }
    364 
    365     /* decompress */
    366     {   FILE* const inpFp = fopen(lz4Filename, "rb");
    367         FILE* const outFp = fopen(decFilename, "wb");
    368 
    369         printf("decompress : %s -> %s\n", lz4Filename, decFilename);
    370         int const ret = decompress_file(inpFp, outFp);
    371 
    372         fclose(outFp);
    373         fclose(inpFp);
    374 
    375         if (ret) {
    376             printf("decompress : failed with code %i\n", ret);
    377             return ret;
    378         }
    379         printf("decompress : done\n");
    380     }
    381 
    382     /* verify */
    383     {   FILE* const inpFp = fopen(inpFilename, "rb");
    384         FILE* const decFp = fopen(decFilename, "rb");
    385 
    386         printf("verify : %s <-> %s\n", inpFilename, decFilename);
    387         int const cmp = compareFiles(inpFp, decFp);
    388 
    389         fclose(decFp);
    390         fclose(inpFp);
    391 
    392         if (cmp) {
    393             printf("corruption detected : decompressed file differs from original\n");
    394             return cmp;
    395         }
    396         printf("verify : OK\n");
    397     }
    398 
    399     return 0;
    400 }
    401