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 { 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 38 BrotliCompressor::~BrotliCompressor() { 39 if (brotli_encoder_state_) { 40 BrotliEncoderDestroyInstance(brotli_encoder_state_); 41 } 42 } 43 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 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 93 const std::vector<uint8_t>& BrotliCompressor::GetCompressedData() { 94 return comp_buffer_.GetCompressedData(); 95 } 96 97 } // namespace bsdiff 98