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 (at) 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 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 } 71 GzipInputStream::~GzipInputStream() { 72 operator delete(output_buffer_); 73 zerror_ = inflateEnd(&zcontext_); 74 } 75 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 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 ---------------------------------- 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 } 147 void GzipInputStream::BackUp(int count) { 148 output_position_ = reinterpret_cast<void*>( 149 reinterpret_cast<uintptr_t>(output_position_) - count); 150 } 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 } 164 int64 GzipInputStream::ByteCount() const { 165 return zcontext_.total_out + 166 (((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_)); 167 } 168 169 // ========================================================================= 170 171 GzipOutputStream::Options::Options() 172 : format(GZIP), 173 buffer_size(kDefaultBufferSize), 174 compression_level(Z_DEFAULT_COMPRESSION), 175 compression_strategy(Z_DEFAULT_STRATEGY) {} 176 177 GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream) { 178 Init(sub_stream, Options()); 179 } 180 181 GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream, 182 const Options& options) { 183 Init(sub_stream, options); 184 } 185 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 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 230 GzipOutputStream::~GzipOutputStream() { 231 Close(); 232 if (input_buffer_ != NULL) { 233 operator delete(input_buffer_); 234 } 235 } 236 237 // private 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 --------------------------------- 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 } 288 void GzipOutputStream::BackUp(int count) { 289 GOOGLE_CHECK_GE(zcontext_.avail_in, count); 290 zcontext_.avail_in -= count; 291 } 292 int64 GzipOutputStream::ByteCount() const { 293 return zcontext_.total_in + zcontext_.avail_in; 294 } 295 296 bool GzipOutputStream::Flush() { 297 do { 298 zerror_ = Deflate(Z_FULL_FLUSH); 299 } while (zerror_ == Z_OK); 300 return zerror_ == Z_OK; 301 } 302 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