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_compressor.h"
6
7 #include "bsdiff/logging.h"
8
9 namespace {
10
11 const size_t kBufferSize = 1024 * 1024;
12 const uint32_t kBrotliDefaultLgwin = 20;
13
14 } // namespace
15
16 namespace bsdiff {
BrotliCompressor(int quality)17 BrotliCompressor::BrotliCompressor(int quality) : comp_buffer_(kBufferSize) {
18 brotli_encoder_state_ =
19 BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
20 if (!brotli_encoder_state_) {
21 LOG(ERROR) << "Failed to initialize brotli decoder state";
22 } else {
23 int compression_quality = quality;
24 if (compression_quality > BROTLI_MAX_QUALITY ||
25 compression_quality < BROTLI_MIN_QUALITY) {
26 LOG(ERROR) << "Invalid quality value: " << quality
27 << ", using default quality instead.";
28 compression_quality = BROTLI_MAX_QUALITY;
29 }
30
31 BrotliEncoderSetParameter(brotli_encoder_state_, BROTLI_PARAM_QUALITY,
32 compression_quality);
33 BrotliEncoderSetParameter(brotli_encoder_state_, BROTLI_PARAM_LGWIN,
34 kBrotliDefaultLgwin);
35 }
36 }
37
~BrotliCompressor()38 BrotliCompressor::~BrotliCompressor() {
39 if (brotli_encoder_state_) {
40 BrotliEncoderDestroyInstance(brotli_encoder_state_);
41 }
42 }
43
Write(const uint8_t * buf,size_t size)44 bool BrotliCompressor::Write(const uint8_t* buf, size_t size) {
45 if (!brotli_encoder_state_)
46 return false;
47
48 const uint8_t* next_in = buf;
49 size_t avail_in = size;
50 while (avail_in > 0) {
51 size_t avail_out = kBufferSize;
52 uint8_t* next_out = comp_buffer_.buffer_data();
53 if (!BrotliEncoderCompressStream(
54 brotli_encoder_state_, BROTLI_OPERATION_PROCESS, &avail_in,
55 &next_in, &avail_out, &next_out, nullptr)) {
56 LOG(ERROR) << "BrotliCompressor failed to compress " << avail_in
57 << " bytes of data.";
58 return false;
59 }
60
61 uint64_t output_bytes = comp_buffer_.buffer_size() - avail_out;
62 if (output_bytes > 0) {
63 comp_buffer_.AddDataToChunks(output_bytes);
64 }
65 }
66 return true;
67 }
68
Finish()69 bool BrotliCompressor::Finish() {
70 if (!brotli_encoder_state_)
71 return false;
72
73 const uint8_t* next_in = nullptr;
74 size_t avail_in = 0;
75 while (!BrotliEncoderIsFinished(brotli_encoder_state_)) {
76 size_t avail_out = kBufferSize;
77 uint8_t* next_out = comp_buffer_.buffer_data();
78 if (!BrotliEncoderCompressStream(
79 brotli_encoder_state_, BROTLI_OPERATION_FINISH, &avail_in, &next_in,
80 &avail_out, &next_out, nullptr)) {
81 LOG(ERROR) << "BrotliCompressor failed to finish compression";
82 return false;
83 }
84
85 uint64_t output_bytes = comp_buffer_.buffer_size() - avail_out;
86 if (output_bytes > 0) {
87 comp_buffer_.AddDataToChunks(output_bytes);
88 }
89 }
90 return true;
91 }
92
GetCompressedData()93 const std::vector<uint8_t>& BrotliCompressor::GetCompressedData() {
94 return comp_buffer_.GetCompressedData();
95 }
96
97 } // namespace bsdiff
98