1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "bsdiff/bz2_decompressor.h" 6 7 #include <algorithm> 8 #include <limits> 9 10 #include <bzlib.h> 11 12 #include "bsdiff/logging.h" 13 14 namespace bsdiff { 15 16 BZ2Decompressor::~BZ2Decompressor() { 17 // Release the memory on destruction if needed. 18 if (stream_initialized_) 19 BZ2_bzDecompressEnd(&stream_); 20 } 21 22 bool BZ2Decompressor::SetInputData(const uint8_t* input_data, size_t size) { 23 // TODO(xunchang) update the avail_in for size > 2GB. 24 if (size > std::numeric_limits<unsigned int>::max()) { 25 LOG(ERROR) << "Oversized input data" << size; 26 return false; 27 } 28 29 stream_.next_in = reinterpret_cast<char*>(const_cast<uint8_t*>(input_data)); 30 stream_.avail_in = size; 31 stream_.bzalloc = nullptr; 32 stream_.bzfree = nullptr; 33 stream_.opaque = nullptr; 34 int bz2err = BZ2_bzDecompressInit(&stream_, 0, 0); 35 if (bz2err != BZ_OK) { 36 LOG(ERROR) << "Failed to bzinit control stream: " << bz2err; 37 return false; 38 } 39 stream_initialized_ = true; 40 return true; 41 } 42 43 bool BZ2Decompressor::Read(uint8_t* output_data, size_t bytes_to_output) { 44 if (!stream_initialized_) { 45 LOG(ERROR) << "BZ2Decompressor not initialized"; 46 return false; 47 } 48 stream_.next_out = reinterpret_cast<char*>(output_data); 49 while (bytes_to_output > 0) { 50 size_t output_size = std::min<size_t>( 51 std::numeric_limits<unsigned int>::max(), bytes_to_output); 52 stream_.avail_out = output_size; 53 54 size_t prev_avail_in = stream_.avail_in; 55 int bz2err = BZ2_bzDecompress(&stream_); 56 if (bz2err != BZ_OK && bz2err != BZ_STREAM_END) { 57 LOG(ERROR) << "Failed to decompress " << output_size 58 << " bytes of data, error: " << bz2err; 59 return false; 60 } 61 bytes_to_output -= (output_size - stream_.avail_out); 62 if (bytes_to_output && prev_avail_in == stream_.avail_in && 63 output_size == stream_.avail_out) { 64 LOG(ERROR) << "BZ2Decompressor made no progress, pending " 65 << bytes_to_output << " bytes to decompress"; 66 return false; 67 } 68 } 69 return true; 70 } 71 72 bool BZ2Decompressor::Close() { 73 if (!stream_initialized_) { 74 LOG(ERROR) << "BZ2Decompressor not initialized"; 75 return false; 76 } 77 int bz2err = BZ2_bzDecompressEnd(&stream_); 78 if (bz2err != BZ_OK) { 79 LOG(ERROR) << "BZ2_bzDecompressEnd returns with " << bz2err; 80 return false; 81 } 82 stream_initialized_ = false; 83 return true; 84 } 85 86 } // namespace bsdiff 87