Line data Source code
1 : // Protocol Buffers - Google's data interchange format
2 : // Copyright 2008 Google Inc. All rights reserved.
3 : // https://developers.google.com/protocol-buffers/
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 :
37 : #if HAVE_ZLIB
38 : #include <google/protobuf/io/gzip_stream.h>
39 :
40 : #include <google/protobuf/stubs/common.h>
41 :
42 : namespace google {
43 : namespace protobuf {
44 : namespace io {
45 :
46 : static const int kDefaultBufferSize = 65536;
47 :
48 0 : GzipInputStream::GzipInputStream(
49 0 : ZeroCopyInputStream* sub_stream, Format format, int buffer_size)
50 0 : : format_(format), sub_stream_(sub_stream), zerror_(Z_OK) {
51 0 : zcontext_.zalloc = Z_NULL;
52 0 : zcontext_.zfree = Z_NULL;
53 0 : zcontext_.opaque = Z_NULL;
54 0 : zcontext_.total_out = 0;
55 0 : zcontext_.next_in = NULL;
56 0 : zcontext_.avail_in = 0;
57 0 : zcontext_.total_in = 0;
58 0 : zcontext_.msg = NULL;
59 0 : if (buffer_size == -1) {
60 0 : output_buffer_length_ = kDefaultBufferSize;
61 : } else {
62 0 : output_buffer_length_ = buffer_size;
63 : }
64 0 : output_buffer_ = operator new(output_buffer_length_);
65 0 : GOOGLE_CHECK(output_buffer_ != NULL);
66 0 : zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
67 0 : zcontext_.avail_out = output_buffer_length_;
68 0 : output_position_ = output_buffer_;
69 0 : }
70 0 : GzipInputStream::~GzipInputStream() {
71 0 : operator delete(output_buffer_);
72 0 : zerror_ = inflateEnd(&zcontext_);
73 0 : }
74 :
75 0 : static inline int internalInflateInit2(
76 : z_stream* zcontext, GzipInputStream::Format format) {
77 0 : int windowBitsFormat = 0;
78 0 : switch (format) {
79 0 : case GzipInputStream::GZIP: windowBitsFormat = 16; break;
80 0 : case GzipInputStream::AUTO: windowBitsFormat = 32; break;
81 0 : case GzipInputStream::ZLIB: windowBitsFormat = 0; break;
82 : }
83 0 : return inflateInit2(zcontext, /* windowBits */15 | windowBitsFormat);
84 : }
85 :
86 0 : int GzipInputStream::Inflate(int flush) {
87 0 : if ((zerror_ == Z_OK) && (zcontext_.avail_out == 0)) {
88 : // previous inflate filled output buffer. don't change input params yet.
89 0 : } else if (zcontext_.avail_in == 0) {
90 : const void* in;
91 : int in_size;
92 0 : bool first = zcontext_.next_in == NULL;
93 0 : bool ok = sub_stream_->Next(&in, &in_size);
94 0 : if (!ok) {
95 0 : zcontext_.next_out = NULL;
96 0 : zcontext_.avail_out = 0;
97 0 : return Z_STREAM_END;
98 : }
99 0 : zcontext_.next_in = static_cast<Bytef*>(const_cast<void*>(in));
100 0 : zcontext_.avail_in = in_size;
101 0 : if (first) {
102 0 : int error = internalInflateInit2(&zcontext_, format_);
103 0 : if (error != Z_OK) {
104 0 : return error;
105 : }
106 : }
107 : }
108 0 : zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
109 0 : zcontext_.avail_out = output_buffer_length_;
110 0 : output_position_ = output_buffer_;
111 0 : int error = inflate(&zcontext_, flush);
112 0 : return error;
113 : }
114 :
115 0 : void GzipInputStream::DoNextOutput(const void** data, int* size) {
116 0 : *data = output_position_;
117 0 : *size = ((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_);
118 0 : output_position_ = zcontext_.next_out;
119 0 : }
120 :
121 : // implements ZeroCopyInputStream ----------------------------------
122 0 : bool GzipInputStream::Next(const void** data, int* size) {
123 0 : bool ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END)
124 0 : || (zerror_ == Z_BUF_ERROR);
125 0 : if ((!ok) || (zcontext_.next_out == NULL)) {
126 0 : return false;
127 : }
128 0 : if (zcontext_.next_out != output_position_) {
129 0 : DoNextOutput(data, size);
130 0 : return true;
131 : }
132 0 : if (zerror_ == Z_STREAM_END) {
133 0 : if (zcontext_.next_out != NULL) {
134 : // sub_stream_ may have concatenated streams to follow
135 0 : zerror_ = inflateEnd(&zcontext_);
136 0 : if (zerror_ != Z_OK) {
137 0 : return false;
138 : }
139 0 : zerror_ = internalInflateInit2(&zcontext_, format_);
140 0 : if (zerror_ != Z_OK) {
141 0 : return false;
142 : }
143 : } else {
144 0 : *data = NULL;
145 0 : *size = 0;
146 0 : return false;
147 : }
148 : }
149 0 : zerror_ = Inflate(Z_NO_FLUSH);
150 0 : if ((zerror_ == Z_STREAM_END) && (zcontext_.next_out == NULL)) {
151 : // The underlying stream's Next returned false inside Inflate.
152 0 : return false;
153 : }
154 0 : ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END)
155 0 : || (zerror_ == Z_BUF_ERROR);
156 0 : if (!ok) {
157 0 : return false;
158 : }
159 0 : DoNextOutput(data, size);
160 0 : return true;
161 : }
162 0 : void GzipInputStream::BackUp(int count) {
163 0 : output_position_ = reinterpret_cast<void*>(
164 0 : reinterpret_cast<uintptr_t>(output_position_) - count);
165 0 : }
166 0 : bool GzipInputStream::Skip(int count) {
167 : const void* data;
168 : int size;
169 0 : bool ok = Next(&data, &size);
170 0 : while (ok && (size < count)) {
171 0 : count -= size;
172 0 : ok = Next(&data, &size);
173 : }
174 0 : if (size > count) {
175 0 : BackUp(size - count);
176 : }
177 0 : return ok;
178 : }
179 0 : int64 GzipInputStream::ByteCount() const {
180 0 : return zcontext_.total_out +
181 0 : (((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_));
182 : }
183 :
184 : // =========================================================================
185 :
186 0 : GzipOutputStream::Options::Options()
187 : : format(GZIP),
188 : buffer_size(kDefaultBufferSize),
189 : compression_level(Z_DEFAULT_COMPRESSION),
190 0 : compression_strategy(Z_DEFAULT_STRATEGY) {}
191 :
192 0 : GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream) {
193 0 : Init(sub_stream, Options());
194 0 : }
195 :
196 0 : GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream,
197 0 : const Options& options) {
198 0 : Init(sub_stream, options);
199 0 : }
200 :
201 0 : void GzipOutputStream::Init(ZeroCopyOutputStream* sub_stream,
202 : const Options& options) {
203 0 : sub_stream_ = sub_stream;
204 0 : sub_data_ = NULL;
205 0 : sub_data_size_ = 0;
206 :
207 0 : input_buffer_length_ = options.buffer_size;
208 0 : input_buffer_ = operator new(input_buffer_length_);
209 0 : GOOGLE_CHECK(input_buffer_ != NULL);
210 :
211 0 : zcontext_.zalloc = Z_NULL;
212 0 : zcontext_.zfree = Z_NULL;
213 0 : zcontext_.opaque = Z_NULL;
214 0 : zcontext_.next_out = NULL;
215 0 : zcontext_.avail_out = 0;
216 0 : zcontext_.total_out = 0;
217 0 : zcontext_.next_in = NULL;
218 0 : zcontext_.avail_in = 0;
219 0 : zcontext_.total_in = 0;
220 0 : zcontext_.msg = NULL;
221 : // default to GZIP format
222 0 : int windowBitsFormat = 16;
223 0 : if (options.format == ZLIB) {
224 0 : windowBitsFormat = 0;
225 : }
226 0 : zerror_ = deflateInit2(
227 : &zcontext_,
228 : options.compression_level,
229 : Z_DEFLATED,
230 : /* windowBits */15 | windowBitsFormat,
231 : /* memLevel (default) */8,
232 : options.compression_strategy);
233 0 : }
234 :
235 0 : GzipOutputStream::~GzipOutputStream() {
236 0 : Close();
237 0 : if (input_buffer_ != NULL) {
238 0 : operator delete(input_buffer_);
239 : }
240 0 : }
241 :
242 : // private
243 0 : int GzipOutputStream::Deflate(int flush) {
244 0 : int error = Z_OK;
245 0 : do {
246 0 : if ((sub_data_ == NULL) || (zcontext_.avail_out == 0)) {
247 0 : bool ok = sub_stream_->Next(&sub_data_, &sub_data_size_);
248 0 : if (!ok) {
249 0 : sub_data_ = NULL;
250 0 : sub_data_size_ = 0;
251 0 : return Z_BUF_ERROR;
252 : }
253 0 : GOOGLE_CHECK_GT(sub_data_size_, 0);
254 0 : zcontext_.next_out = static_cast<Bytef*>(sub_data_);
255 0 : zcontext_.avail_out = sub_data_size_;
256 : }
257 0 : error = deflate(&zcontext_, flush);
258 0 : } while (error == Z_OK && zcontext_.avail_out == 0);
259 0 : if ((flush == Z_FULL_FLUSH) || (flush == Z_FINISH)) {
260 : // Notify lower layer of data.
261 0 : sub_stream_->BackUp(zcontext_.avail_out);
262 : // We don't own the buffer anymore.
263 0 : sub_data_ = NULL;
264 0 : sub_data_size_ = 0;
265 : }
266 0 : return error;
267 : }
268 :
269 : // implements ZeroCopyOutputStream ---------------------------------
270 0 : bool GzipOutputStream::Next(void** data, int* size) {
271 0 : if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
272 0 : return false;
273 : }
274 0 : if (zcontext_.avail_in != 0) {
275 0 : zerror_ = Deflate(Z_NO_FLUSH);
276 0 : if (zerror_ != Z_OK) {
277 0 : return false;
278 : }
279 : }
280 0 : if (zcontext_.avail_in == 0) {
281 : // all input was consumed. reset the buffer.
282 0 : zcontext_.next_in = static_cast<Bytef*>(input_buffer_);
283 0 : zcontext_.avail_in = input_buffer_length_;
284 0 : *data = input_buffer_;
285 0 : *size = input_buffer_length_;
286 : } else {
287 : // The loop in Deflate should consume all avail_in
288 0 : GOOGLE_LOG(DFATAL) << "Deflate left bytes unconsumed";
289 : }
290 0 : return true;
291 : }
292 0 : void GzipOutputStream::BackUp(int count) {
293 0 : GOOGLE_CHECK_GE(zcontext_.avail_in, count);
294 0 : zcontext_.avail_in -= count;
295 0 : }
296 0 : int64 GzipOutputStream::ByteCount() const {
297 0 : return zcontext_.total_in + zcontext_.avail_in;
298 : }
299 :
300 0 : bool GzipOutputStream::Flush() {
301 0 : zerror_ = Deflate(Z_FULL_FLUSH);
302 : // Return true if the flush succeeded or if it was a no-op.
303 0 : return (zerror_ == Z_OK) ||
304 0 : (zerror_ == Z_BUF_ERROR && zcontext_.avail_in == 0 &&
305 0 : zcontext_.avail_out != 0);
306 : }
307 :
308 0 : bool GzipOutputStream::Close() {
309 0 : if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
310 0 : return false;
311 : }
312 0 : do {
313 0 : zerror_ = Deflate(Z_FINISH);
314 0 : } while (zerror_ == Z_OK);
315 0 : zerror_ = deflateEnd(&zcontext_);
316 0 : bool ok = zerror_ == Z_OK;
317 0 : zerror_ = Z_STREAM_END;
318 0 : return ok;
319 : }
320 :
321 : } // namespace io
322 : } // namespace protobuf
323 : } // namespace google
324 :
325 : #endif // HAVE_ZLIB
|