• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

contrib/zlib/examples/fitblk.c

00001 /* fitblk.c: example of fitting compressed output to a specified size
00002    Not copyrighted -- provided to the public domain
00003    Version 1.1  25 November 2004  Mark Adler */
00004 
00005 /* Version history:
00006    1.0  24 Nov 2004  First version
00007    1.1  25 Nov 2004  Change deflateInit2() to deflateInit()
00008                      Use fixed-size, stack-allocated raw buffers
00009                      Simplify code moving compression to subroutines
00010                      Use assert() for internal errors
00011                      Add detailed description of approach
00012  */
00013 
00014 /* Approach to just fitting a requested compressed size:
00015 
00016    fitblk performs three compression passes on a portion of the input
00017    data in order to determine how much of that input will compress to
00018    nearly the requested output block size.  The first pass generates
00019    enough deflate blocks to produce output to fill the requested
00020    output size plus a specfied excess amount (see the EXCESS define
00021    below).  The last deflate block may go quite a bit past that, but
00022    is discarded.  The second pass decompresses and recompresses just
00023    the compressed data that fit in the requested plus excess sized
00024    buffer.  The deflate process is terminated after that amount of
00025    input, which is less than the amount consumed on the first pass.
00026    The last deflate block of the result will be of a comparable size
00027    to the final product, so that the header for that deflate block and
00028    the compression ratio for that block will be about the same as in
00029    the final product.  The third compression pass decompresses the
00030    result of the second step, but only the compressed data up to the
00031    requested size minus an amount to allow the compressed stream to
00032    complete (see the MARGIN define below).  That will result in a
00033    final compressed stream whose length is less than or equal to the
00034    requested size.  Assuming sufficient input and a requested size
00035    greater than a few hundred bytes, the shortfall will typically be
00036    less than ten bytes.
00037 
00038    If the input is short enough that the first compression completes
00039    before filling the requested output size, then that compressed
00040    stream is return with no recompression.
00041 
00042    EXCESS is chosen to be just greater than the shortfall seen in a
00043    two pass approach similar to the above.  That shortfall is due to
00044    the last deflate block compressing more efficiently with a smaller
00045    header on the second pass.  EXCESS is set to be large enough so
00046    that there is enough uncompressed data for the second pass to fill
00047    out the requested size, and small enough so that the final deflate
00048    block of the second pass will be close in size to the final deflate
00049    block of the third and final pass.  MARGIN is chosen to be just
00050    large enough to assure that the final compression has enough room
00051    to complete in all cases.
00052  */
00053 
00054 #include <stdio.h>
00055 #include <stdlib.h>
00056 #include <assert.h>
00057 #include "zlib.h"
00058 
00059 #define local static
00060 
00061 /* print nastygram and leave */
00062 local void quit(char *why)
00063 {
00064     fprintf(stderr, "fitblk abort: %s\n", why);
00065     exit(1);
00066 }
00067 
00068 #define RAWLEN 4096    /* intermediate uncompressed buffer size */
00069 
00070 /* compress from file to def until provided buffer is full or end of
00071    input reached; return last deflate() return value, or Z_ERRNO if
00072    there was read error on the file */
00073 local int partcompress(FILE *in, z_streamp def)
00074 {
00075     int ret, flush;
00076     unsigned char raw[RAWLEN];
00077 
00078     flush = Z_NO_FLUSH;
00079     do {
00080         def->avail_in = fread(raw, 1, RAWLEN, in);
00081         if (ferror(in))
00082             return Z_ERRNO;
00083         def->next_in = raw;
00084         if (feof(in))
00085             flush = Z_FINISH;
00086         ret = deflate(def, flush);
00087         assert(ret != Z_STREAM_ERROR);
00088     } while (def->avail_out != 0 && flush == Z_NO_FLUSH);
00089     return ret;
00090 }
00091 
00092 /* recompress from inf's input to def's output; the input for inf and
00093    the output for def are set in those structures before calling;
00094    return last deflate() return value, or Z_MEM_ERROR if inflate()
00095    was not able to allocate enough memory when it needed to */
00096 local int recompress(z_streamp inf, z_streamp def)
00097 {
00098     int ret, flush;
00099     unsigned char raw[RAWLEN];
00100 
00101     flush = Z_NO_FLUSH;
00102     do {
00103         /* decompress */
00104         inf->avail_out = RAWLEN;
00105         inf->next_out = raw;
00106         ret = inflate(inf, Z_NO_FLUSH);
00107         assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR &&
00108                ret != Z_NEED_DICT);
00109         if (ret == Z_MEM_ERROR)
00110             return ret;
00111 
00112         /* compress what was decompresed until done or no room */
00113         def->avail_in = RAWLEN - inf->avail_out;
00114         def->next_in = raw;
00115         if (inf->avail_out != 0)
00116             flush = Z_FINISH;
00117         ret = deflate(def, flush);
00118         assert(ret != Z_STREAM_ERROR);
00119     } while (ret != Z_STREAM_END && def->avail_out != 0);
00120     return ret;
00121 }
00122 
00123 #define EXCESS 256      /* empirically determined stream overage */
00124 #define MARGIN 8        /* amount to back off for completion */
00125 
00126 /* compress from stdin to fixed-size block on stdout */
00127 int main(int argc, char **argv)
00128 {
00129     int ret;                /* return code */
00130     unsigned size;          /* requested fixed output block size */
00131     unsigned have;          /* bytes written by deflate() call */
00132     unsigned char *blk;     /* intermediate and final stream */
00133     unsigned char *tmp;     /* close to desired size stream */
00134     z_stream def, inf;      /* zlib deflate and inflate states */
00135 
00136     /* get requested output size */
00137     if (argc != 2)
00138         quit("need one argument: size of output block");
00139     ret = strtol(argv[1], argv + 1, 10);
00140     if (argv[1][0] != 0)
00141         quit("argument must be a number");
00142     if (ret < 8)            /* 8 is minimum zlib stream size */
00143         quit("need positive size of 8 or greater");
00144     size = (unsigned)ret;
00145 
00146     /* allocate memory for buffers and compression engine */
00147     blk = malloc(size + EXCESS);
00148     def.zalloc = Z_NULL;
00149     def.zfree = Z_NULL;
00150     def.opaque = Z_NULL;
00151     ret = deflateInit(&def, Z_DEFAULT_COMPRESSION);
00152     if (ret != Z_OK || blk == NULL)
00153         quit("out of memory");
00154 
00155     /* compress from stdin until output full, or no more input */
00156     def.avail_out = size + EXCESS;
00157     def.next_out = blk;
00158     ret = partcompress(stdin, &def);
00159     if (ret == Z_ERRNO)
00160         quit("error reading input");
00161 
00162     /* if it all fit, then size was undersubscribed -- done! */
00163     if (ret == Z_STREAM_END && def.avail_out >= EXCESS) {
00164         /* write block to stdout */
00165         have = size + EXCESS - def.avail_out;
00166         if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
00167             quit("error writing output");
00168 
00169         /* clean up and print results to stderr */
00170         ret = deflateEnd(&def);
00171         assert(ret != Z_STREAM_ERROR);
00172         free(blk);
00173         fprintf(stderr,
00174                 "%u bytes unused out of %u requested (all input)\n",
00175                 size - have, size);
00176         return 0;
00177     }
00178 
00179     /* it didn't all fit -- set up for recompression */
00180     inf.zalloc = Z_NULL;
00181     inf.zfree = Z_NULL;
00182     inf.opaque = Z_NULL;
00183     inf.avail_in = 0;
00184     inf.next_in = Z_NULL;
00185     ret = inflateInit(&inf);
00186     tmp = malloc(size + EXCESS);
00187     if (ret != Z_OK || tmp == NULL)
00188         quit("out of memory");
00189     ret = deflateReset(&def);
00190     assert(ret != Z_STREAM_ERROR);
00191 
00192     /* do first recompression close to the right amount */
00193     inf.avail_in = size + EXCESS;
00194     inf.next_in = blk;
00195     def.avail_out = size + EXCESS;
00196     def.next_out = tmp;
00197     ret = recompress(&inf, &def);
00198     if (ret == Z_MEM_ERROR)
00199         quit("out of memory");
00200 
00201     /* set up for next reocmpression */
00202     ret = inflateReset(&inf);
00203     assert(ret != Z_STREAM_ERROR);
00204     ret = deflateReset(&def);
00205     assert(ret != Z_STREAM_ERROR);
00206 
00207     /* do second and final recompression (third compression) */
00208     inf.avail_in = size - MARGIN;   /* assure stream will complete */
00209     inf.next_in = tmp;
00210     def.avail_out = size;
00211     def.next_out = blk;
00212     ret = recompress(&inf, &def);
00213     if (ret == Z_MEM_ERROR)
00214         quit("out of memory");
00215     assert(ret == Z_STREAM_END);    /* otherwise MARGIN too small */
00216 
00217     /* done -- write block to stdout */
00218     have = size - def.avail_out;
00219     if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
00220         quit("error writing output");
00221 
00222     /* clean up and print results to stderr */
00223     free(tmp);
00224     ret = inflateEnd(&inf);
00225     assert(ret != Z_STREAM_ERROR);
00226     ret = deflateEnd(&def);
00227     assert(ret != Z_STREAM_ERROR);
00228     free(blk);
00229     fprintf(stderr,
00230             "%u bytes unused out of %u requested (%lu input)\n",
00231             size - have, size, def.total_in);
00232     return 0;
00233 }

Generated on Wed Oct 20 2010 11:12:17 for APBS by  doxygen 1.7.2