Home | History | Annotate | Download | only in bsdiff
      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