| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "bsdiff/brotli_compressor.h" |
| |
| #include "bsdiff/logging.h" |
| |
| namespace { |
| |
| const size_t kBufferSize = 1024 * 1024; |
| const uint32_t kBrotliDefaultLgwin = 20; |
| |
| } // namespace |
| |
| namespace bsdiff { |
| BrotliCompressor::BrotliCompressor(int quality) : comp_buffer_(kBufferSize) { |
| brotli_encoder_state_ = |
| BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); |
| if (!brotli_encoder_state_) { |
| LOG(ERROR) << "Failed to initialize brotli decoder state"; |
| } else { |
| int compression_quality = quality; |
| if (compression_quality > BROTLI_MAX_QUALITY || |
| compression_quality < BROTLI_MIN_QUALITY) { |
| LOG(ERROR) << "Invalid quality value: " << quality |
| << ", using default quality instead."; |
| compression_quality = BROTLI_MAX_QUALITY; |
| } |
| |
| BrotliEncoderSetParameter(brotli_encoder_state_, BROTLI_PARAM_QUALITY, |
| compression_quality); |
| BrotliEncoderSetParameter(brotli_encoder_state_, BROTLI_PARAM_LGWIN, |
| kBrotliDefaultLgwin); |
| } |
| } |
| |
| BrotliCompressor::~BrotliCompressor() { |
| if (brotli_encoder_state_) { |
| BrotliEncoderDestroyInstance(brotli_encoder_state_); |
| } |
| } |
| |
| bool BrotliCompressor::Write(const uint8_t* buf, size_t size) { |
| if (!brotli_encoder_state_) |
| return false; |
| |
| const uint8_t* next_in = buf; |
| size_t avail_in = size; |
| while (avail_in > 0) { |
| size_t avail_out = kBufferSize; |
| uint8_t* next_out = comp_buffer_.buffer_data(); |
| if (!BrotliEncoderCompressStream( |
| brotli_encoder_state_, BROTLI_OPERATION_PROCESS, &avail_in, |
| &next_in, &avail_out, &next_out, nullptr)) { |
| LOG(ERROR) << "BrotliCompressor failed to compress " << avail_in |
| << " bytes of data."; |
| return false; |
| } |
| |
| uint64_t output_bytes = comp_buffer_.buffer_size() - avail_out; |
| if (output_bytes > 0) { |
| comp_buffer_.AddDataToChunks(output_bytes); |
| } |
| } |
| return true; |
| } |
| |
| bool BrotliCompressor::Finish() { |
| if (!brotli_encoder_state_) |
| return false; |
| |
| const uint8_t* next_in = nullptr; |
| size_t avail_in = 0; |
| while (!BrotliEncoderIsFinished(brotli_encoder_state_)) { |
| size_t avail_out = kBufferSize; |
| uint8_t* next_out = comp_buffer_.buffer_data(); |
| if (!BrotliEncoderCompressStream( |
| brotli_encoder_state_, BROTLI_OPERATION_FINISH, &avail_in, &next_in, |
| &avail_out, &next_out, nullptr)) { |
| LOG(ERROR) << "BrotliCompressor failed to finish compression"; |
| return false; |
| } |
| |
| uint64_t output_bytes = comp_buffer_.buffer_size() - avail_out; |
| if (output_bytes > 0) { |
| comp_buffer_.AddDataToChunks(output_bytes); |
| } |
| } |
| return true; |
| } |
| |
| const std::vector<uint8_t>& BrotliCompressor::GetCompressedData() { |
| return comp_buffer_.GetCompressedData(); |
| } |
| |
| } // namespace bsdiff |