| // 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/brotli_decompressor.h" |
| |
| #include "bsdiff/logging.h" |
| |
| namespace bsdiff { |
| |
| BrotliDecompressor::~BrotliDecompressor() { |
| if (brotli_decoder_state_) |
| BrotliDecoderDestroyInstance(brotli_decoder_state_); |
| } |
| |
| bool BrotliDecompressor::SetInputData(const uint8_t* input_data, size_t size) { |
| brotli_decoder_state_ = |
| BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); |
| if (brotli_decoder_state_ == nullptr) { |
| LOG(ERROR) << "Failed to initialize brotli decoder."; |
| return false; |
| } |
| next_in_ = input_data; |
| available_in_ = size; |
| return true; |
| } |
| |
| bool BrotliDecompressor::Read(uint8_t* output_data, size_t bytes_to_output) { |
| if (!brotli_decoder_state_) { |
| LOG(ERROR) << "BrotliDecompressor not initialized"; |
| return false; |
| } |
| auto next_out = output_data; |
| size_t available_out = bytes_to_output; |
| |
| while (available_out > 0) { |
| // The brotli decoder will update |available_in_|, |available_in_|, |
| // |next_out| and |available_out|. |
| BrotliDecoderResult result = BrotliDecoderDecompressStream( |
| brotli_decoder_state_, &available_in_, &next_in_, &available_out, |
| &next_out, nullptr); |
| |
| if (result == BROTLI_DECODER_RESULT_ERROR) { |
| LOG(ERROR) << "Decompression failed with " |
| << BrotliDecoderErrorString( |
| BrotliDecoderGetErrorCode(brotli_decoder_state_)); |
| return false; |
| } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { |
| LOG(ERROR) << "Decompressor reached EOF while reading from input stream."; |
| return false; |
| } else if (result == BROTLI_DECODER_RESULT_SUCCESS) { |
| // This means that decoding is finished, no more input might be consumed |
| // and no more output will be produced. In the normal case, when there is |
| // more data available than what was requested in this Read() call it |
| // returns BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT. |
| if (available_out > 0) { |
| LOG(ERROR) << "Expected to read " << available_out |
| << " more bytes but reached the end of compressed brotli " |
| "stream"; |
| return false; |
| } |
| return true; |
| } |
| } |
| return true; |
| } |
| |
| bool BrotliDecompressor::Close() { |
| if (!brotli_decoder_state_) { |
| LOG(ERROR) << "BrotliDecompressor not initialized"; |
| return false; |
| } |
| // In some cases, the brotli compressed stream could be empty. As a result, |
| // the function BrotliDecoderIsFinished() will return false because we never |
| // start the decompression. When that happens, we just destroy the decoder |
| // and return true. |
| if (BrotliDecoderIsUsed(brotli_decoder_state_) && |
| !BrotliDecoderIsFinished(brotli_decoder_state_)) { |
| LOG(ERROR) << "Unfinished brotli decoder."; |
| return false; |
| } |
| |
| BrotliDecoderDestroyInstance(brotli_decoder_state_); |
| brotli_decoder_state_ = nullptr; |
| return true; |
| } |
| |
| } // namespace bsdiff |