blob: 88861db1c90b5be92598f60195210b9cc1b87878 [file] [log] [blame]
// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// 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 "util/stream/zlib_output_stream.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "util/misc/zlib.h"
namespace crashpad {
ZlibOutputStream::ZlibOutputStream(
Mode mode,
std::unique_ptr<OutputStreamInterface> output_stream)
: output_stream_(std::move(output_stream)),
mode_(mode),
initialized_(),
flush_needed_(false) {}
ZlibOutputStream::~ZlibOutputStream() {
if (!initialized_.is_valid())
return;
DCHECK(!flush_needed_);
if (mode_ == Mode::kCompress) {
if (deflateEnd(&zlib_stream_) != Z_OK)
LOG(ERROR) << "deflateEnd: " << zlib_stream_.msg;
} else if (mode_ == Mode::kDecompress) {
if (inflateEnd(&zlib_stream_) != Z_OK)
LOG(ERROR) << "inflateEnd: " << zlib_stream_.msg;
}
}
bool ZlibOutputStream::Write(const uint8_t* data, size_t size) {
if (initialized_.is_uninitialized()) {
initialized_.set_invalid();
zlib_stream_.zalloc = Z_NULL;
zlib_stream_.zfree = Z_NULL;
zlib_stream_.opaque = Z_NULL;
if (mode_ == Mode::kDecompress) {
int result = inflateInit(&zlib_stream_);
if (result != Z_OK) {
LOG(ERROR) << "inflateInit: " << ZlibErrorString(result);
return false;
}
} else if (mode_ == Mode::kCompress) {
int result = deflateInit(&zlib_stream_, Z_BEST_COMPRESSION);
if (result != Z_OK) {
LOG(ERROR) << "deflateInit: " << ZlibErrorString(result);
return false;
}
}
zlib_stream_.next_out = buffer_;
zlib_stream_.avail_out = base::saturated_cast<uInt>(base::size(buffer_));
initialized_.set_valid();
}
if (!initialized_.is_valid())
return false;
zlib_stream_.next_in = data;
zlib_stream_.avail_in = base::saturated_cast<uInt>(size);
flush_needed_ = false;
while (zlib_stream_.avail_in > 0) {
if (mode_ == Mode::kCompress) {
if (deflate(&zlib_stream_, Z_NO_FLUSH) != Z_OK) {
LOG(ERROR) << "deflate: " << zlib_stream_.msg;
return false;
}
} else if (mode_ == Mode::kDecompress) {
int result = inflate(&zlib_stream_, Z_NO_FLUSH);
if (result == Z_STREAM_END) {
if (zlib_stream_.avail_in > 0) {
LOG(ERROR) << "inflate: unconsumed input";
return false;
}
} else if (result != Z_OK) {
LOG(ERROR) << "inflate: " << zlib_stream_.msg;
return false;
}
}
if (!WriteOutputStream())
return false;
}
flush_needed_ = true;
return true;
}
bool ZlibOutputStream::Flush() {
if (initialized_.is_valid() && flush_needed_) {
flush_needed_ = false;
int result = Z_OK;
do {
if (mode_ == Mode::kCompress) {
result = deflate(&zlib_stream_, Z_FINISH);
if (result != Z_STREAM_END && result != Z_BUF_ERROR && result != Z_OK) {
LOG(ERROR) << "deflate: " << zlib_stream_.msg;
return false;
}
} else if (mode_ == Mode::kDecompress) {
result = inflate(&zlib_stream_, Z_FINISH);
if (result != Z_STREAM_END && result != Z_BUF_ERROR && result != Z_OK) {
LOG(ERROR) << "inflate: " << zlib_stream_.msg;
return false;
}
}
if (!WriteOutputStream())
return false;
} while (result != Z_STREAM_END);
}
return output_stream_->Flush();
}
bool ZlibOutputStream::WriteOutputStream() {
auto valid_size = base::size(buffer_) - zlib_stream_.avail_out;
if (valid_size > 0 && !output_stream_->Write(buffer_, valid_size))
return false;
zlib_stream_.next_out = buffer_;
zlib_stream_.avail_out = base::saturated_cast<uInt>(base::size(buffer_));
return true;
}
} // namespace crashpad