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/brotli_decompressor.h"
      6 
      7 #include "bsdiff/logging.h"
      8 
      9 namespace bsdiff {
     10 
     11 BrotliDecompressor::~BrotliDecompressor() {
     12   if (brotli_decoder_state_)
     13     BrotliDecoderDestroyInstance(brotli_decoder_state_);
     14 }
     15 
     16 bool BrotliDecompressor::SetInputData(const uint8_t* input_data, size_t size) {
     17   brotli_decoder_state_ =
     18       BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
     19   if (brotli_decoder_state_ == nullptr) {
     20     LOG(ERROR) << "Failed to initialize brotli decoder.";
     21     return false;
     22   }
     23   next_in_ = input_data;
     24   available_in_ = size;
     25   return true;
     26 }
     27 
     28 bool BrotliDecompressor::Read(uint8_t* output_data, size_t bytes_to_output) {
     29   if (!brotli_decoder_state_) {
     30     LOG(ERROR) << "BrotliDecompressor not initialized";
     31     return false;
     32   }
     33   auto next_out = output_data;
     34   size_t available_out = bytes_to_output;
     35 
     36   while (available_out > 0) {
     37     // The brotli decoder will update |available_in_|, |available_in_|,
     38     // |next_out| and |available_out|.
     39     BrotliDecoderResult result = BrotliDecoderDecompressStream(
     40         brotli_decoder_state_, &available_in_, &next_in_, &available_out,
     41         &next_out, nullptr);
     42 
     43     if (result == BROTLI_DECODER_RESULT_ERROR) {
     44       LOG(ERROR) << "Decompression failed with "
     45                  << BrotliDecoderErrorString(
     46                         BrotliDecoderGetErrorCode(brotli_decoder_state_));
     47       return false;
     48     } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
     49       LOG(ERROR) << "Decompressor reached EOF while reading from input stream.";
     50       return false;
     51     } else if (result == BROTLI_DECODER_RESULT_SUCCESS) {
     52       // This means that decoding is finished, no more input might be consumed
     53       // and no more output will be produced. In the normal case, when there is
     54       // more data available than what was requested in this Read() call it
     55       // returns BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT.
     56       if (available_out > 0) {
     57         LOG(ERROR) << "Expected to read " << available_out
     58                    << " more bytes but reached the end of compressed brotli "
     59                       "stream";
     60         return false;
     61       }
     62       return true;
     63     }
     64   }
     65   return true;
     66 }
     67 
     68 bool BrotliDecompressor::Close() {
     69   if (!brotli_decoder_state_) {
     70     LOG(ERROR) << "BrotliDecompressor not initialized";
     71     return false;
     72   }
     73   // In some cases, the brotli compressed stream could be empty. As a result,
     74   // the function BrotliDecoderIsFinished() will return false because we never
     75   // start the decompression. When that happens, we just destroy the decoder
     76   // and return true.
     77   if (BrotliDecoderIsUsed(brotli_decoder_state_) &&
     78       !BrotliDecoderIsFinished(brotli_decoder_state_)) {
     79     LOG(ERROR) << "Unfinished brotli decoder.";
     80     return false;
     81   }
     82 
     83   BrotliDecoderDestroyInstance(brotli_decoder_state_);
     84   brotli_decoder_state_ = nullptr;
     85   return true;
     86 }
     87 
     88 }  // namespace bsdiff
     89