• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
43 namespace google {
44 namespace protobuf {
45 namespace io {
46 
47 static const int kDefaultBufferSize = 65536;
48 
GzipInputStream(ZeroCopyInputStream * sub_stream,Format format,int buffer_size)49 GzipInputStream::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 }
~GzipInputStream()71 GzipInputStream::~GzipInputStream() {
72   operator delete(output_buffer_);
73   zerror_ = inflateEnd(&zcontext_);
74 }
75 
Inflate(int flush)76 int 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 
DoNextOutput(const void ** data,int * size)112 void 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 ----------------------------------
Next(const void ** data,int * size)119 bool 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 }
BackUp(int count)147 void GzipInputStream::BackUp(int count) {
148   output_position_ = reinterpret_cast<void*>(
149       reinterpret_cast<uintptr_t>(output_position_) - count);
150 }
Skip(int count)151 bool 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 }
ByteCount() const164 int64 GzipInputStream::ByteCount() const {
165   return zcontext_.total_out +
166     (((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_));
167 }
168 
169 // =========================================================================
170 
Options()171 GzipOutputStream::Options::Options()
172     : format(GZIP),
173       buffer_size(kDefaultBufferSize),
174       compression_level(Z_DEFAULT_COMPRESSION),
175       compression_strategy(Z_DEFAULT_STRATEGY) {}
176 
GzipOutputStream(ZeroCopyOutputStream * sub_stream)177 GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream) {
178   Init(sub_stream, Options());
179 }
180 
GzipOutputStream(ZeroCopyOutputStream * sub_stream,const Options & options)181 GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream,
182                                    const Options& options) {
183   Init(sub_stream, options);
184 }
185 
GzipOutputStream(ZeroCopyOutputStream * sub_stream,Format format,int buffer_size)186 GzipOutputStream::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 
Init(ZeroCopyOutputStream * sub_stream,const Options & options)196 void 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 
~GzipOutputStream()230 GzipOutputStream::~GzipOutputStream() {
231   Close();
232   if (input_buffer_ != NULL) {
233     operator delete(input_buffer_);
234   }
235 }
236 
237 // private
Deflate(int flush)238 int 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 ---------------------------------
Next(void ** data,int * size)266 bool 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 }
BackUp(int count)288 void GzipOutputStream::BackUp(int count) {
289   GOOGLE_CHECK_GE(zcontext_.avail_in, count);
290   zcontext_.avail_in -= count;
291 }
ByteCount() const292 int64 GzipOutputStream::ByteCount() const {
293   return zcontext_.total_in + zcontext_.avail_in;
294 }
295 
Flush()296 bool GzipOutputStream::Flush() {
297   do {
298     zerror_ = Deflate(Z_FULL_FLUSH);
299   } while (zerror_ == Z_OK);
300   return zerror_ == Z_OK;
301 }
302 
Close()303 bool 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