Home | History | Annotate | Download | only in io
      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