• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
~BrotliDecompressor()11 BrotliDecompressor::~BrotliDecompressor() {
12   if (brotli_decoder_state_)
13     BrotliDecoderDestroyInstance(brotli_decoder_state_);
14 }
15 
SetInputData(const uint8_t * input_data,size_t size)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 
Read(uint8_t * output_data,size_t bytes_to_output)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 
Close()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