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