| // 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 |