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