1 // Copyright 2021 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 "puffin/src/include/puffin/brotli_util.h"
6
7 #include "brotli/decode.h"
8 #include "brotli/encode.h"
9 #include "puffin/src/logging.h"
10 #include "puffin/memory_stream.h"
11
12 namespace puffin {
13
14 namespace {
15
16 constexpr auto kBufferSize = 32768;
17 constexpr auto kDefaultParamQuality = 9;
18 constexpr auto kDefaultParamLgwin = 20;
19 } // namespace
20
BrotliEncode(const uint8_t * input,size_t input_size,UniqueStreamPtr output_stream,int quality)21 bool BrotliEncode(const uint8_t* input,
22 size_t input_size,
23 UniqueStreamPtr output_stream,
24 int quality) {
25 std::unique_ptr<BrotliEncoderState, decltype(&BrotliEncoderDestroyInstance)>
26 encoder(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
27 BrotliEncoderDestroyInstance);
28 TEST_AND_RETURN_FALSE(encoder != nullptr);
29
30 BrotliEncoderSetParameter(encoder.get(), BROTLI_PARAM_QUALITY,
31 quality);
32 BrotliEncoderSetParameter(encoder.get(), BROTLI_PARAM_LGWIN,
33 kDefaultParamLgwin);
34
35 size_t available_in = input_size;
36 while (available_in != 0 || !BrotliEncoderIsFinished(encoder.get())) {
37 const uint8_t* next_in = input + input_size - available_in;
38 // Set up the output buffer
39 uint8_t buffer[kBufferSize];
40 uint8_t* next_out = buffer;
41 size_t available_out = kBufferSize;
42
43 BrotliEncoderOperation op =
44 available_in == 0 ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
45
46 if (!BrotliEncoderCompressStream(encoder.get(), op, &available_in, &next_in,
47 &available_out, &next_out, nullptr)) {
48 LOG(ERROR) << "Failed to compress " << input_size << " bytes with brotli";
49 return false;
50 }
51
52 size_t bytes_consumed = kBufferSize - available_out;
53 output_stream->Write(buffer, bytes_consumed);
54 }
55
56 return true;
57 }
58
BrotliEncode(const uint8_t * input,size_t input_size,UniqueStreamPtr output_stream)59 bool BrotliEncode(const uint8_t* input,
60 size_t input_size,
61 UniqueStreamPtr output_stream) {
62 return BrotliEncode(input, input_size, std::move(output_stream),
63 kDefaultParamQuality);
64 }
65
BrotliEncode(const uint8_t * input,size_t input_size,std::vector<uint8_t> * output)66 bool BrotliEncode(const uint8_t* input,
67 size_t input_size,
68 std::vector<uint8_t>* output) {
69 TEST_AND_RETURN_FALSE(output != nullptr);
70 return BrotliEncode(input, input_size, MemoryStream::CreateForWrite(output));
71 }
72
BrotliDecode(const uint8_t * input,size_t input_size,UniqueStreamPtr output_stream)73 bool BrotliDecode(const uint8_t* input,
74 size_t input_size,
75 UniqueStreamPtr output_stream) {
76 std::unique_ptr<BrotliDecoderState, decltype(&BrotliDecoderDestroyInstance)>
77 decoder(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
78 BrotliDecoderDestroyInstance);
79 TEST_AND_RETURN_FALSE(decoder != nullptr);
80
81 size_t available_in = input_size;
82 while (available_in != 0 || !BrotliDecoderIsFinished(decoder.get())) {
83 const uint8_t* next_in = input + input_size - available_in;
84 // Set up the output buffer
85 uint8_t buffer[kBufferSize];
86 uint8_t* next_out = buffer;
87 size_t available_out = kBufferSize;
88
89 BrotliDecoderResult result =
90 BrotliDecoderDecompressStream(decoder.get(), &available_in, &next_in,
91 &available_out, &next_out, nullptr);
92 if (result == BROTLI_DECODER_RESULT_ERROR ||
93 result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
94 LOG(ERROR) << "Failed to decompress " << input_size
95 << " bytes with brotli, result " << result;
96 return false;
97 }
98
99 size_t bytes_consumed = kBufferSize - available_out;
100 output_stream->Write(buffer, bytes_consumed);
101 }
102 return true;
103 }
104
BrotliDecode(const uint8_t * input,size_t input_size,std::vector<uint8_t> * output)105 bool BrotliDecode(const uint8_t* input,
106 size_t input_size,
107 std::vector<uint8_t>* output) {
108 TEST_AND_RETURN_FALSE(output != nullptr);
109 return BrotliDecode(input, input_size, MemoryStream::CreateForWrite(output));
110 }
111
112 } // namespace puffin
113