Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 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 | |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 9 | namespace bsdiff { |
| 10 | |
Alex Deymo | c2ae7a5 | 2018-03-12 19:23:35 +0100 | [diff] [blame] | 11 | BrotliDecompressor::~BrotliDecompressor() { |
| 12 | if (brotli_decoder_state_) |
| 13 | BrotliDecoderDestroyInstance(brotli_decoder_state_); |
| 14 | } |
| 15 | |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 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) { |
Tianjie Xu | 18480eb | 2017-11-29 16:21:43 -0800 | [diff] [blame] | 20 | LOG(ERROR) << "Failed to initialize brotli decoder."; |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 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) { |
Alex Deymo | c2ae7a5 | 2018-03-12 19:23:35 +0100 | [diff] [blame] | 29 | if (!brotli_decoder_state_) { |
| 30 | LOG(ERROR) << "BrotliDecompressor not initialized"; |
| 31 | return false; |
| 32 | } |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 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( |
Tianjie Xu | 18480eb | 2017-11-29 16:21:43 -0800 | [diff] [blame] | 46 | BrotliDecoderGetErrorCode(brotli_decoder_state_)); |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 47 | return false; |
| 48 | } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { |
Tianjie Xu | 18480eb | 2017-11-29 16:21:43 -0800 | [diff] [blame] | 49 | LOG(ERROR) << "Decompressor reached EOF while reading from input stream."; |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 50 | return false; |
Alex Deymo | 338f340 | 2018-03-23 13:40:17 +0100 | [diff] [blame] | 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; |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 63 | } |
| 64 | } |
| 65 | return true; |
| 66 | } |
| 67 | |
| 68 | bool BrotliDecompressor::Close() { |
Alex Deymo | c2ae7a5 | 2018-03-12 19:23:35 +0100 | [diff] [blame] | 69 | if (!brotli_decoder_state_) { |
| 70 | LOG(ERROR) << "BrotliDecompressor not initialized"; |
| 71 | return false; |
| 72 | } |
Tianjie Xu | d4875cd | 2017-11-17 16:06:30 -0800 | [diff] [blame] | 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_)) { |
Tianjie Xu | 18480eb | 2017-11-29 16:21:43 -0800 | [diff] [blame] | 79 | LOG(ERROR) << "Unfinished brotli decoder."; |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 80 | return false; |
| 81 | } |
| 82 | |
| 83 | BrotliDecoderDestroyInstance(brotli_decoder_state_); |
Alex Deymo | c2ae7a5 | 2018-03-12 19:23:35 +0100 | [diff] [blame] | 84 | brotli_decoder_state_ = nullptr; |
Tianjie Xu | 4d10c3e | 2017-10-26 14:02:06 -0700 | [diff] [blame] | 85 | return true; |
| 86 | } |
| 87 | |
Alex Deymo | 9bb4ddb | 2018-02-14 16:30:54 +0100 | [diff] [blame] | 88 | } // namespace bsdiff |