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