Home | History | Annotate | Download | only in bsdiff
      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