| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "bsdiff/bz2_decompressor.h" |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include <bzlib.h> |
| |
| #include "bsdiff/logging.h" |
| |
| namespace bsdiff { |
| |
| BZ2Decompressor::~BZ2Decompressor() { |
| // Release the memory on destruction if needed. |
| if (stream_initialized_) |
| BZ2_bzDecompressEnd(&stream_); |
| } |
| |
| bool BZ2Decompressor::SetInputData(const uint8_t* input_data, size_t size) { |
| // TODO(xunchang) update the avail_in for size > 2GB. |
| if (size > std::numeric_limits<unsigned int>::max()) { |
| LOG(ERROR) << "Oversized input data" << size; |
| return false; |
| } |
| |
| stream_.next_in = reinterpret_cast<char*>(const_cast<uint8_t*>(input_data)); |
| stream_.avail_in = size; |
| stream_.bzalloc = nullptr; |
| stream_.bzfree = nullptr; |
| stream_.opaque = nullptr; |
| int bz2err = BZ2_bzDecompressInit(&stream_, 0, 0); |
| if (bz2err != BZ_OK) { |
| LOG(ERROR) << "Failed to bzinit control stream: " << bz2err; |
| return false; |
| } |
| stream_initialized_ = true; |
| return true; |
| } |
| |
| bool BZ2Decompressor::Read(uint8_t* output_data, size_t bytes_to_output) { |
| if (!stream_initialized_) { |
| LOG(ERROR) << "BZ2Decompressor not initialized"; |
| return false; |
| } |
| stream_.next_out = reinterpret_cast<char*>(output_data); |
| while (bytes_to_output > 0) { |
| size_t output_size = std::min<size_t>( |
| std::numeric_limits<unsigned int>::max(), bytes_to_output); |
| stream_.avail_out = output_size; |
| |
| size_t prev_avail_in = stream_.avail_in; |
| int bz2err = BZ2_bzDecompress(&stream_); |
| if (bz2err != BZ_OK && bz2err != BZ_STREAM_END) { |
| LOG(ERROR) << "Failed to decompress " << output_size |
| << " bytes of data, error: " << bz2err; |
| return false; |
| } |
| bytes_to_output -= (output_size - stream_.avail_out); |
| if (bytes_to_output && prev_avail_in == stream_.avail_in && |
| output_size == stream_.avail_out) { |
| LOG(ERROR) << "BZ2Decompressor made no progress, pending " |
| << bytes_to_output << " bytes to decompress"; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool BZ2Decompressor::Close() { |
| if (!stream_initialized_) { |
| LOG(ERROR) << "BZ2Decompressor not initialized"; |
| return false; |
| } |
| int bz2err = BZ2_bzDecompressEnd(&stream_); |
| if (bz2err != BZ_OK) { |
| LOG(ERROR) << "BZ2_bzDecompressEnd returns with " << bz2err; |
| return false; |
| } |
| stream_initialized_ = false; |
| return true; |
| } |
| |
| } // namespace bsdiff |