WvStreams
wvgzip.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Gzip encoder/decoder based on zlib.
00006  */
00007 #include "wvgzip.h"
00008 #include <zlib.h>
00009 #include <assert.h>
00010 
00011 #define ZBUFSIZE 10240
00012 
00013 
00014 WvGzipEncoder::WvGzipEncoder(Mode _mode, size_t _out_limit) :
00015     out_limit(_out_limit), tmpbuf(ZBUFSIZE), mode(_mode)
00016 {
00017     ignore_decompression_errors = false;
00018     full_flush = false;
00019     init();
00020 }
00021 
00022 
00023 WvGzipEncoder::~WvGzipEncoder()
00024 {
00025     close();
00026 }
00027 
00028 
00029 void WvGzipEncoder::init()
00030 {
00031     zstr = new z_stream;
00032     memset(zstr, 0, sizeof(*zstr));
00033     zstr->zalloc = Z_NULL;
00034     zstr->zfree = Z_NULL;
00035     zstr->opaque = NULL;
00036     zstr->msg = NULL;
00037     
00038     int retval;
00039     if (mode == Deflate)
00040         retval = deflateInit(zstr, Z_BEST_SPEED);
00041     else
00042         retval = inflateInit(zstr);
00043     
00044     if (retval != Z_OK)
00045     {
00046         seterror("error %s initializing gzip %s: %s", retval,
00047             mode == Deflate ? "compressor" : "decompressor",
00048             zstr->msg ? zstr->msg : "unknown");
00049         return;
00050     }
00051     zstr->next_in = zstr->next_out = NULL;
00052     zstr->avail_in = zstr->avail_out = 0;
00053 }
00054 
00055 void WvGzipEncoder::close()
00056 {
00057     if (mode == Deflate)
00058         deflateEnd(zstr);
00059     else
00060         inflateEnd(zstr);
00061 
00062     delete zstr;
00063 
00064 }
00065 
00066 bool WvGzipEncoder::_encode(WvBuf &inbuf, WvBuf &outbuf, bool flush)
00067 {
00068     bool success;
00069     output = 0;
00070     for (;;)
00071     {
00072         size_t starting_size = inbuf.used();
00073         prepare(& inbuf);
00074         bool alldata = inbuf.used() == 0;
00075         success = process(outbuf, flush && alldata, false);
00076         if (zstr->avail_in != 0)
00077         {
00078             // unget unused data
00079             inbuf.unget(zstr->avail_in);
00080             zstr->avail_in = 0;
00081         }
00082         if (! success)
00083             return false;
00084         if (alldata || (starting_size == inbuf.used()) ||
00085             (out_limit && (output >= out_limit)))
00086             return true;
00087     }
00088 }
00089 
00090 
00091 bool WvGzipEncoder::_finish(WvBuf &outbuf)
00092 {
00093     prepare(NULL);
00094     return process(outbuf, false, true);
00095 }
00096 
00097 
00098 bool WvGzipEncoder::_reset()
00099 {
00100     close();
00101     init();
00102     return true;
00103 }
00104 
00105 
00106 void WvGzipEncoder::prepare(WvBuf *inbuf)
00107 {
00108     assert(zstr->avail_in == 0);
00109     if (inbuf && inbuf->used() != 0)
00110     {
00111         size_t avail = inbuf->optgettable();
00112         zstr->avail_in = avail;
00113         zstr->next_in = const_cast<Bytef*>(
00114             (const Bytef*)inbuf->get(avail));
00115     }
00116     else
00117     {
00118         zstr->avail_in = 0;
00119         zstr->next_in = (Bytef*)""; // so it's not NULL
00120     }
00121 }
00122 
00123 
00124 bool WvGzipEncoder::process(WvBuf &outbuf, bool flush, bool finish)
00125 {
00126     int flushmode = finish ? Z_FINISH :
00127         flush ? (full_flush ? Z_FULL_FLUSH : Z_SYNC_FLUSH) : Z_NO_FLUSH;
00128     int retval;
00129     do
00130     {
00131         // process the next chunk
00132         tmpbuf.zap();
00133         size_t avail_out = tmpbuf.free();
00134         if (out_limit)
00135             avail_out = tmpbuf.free() < (out_limit - output) ? tmpbuf.free()
00136                 : (out_limit - output);
00137 
00138         zstr->avail_out = avail_out;
00139         zstr->next_out = tmpbuf.alloc(avail_out);
00140         if (mode == Deflate)
00141             retval = deflate(zstr, flushmode);
00142         else
00143             retval = inflate(zstr, flushmode);
00144         tmpbuf.unalloc(zstr->avail_out);
00145 
00146         output += avail_out - zstr->avail_out;
00147 
00148         // consume pending output
00149         outbuf.merge(tmpbuf);
00150 
00151         if (retval == Z_DATA_ERROR && mode == Inflate
00152             && ignore_decompression_errors)
00153             retval = inflateSync(zstr);
00154     } while (retval == Z_OK && (!out_limit || (out_limit > output)));
00155 
00156     if (retval == Z_STREAM_END)
00157         setfinished();
00158     else if (retval != Z_OK && retval != Z_BUF_ERROR &&
00159              !(retval == Z_DATA_ERROR && mode == Inflate
00160                && ignore_decompression_errors))
00161     {
00162         seterror("error %s during gzip %s: %s", retval,
00163             mode == Deflate ? "compression" : "decompression",
00164             zstr->msg ? zstr->msg : "unknown");
00165         return false;
00166     }
00167 
00168     return true;
00169 }
00170