blob: 5e2f0bfb7b269705f62eaeeb8ffc2a896582b936 [file] [log] [blame]
/**
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "net/instaweb/util/public/gzip_inflater.h"
#include <stdlib.h>
#include "base/logging.h"
#ifdef USE_SYSTEM_ZLIB
#include "zlib.h"
#else
#include "third_party/zlib/zlib.h"
#endif
namespace net_instaweb {
GzipInflater::GzipInflater(InflateType type)
: zlib_(NULL),
finished_(false),
error_(false),
type_(type) {
}
GzipInflater::~GzipInflater() {
Free();
}
void GzipInflater::Free() {
if (zlib_ == NULL) {
// Already freed.
return;
}
int err = inflateEnd(zlib_);
if (err != Z_OK) {
error_ = true;
}
free(zlib_);
zlib_ = NULL;
}
bool GzipInflater::Init() {
if (zlib_ != NULL) {
return false;
}
zlib_ = static_cast<z_stream *>(malloc(sizeof(z_stream)));
if (zlib_ == NULL) {
return false;
}
memset(zlib_, 0, sizeof(z_stream));
// Tell zlib that the data is gzip-encoded or deflate-encode.
int window_size = 31; // window size of 15, plus 16 for gzip
if (type_ == kDeflate) {
window_size = -15; // window size of -15 for row deflate.
}
int err = inflateInit2(zlib_, window_size);
if (err != Z_OK) {
Free();
error_ = true;
return false;
}
return true;
}
bool GzipInflater::HasUnconsumedInput() const {
if (zlib_ == NULL) {
return false;
}
if (finished_ || error_) {
return false;
}
return zlib_->avail_in > 0;
}
bool GzipInflater::SetInput(const void *in, size_t in_size) {
if (zlib_ == NULL) {
return false;
}
if (HasUnconsumedInput()) {
return false;
}
if (finished_) {
return false;
}
if (error_) {
return false;
}
if (in == NULL || in_size == 0) {
return false;
}
// The zlib library won't modify the buffer, but it does not use
// const here, so we must const cast.
zlib_->next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(in));
zlib_->avail_in = static_cast<uInt>(in_size);
return true;
}
int GzipInflater::InflateBytes(char *buf, size_t buf_size) {
if (zlib_ == NULL) {
return -1;
}
if (!HasUnconsumedInput()) {
return -1;
}
if (finished_) {
return -1;
}
if (error_) {
return -1;
}
if (buf == NULL || buf_size == 0) {
return -1;
}
const size_t inflated_bytes_before = zlib_->total_out;
zlib_->next_out = reinterpret_cast<Bytef *>(buf);
zlib_->avail_out = static_cast<uInt>(buf_size);
int err = inflate(zlib_, Z_SYNC_FLUSH);
const size_t inflated_bytes = zlib_->total_out - inflated_bytes_before;
if (err == Z_STREAM_END) {
finished_ = true;
} else if (err == Z_OK) {
if (inflated_bytes < buf_size) {
// Sanity check that if we didn't fill the output buffer, it's
// because we consumed all of the input.
DCHECK(!HasUnconsumedInput());
}
} else if (err == Z_BUF_ERROR) {
// Sanity check that if we encountered this error, it's because we
// were unable to write any inflated bytes to the output
// buffer. zlib documentation says that this is a non-terminal
// error, so we do not set error_ to true here.
DCHECK_EQ(inflated_bytes, static_cast<size_t>(0));
} else {
error_ = true;
return -1;
}
return static_cast<int>(inflated_bytes);
}
void GzipInflater::ShutDown() {
Free();
}
} // namespace net_instaweb