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