1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: brianolson@google.com (Brian Olson)
32//
33// This file contains the implementation of classes GzipInputStream and
34// GzipOutputStream.
35
36#include "config.h"
37
38#if HAVE_ZLIB
39#include <google/protobuf/io/gzip_stream.h>
40
41#include <google/protobuf/stubs/common.h>
42
43namespace google {
44namespace protobuf {
45namespace io {
46
47static const int kDefaultBufferSize = 65536;
48
49GzipInputStream::GzipInputStream(
50    ZeroCopyInputStream* sub_stream, Format format, int buffer_size)
51    : format_(format), sub_stream_(sub_stream), zerror_(Z_OK) {
52  zcontext_.zalloc = Z_NULL;
53  zcontext_.zfree = Z_NULL;
54  zcontext_.opaque = Z_NULL;
55  zcontext_.total_out = 0;
56  zcontext_.next_in = NULL;
57  zcontext_.avail_in = 0;
58  zcontext_.total_in = 0;
59  zcontext_.msg = NULL;
60  if (buffer_size == -1) {
61    output_buffer_length_ = kDefaultBufferSize;
62  } else {
63    output_buffer_length_ = buffer_size;
64  }
65  output_buffer_ = operator new(output_buffer_length_);
66  GOOGLE_CHECK(output_buffer_ != NULL);
67  zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
68  zcontext_.avail_out = output_buffer_length_;
69  output_position_ = output_buffer_;
70}
71GzipInputStream::~GzipInputStream() {
72  operator delete(output_buffer_);
73  zerror_ = inflateEnd(&zcontext_);
74}
75
76int GzipInputStream::Inflate(int flush) {
77  if ((zerror_ == Z_OK) && (zcontext_.avail_out == 0)) {
78    // previous inflate filled output buffer. don't change input params yet.
79  } else if (zcontext_.avail_in == 0) {
80    const void* in;
81    int in_size;
82    bool first = zcontext_.next_in == NULL;
83    bool ok = sub_stream_->Next(&in, &in_size);
84    if (!ok) {
85      zcontext_.next_out = NULL;
86      zcontext_.avail_out = 0;
87      return Z_STREAM_END;
88    }
89    zcontext_.next_in = static_cast<Bytef*>(const_cast<void*>(in));
90    zcontext_.avail_in = in_size;
91    if (first) {
92      int windowBitsFormat = 0;
93      switch (format_) {
94        case GZIP: windowBitsFormat = 16; break;
95        case AUTO: windowBitsFormat = 32; break;
96        case ZLIB: windowBitsFormat = 0; break;
97      }
98      int error = inflateInit2(&zcontext_,
99        /* windowBits */15 | windowBitsFormat);
100      if (error != Z_OK) {
101        return error;
102      }
103    }
104  }
105  zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
106  zcontext_.avail_out = output_buffer_length_;
107  output_position_ = output_buffer_;
108  int error = inflate(&zcontext_, flush);
109  return error;
110}
111
112void GzipInputStream::DoNextOutput(const void** data, int* size) {
113  *data = output_position_;
114  *size = ((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_);
115  output_position_ = zcontext_.next_out;
116}
117
118// implements ZeroCopyInputStream ----------------------------------
119bool GzipInputStream::Next(const void** data, int* size) {
120  bool ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END)
121      || (zerror_ == Z_BUF_ERROR);
122  if ((!ok) || (zcontext_.next_out == NULL)) {
123    return false;
124  }
125  if (zcontext_.next_out != output_position_) {
126    DoNextOutput(data, size);
127    return true;
128  }
129  if (zerror_ == Z_STREAM_END) {
130    *data = NULL;
131    *size = 0;
132    return false;
133  }
134  zerror_ = Inflate(Z_NO_FLUSH);
135  if ((zerror_ == Z_STREAM_END) && (zcontext_.next_out == NULL)) {
136    // The underlying stream's Next returned false inside Inflate.
137    return false;
138  }
139  ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END)
140      || (zerror_ == Z_BUF_ERROR);
141  if (!ok) {
142    return false;
143  }
144  DoNextOutput(data, size);
145  return true;
146}
147void GzipInputStream::BackUp(int count) {
148  output_position_ = reinterpret_cast<void*>(
149      reinterpret_cast<uintptr_t>(output_position_) - count);
150}
151bool GzipInputStream::Skip(int count) {
152  const void* data;
153  int size;
154  bool ok = Next(&data, &size);
155  while (ok && (size < count)) {
156    count -= size;
157    ok = Next(&data, &size);
158  }
159  if (size > count) {
160    BackUp(size - count);
161  }
162  return ok;
163}
164int64 GzipInputStream::ByteCount() const {
165  return zcontext_.total_out +
166    (((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_));
167}
168
169// =========================================================================
170
171GzipOutputStream::Options::Options()
172    : format(GZIP),
173      buffer_size(kDefaultBufferSize),
174      compression_level(Z_DEFAULT_COMPRESSION),
175      compression_strategy(Z_DEFAULT_STRATEGY) {}
176
177GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream) {
178  Init(sub_stream, Options());
179}
180
181GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream,
182                                   const Options& options) {
183  Init(sub_stream, options);
184}
185
186GzipOutputStream::GzipOutputStream(
187    ZeroCopyOutputStream* sub_stream, Format format, int buffer_size) {
188  Options options;
189  options.format = format;
190  if (buffer_size != -1) {
191    options.buffer_size = buffer_size;
192  }
193  Init(sub_stream, options);
194}
195
196void GzipOutputStream::Init(ZeroCopyOutputStream* sub_stream,
197                            const Options& options) {
198  sub_stream_ = sub_stream;
199  sub_data_ = NULL;
200  sub_data_size_ = 0;
201
202  input_buffer_length_ = options.buffer_size;
203  input_buffer_ = operator new(input_buffer_length_);
204  GOOGLE_CHECK(input_buffer_ != NULL);
205
206  zcontext_.zalloc = Z_NULL;
207  zcontext_.zfree = Z_NULL;
208  zcontext_.opaque = Z_NULL;
209  zcontext_.next_out = NULL;
210  zcontext_.avail_out = 0;
211  zcontext_.total_out = 0;
212  zcontext_.next_in = NULL;
213  zcontext_.avail_in = 0;
214  zcontext_.total_in = 0;
215  zcontext_.msg = NULL;
216  // default to GZIP format
217  int windowBitsFormat = 16;
218  if (options.format == ZLIB) {
219    windowBitsFormat = 0;
220  }
221  zerror_ = deflateInit2(
222      &zcontext_,
223      options.compression_level,
224      Z_DEFLATED,
225      /* windowBits */15 | windowBitsFormat,
226      /* memLevel (default) */8,
227      options.compression_strategy);
228}
229
230GzipOutputStream::~GzipOutputStream() {
231  Close();
232  if (input_buffer_ != NULL) {
233    operator delete(input_buffer_);
234  }
235}
236
237// private
238int GzipOutputStream::Deflate(int flush) {
239  int error = Z_OK;
240  do {
241    if ((sub_data_ == NULL) || (zcontext_.avail_out == 0)) {
242      bool ok = sub_stream_->Next(&sub_data_, &sub_data_size_);
243      if (!ok) {
244        sub_data_ = NULL;
245        sub_data_size_ = 0;
246        return Z_BUF_ERROR;
247      }
248      GOOGLE_CHECK_GT(sub_data_size_, 0);
249      zcontext_.next_out = static_cast<Bytef*>(sub_data_);
250      zcontext_.avail_out = sub_data_size_;
251    }
252    error = deflate(&zcontext_, flush);
253  } while (error == Z_OK && zcontext_.avail_out == 0);
254  if (((flush == Z_FULL_FLUSH) || (flush == Z_FINISH))
255      && (zcontext_.avail_out != sub_data_size_)) {
256    // Notify lower layer of data.
257    sub_stream_->BackUp(zcontext_.avail_out);
258    // We don't own the buffer anymore.
259    sub_data_ = NULL;
260    sub_data_size_ = 0;
261  }
262  return error;
263}
264
265// implements ZeroCopyOutputStream ---------------------------------
266bool GzipOutputStream::Next(void** data, int* size) {
267  if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
268    return false;
269  }
270  if (zcontext_.avail_in != 0) {
271    zerror_ = Deflate(Z_NO_FLUSH);
272    if (zerror_ != Z_OK) {
273      return false;
274    }
275  }
276  if (zcontext_.avail_in == 0) {
277    // all input was consumed. reset the buffer.
278    zcontext_.next_in = static_cast<Bytef*>(input_buffer_);
279    zcontext_.avail_in = input_buffer_length_;
280    *data = input_buffer_;
281    *size = input_buffer_length_;
282  } else {
283    // The loop in Deflate should consume all avail_in
284    GOOGLE_LOG(DFATAL) << "Deflate left bytes unconsumed";
285  }
286  return true;
287}
288void GzipOutputStream::BackUp(int count) {
289  GOOGLE_CHECK_GE(zcontext_.avail_in, count);
290  zcontext_.avail_in -= count;
291}
292int64 GzipOutputStream::ByteCount() const {
293  return zcontext_.total_in + zcontext_.avail_in;
294}
295
296bool GzipOutputStream::Flush() {
297  do {
298    zerror_ = Deflate(Z_FULL_FLUSH);
299  } while (zerror_ == Z_OK);
300  return zerror_ == Z_OK;
301}
302
303bool GzipOutputStream::Close() {
304  if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
305    return false;
306  }
307  do {
308    zerror_ = Deflate(Z_FINISH);
309  } while (zerror_ == Z_OK);
310  zerror_ = deflateEnd(&zcontext_);
311  bool ok = zerror_ == Z_OK;
312  zerror_ = Z_STREAM_END;
313  return ok;
314}
315
316}  // namespace io
317}  // namespace protobuf
318}  // namespace google
319
320#endif  // HAVE_ZLIB
321