WvStreams
wvgzip.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  *
5  * Gzip encoder/decoder based on zlib.
6  */
7 #include "wvgzip.h"
8 #include <zlib.h>
9 #include <assert.h>
10 
11 #define ZBUFSIZE 10240
12 
13 
14 WvGzipEncoder::WvGzipEncoder(Mode _mode, size_t _out_limit) :
15  out_limit(_out_limit), tmpbuf(ZBUFSIZE), mode(_mode)
16 {
18  full_flush = false;
19  init();
20 }
21 
22 
23 WvGzipEncoder::~WvGzipEncoder()
24 {
25  close();
26 }
27 
28 
29 void WvGzipEncoder::init()
30 {
31  zstr = new z_stream;
32  memset(zstr, 0, sizeof(*zstr));
33  zstr->zalloc = Z_NULL;
34  zstr->zfree = Z_NULL;
35  zstr->opaque = NULL;
36  zstr->msg = NULL;
37 
38  int retval;
39  if (mode == Deflate)
40  retval = deflateInit(zstr, Z_BEST_SPEED);
41  else
42  retval = inflateInit(zstr);
43 
44  if (retval != Z_OK)
45  {
46  seterror("error %s initializing gzip %s: %s", retval,
47  mode == Deflate ? "compressor" : "decompressor",
48  zstr->msg ? zstr->msg : "unknown");
49  return;
50  }
51  zstr->next_in = zstr->next_out = NULL;
52  zstr->avail_in = zstr->avail_out = 0;
53 }
54 
55 void WvGzipEncoder::close()
56 {
57  if (mode == Deflate)
58  deflateEnd(zstr);
59  else
60  inflateEnd(zstr);
61 
62  delete zstr;
63 
64 }
65 
66 bool WvGzipEncoder::_encode(WvBuf &inbuf, WvBuf &outbuf, bool flush)
67 {
68  bool success;
69  output = 0;
70  for (;;)
71  {
72  size_t starting_size = inbuf.used();
73  prepare(& inbuf);
74  bool alldata = inbuf.used() == 0;
75  success = process(outbuf, flush && alldata, false);
76  if (zstr->avail_in != 0)
77  {
78  // unget unused data
79  inbuf.unget(zstr->avail_in);
80  zstr->avail_in = 0;
81  }
82  if (! success)
83  return false;
84  if (alldata || (starting_size == inbuf.used()) ||
85  (out_limit && (output >= out_limit)))
86  return true;
87  }
88 }
89 
90 
92 {
93  prepare(NULL);
94  return process(outbuf, false, true);
95 }
96 
97 
99 {
100  close();
101  init();
102  return true;
103 }
104 
105 
106 void WvGzipEncoder::prepare(WvBuf *inbuf)
107 {
108  assert(zstr->avail_in == 0);
109  if (inbuf && inbuf->used() != 0)
110  {
111  size_t avail = inbuf->optgettable();
112  zstr->avail_in = avail;
113  zstr->next_in = const_cast<Bytef*>(
114  (const Bytef*)inbuf->get(avail));
115  }
116  else
117  {
118  zstr->avail_in = 0;
119  zstr->next_in = (Bytef*)""; // so it's not NULL
120  }
121 }
122 
123 
124 bool WvGzipEncoder::process(WvBuf &outbuf, bool flush, bool finish)
125 {
126  int flushmode = finish ? Z_FINISH :
127  flush ? (full_flush ? Z_FULL_FLUSH : Z_SYNC_FLUSH) : Z_NO_FLUSH;
128  int retval;
129  do
130  {
131  // process the next chunk
132  tmpbuf.zap();
133  size_t avail_out = tmpbuf.free();
134  if (out_limit)
135  avail_out = tmpbuf.free() < (out_limit - output) ? tmpbuf.free()
136  : (out_limit - output);
137 
138  zstr->avail_out = avail_out;
139  zstr->next_out = tmpbuf.alloc(avail_out);
140  if (mode == Deflate)
141  retval = deflate(zstr, flushmode);
142  else
143  retval = inflate(zstr, flushmode);
144  tmpbuf.unalloc(zstr->avail_out);
145 
146  output += avail_out - zstr->avail_out;
147 
148  // consume pending output
149  outbuf.merge(tmpbuf);
150 
151  if (retval == Z_DATA_ERROR && mode == Inflate
153  retval = inflateSync(zstr);
154  } while (retval == Z_OK && (!out_limit || (out_limit > output)));
155 
156  if (retval == Z_STREAM_END)
157  setfinished();
158  else if (retval != Z_OK && retval != Z_BUF_ERROR &&
159  !(retval == Z_DATA_ERROR && mode == Inflate
161  {
162  seterror("error %s during gzip %s: %s", retval,
163  mode == Deflate ? "compression" : "decompression",
164  zstr->msg ? zstr->msg : "unknown");
165  return false;
166  }
167 
168  return true;
169 }
170