// 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