Home | History | Annotate | Line # | Download | only in examples
      1      1.1  christos /* fitblk.c: example of fitting compressed output to a specified size
      2      1.1  christos    Not copyrighted -- provided to the public domain
      3      1.1  christos    Version 1.1  25 November 2004  Mark Adler */
      4      1.1  christos 
      5      1.1  christos /* Version history:
      6      1.1  christos    1.0  24 Nov 2004  First version
      7      1.1  christos    1.1  25 Nov 2004  Change deflateInit2() to deflateInit()
      8      1.1  christos                      Use fixed-size, stack-allocated raw buffers
      9      1.1  christos                      Simplify code moving compression to subroutines
     10      1.1  christos                      Use assert() for internal errors
     11      1.1  christos                      Add detailed description of approach
     12      1.1  christos  */
     13      1.1  christos 
     14      1.1  christos /* Approach to just fitting a requested compressed size:
     15      1.1  christos 
     16      1.1  christos    fitblk performs three compression passes on a portion of the input
     17      1.1  christos    data in order to determine how much of that input will compress to
     18      1.1  christos    nearly the requested output block size.  The first pass generates
     19      1.1  christos    enough deflate blocks to produce output to fill the requested
     20  1.1.1.3  christos    output size plus a specified excess amount (see the EXCESS define
     21      1.1  christos    below).  The last deflate block may go quite a bit past that, but
     22      1.1  christos    is discarded.  The second pass decompresses and recompresses just
     23      1.1  christos    the compressed data that fit in the requested plus excess sized
     24      1.1  christos    buffer.  The deflate process is terminated after that amount of
     25      1.1  christos    input, which is less than the amount consumed on the first pass.
     26      1.1  christos    The last deflate block of the result will be of a comparable size
     27      1.1  christos    to the final product, so that the header for that deflate block and
     28      1.1  christos    the compression ratio for that block will be about the same as in
     29      1.1  christos    the final product.  The third compression pass decompresses the
     30      1.1  christos    result of the second step, but only the compressed data up to the
     31      1.1  christos    requested size minus an amount to allow the compressed stream to
     32      1.1  christos    complete (see the MARGIN define below).  That will result in a
     33      1.1  christos    final compressed stream whose length is less than or equal to the
     34      1.1  christos    requested size.  Assuming sufficient input and a requested size
     35      1.1  christos    greater than a few hundred bytes, the shortfall will typically be
     36      1.1  christos    less than ten bytes.
     37      1.1  christos 
     38      1.1  christos    If the input is short enough that the first compression completes
     39      1.1  christos    before filling the requested output size, then that compressed
     40      1.1  christos    stream is return with no recompression.
     41      1.1  christos 
     42      1.1  christos    EXCESS is chosen to be just greater than the shortfall seen in a
     43      1.1  christos    two pass approach similar to the above.  That shortfall is due to
     44      1.1  christos    the last deflate block compressing more efficiently with a smaller
     45      1.1  christos    header on the second pass.  EXCESS is set to be large enough so
     46      1.1  christos    that there is enough uncompressed data for the second pass to fill
     47      1.1  christos    out the requested size, and small enough so that the final deflate
     48      1.1  christos    block of the second pass will be close in size to the final deflate
     49      1.1  christos    block of the third and final pass.  MARGIN is chosen to be just
     50      1.1  christos    large enough to assure that the final compression has enough room
     51      1.1  christos    to complete in all cases.
     52      1.1  christos  */
     53      1.1  christos 
     54      1.1  christos #include <stdio.h>
     55      1.1  christos #include <stdlib.h>
     56      1.1  christos #include <assert.h>
     57      1.1  christos #include "zlib.h"
     58      1.1  christos 
     59      1.1  christos #define local static
     60      1.1  christos 
     61      1.1  christos /* print nastygram and leave */
     62      1.1  christos local void quit(char *why)
     63      1.1  christos {
     64      1.1  christos     fprintf(stderr, "fitblk abort: %s\n", why);
     65      1.1  christos     exit(1);
     66      1.1  christos }
     67      1.1  christos 
     68      1.1  christos #define RAWLEN 4096    /* intermediate uncompressed buffer size */
     69      1.1  christos 
     70      1.1  christos /* compress from file to def until provided buffer is full or end of
     71      1.1  christos    input reached; return last deflate() return value, or Z_ERRNO if
     72      1.1  christos    there was read error on the file */
     73      1.1  christos local int partcompress(FILE *in, z_streamp def)
     74      1.1  christos {
     75      1.1  christos     int ret, flush;
     76      1.1  christos     unsigned char raw[RAWLEN];
     77      1.1  christos 
     78      1.1  christos     flush = Z_NO_FLUSH;
     79      1.1  christos     do {
     80      1.1  christos         def->avail_in = fread(raw, 1, RAWLEN, in);
     81      1.1  christos         if (ferror(in))
     82      1.1  christos             return Z_ERRNO;
     83      1.1  christos         def->next_in = raw;
     84      1.1  christos         if (feof(in))
     85      1.1  christos             flush = Z_FINISH;
     86      1.1  christos         ret = deflate(def, flush);
     87      1.1  christos         assert(ret != Z_STREAM_ERROR);
     88      1.1  christos     } while (def->avail_out != 0 && flush == Z_NO_FLUSH);
     89      1.1  christos     return ret;
     90      1.1  christos }
     91      1.1  christos 
     92      1.1  christos /* recompress from inf's input to def's output; the input for inf and
     93      1.1  christos    the output for def are set in those structures before calling;
     94      1.1  christos    return last deflate() return value, or Z_MEM_ERROR if inflate()
     95      1.1  christos    was not able to allocate enough memory when it needed to */
     96      1.1  christos local int recompress(z_streamp inf, z_streamp def)
     97      1.1  christos {
     98      1.1  christos     int ret, flush;
     99      1.1  christos     unsigned char raw[RAWLEN];
    100      1.1  christos 
    101      1.1  christos     flush = Z_NO_FLUSH;
    102      1.1  christos     do {
    103      1.1  christos         /* decompress */
    104      1.1  christos         inf->avail_out = RAWLEN;
    105      1.1  christos         inf->next_out = raw;
    106      1.1  christos         ret = inflate(inf, Z_NO_FLUSH);
    107      1.1  christos         assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR &&
    108      1.1  christos                ret != Z_NEED_DICT);
    109      1.1  christos         if (ret == Z_MEM_ERROR)
    110      1.1  christos             return ret;
    111      1.1  christos 
    112  1.1.1.3  christos         /* compress what was decompressed until done or no room */
    113      1.1  christos         def->avail_in = RAWLEN - inf->avail_out;
    114      1.1  christos         def->next_in = raw;
    115      1.1  christos         if (inf->avail_out != 0)
    116      1.1  christos             flush = Z_FINISH;
    117      1.1  christos         ret = deflate(def, flush);
    118      1.1  christos         assert(ret != Z_STREAM_ERROR);
    119      1.1  christos     } while (ret != Z_STREAM_END && def->avail_out != 0);
    120      1.1  christos     return ret;
    121      1.1  christos }
    122      1.1  christos 
    123      1.1  christos #define EXCESS 256      /* empirically determined stream overage */
    124      1.1  christos #define MARGIN 8        /* amount to back off for completion */
    125      1.1  christos 
    126      1.1  christos /* compress from stdin to fixed-size block on stdout */
    127      1.1  christos int main(int argc, char **argv)
    128      1.1  christos {
    129      1.1  christos     int ret;                /* return code */
    130      1.1  christos     unsigned size;          /* requested fixed output block size */
    131      1.1  christos     unsigned have;          /* bytes written by deflate() call */
    132      1.1  christos     unsigned char *blk;     /* intermediate and final stream */
    133      1.1  christos     unsigned char *tmp;     /* close to desired size stream */
    134      1.1  christos     z_stream def, inf;      /* zlib deflate and inflate states */
    135      1.1  christos 
    136      1.1  christos     /* get requested output size */
    137      1.1  christos     if (argc != 2)
    138      1.1  christos         quit("need one argument: size of output block");
    139      1.1  christos     ret = strtol(argv[1], argv + 1, 10);
    140      1.1  christos     if (argv[1][0] != 0)
    141      1.1  christos         quit("argument must be a number");
    142      1.1  christos     if (ret < 8)            /* 8 is minimum zlib stream size */
    143      1.1  christos         quit("need positive size of 8 or greater");
    144      1.1  christos     size = (unsigned)ret;
    145      1.1  christos 
    146      1.1  christos     /* allocate memory for buffers and compression engine */
    147      1.1  christos     blk = malloc(size + EXCESS);
    148      1.1  christos     def.zalloc = Z_NULL;
    149      1.1  christos     def.zfree = Z_NULL;
    150      1.1  christos     def.opaque = Z_NULL;
    151      1.1  christos     ret = deflateInit(&def, Z_DEFAULT_COMPRESSION);
    152      1.1  christos     if (ret != Z_OK || blk == NULL)
    153      1.1  christos         quit("out of memory");
    154      1.1  christos 
    155      1.1  christos     /* compress from stdin until output full, or no more input */
    156      1.1  christos     def.avail_out = size + EXCESS;
    157      1.1  christos     def.next_out = blk;
    158      1.1  christos     ret = partcompress(stdin, &def);
    159      1.1  christos     if (ret == Z_ERRNO)
    160      1.1  christos         quit("error reading input");
    161      1.1  christos 
    162      1.1  christos     /* if it all fit, then size was undersubscribed -- done! */
    163      1.1  christos     if (ret == Z_STREAM_END && def.avail_out >= EXCESS) {
    164      1.1  christos         /* write block to stdout */
    165      1.1  christos         have = size + EXCESS - def.avail_out;
    166      1.1  christos         if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
    167      1.1  christos             quit("error writing output");
    168      1.1  christos 
    169      1.1  christos         /* clean up and print results to stderr */
    170      1.1  christos         ret = deflateEnd(&def);
    171      1.1  christos         assert(ret != Z_STREAM_ERROR);
    172      1.1  christos         free(blk);
    173      1.1  christos         fprintf(stderr,
    174      1.1  christos                 "%u bytes unused out of %u requested (all input)\n",
    175      1.1  christos                 size - have, size);
    176      1.1  christos         return 0;
    177      1.1  christos     }
    178      1.1  christos 
    179      1.1  christos     /* it didn't all fit -- set up for recompression */
    180      1.1  christos     inf.zalloc = Z_NULL;
    181      1.1  christos     inf.zfree = Z_NULL;
    182      1.1  christos     inf.opaque = Z_NULL;
    183      1.1  christos     inf.avail_in = 0;
    184      1.1  christos     inf.next_in = Z_NULL;
    185      1.1  christos     ret = inflateInit(&inf);
    186      1.1  christos     tmp = malloc(size + EXCESS);
    187      1.1  christos     if (ret != Z_OK || tmp == NULL)
    188      1.1  christos         quit("out of memory");
    189      1.1  christos     ret = deflateReset(&def);
    190      1.1  christos     assert(ret != Z_STREAM_ERROR);
    191      1.1  christos 
    192      1.1  christos     /* do first recompression close to the right amount */
    193      1.1  christos     inf.avail_in = size + EXCESS;
    194      1.1  christos     inf.next_in = blk;
    195      1.1  christos     def.avail_out = size + EXCESS;
    196      1.1  christos     def.next_out = tmp;
    197      1.1  christos     ret = recompress(&inf, &def);
    198      1.1  christos     if (ret == Z_MEM_ERROR)
    199      1.1  christos         quit("out of memory");
    200      1.1  christos 
    201  1.1.1.4  christos     /* set up for next recompression */
    202      1.1  christos     ret = inflateReset(&inf);
    203      1.1  christos     assert(ret != Z_STREAM_ERROR);
    204      1.1  christos     ret = deflateReset(&def);
    205      1.1  christos     assert(ret != Z_STREAM_ERROR);
    206      1.1  christos 
    207      1.1  christos     /* do second and final recompression (third compression) */
    208      1.1  christos     inf.avail_in = size - MARGIN;   /* assure stream will complete */
    209      1.1  christos     inf.next_in = tmp;
    210      1.1  christos     def.avail_out = size;
    211      1.1  christos     def.next_out = blk;
    212      1.1  christos     ret = recompress(&inf, &def);
    213      1.1  christos     if (ret == Z_MEM_ERROR)
    214      1.1  christos         quit("out of memory");
    215      1.1  christos     assert(ret == Z_STREAM_END);    /* otherwise MARGIN too small */
    216      1.1  christos 
    217      1.1  christos     /* done -- write block to stdout */
    218      1.1  christos     have = size - def.avail_out;
    219      1.1  christos     if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
    220      1.1  christos         quit("error writing output");
    221      1.1  christos 
    222      1.1  christos     /* clean up and print results to stderr */
    223      1.1  christos     free(tmp);
    224      1.1  christos     ret = inflateEnd(&inf);
    225      1.1  christos     assert(ret != Z_STREAM_ERROR);
    226      1.1  christos     ret = deflateEnd(&def);
    227      1.1  christos     assert(ret != Z_STREAM_ERROR);
    228      1.1  christos     free(blk);
    229      1.1  christos     fprintf(stderr,
    230      1.1  christos             "%u bytes unused out of %u requested (%lu input)\n",
    231      1.1  christos             size - have, size, def.total_in);
    232      1.1  christos     return 0;
    233      1.1  christos }
    234