| // 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/bz2_compressor.h" |
| |
| #include <string.h> |
| |
| #include "bsdiff/logging.h" |
| |
| namespace { |
| |
| // The BZ2 compression level used. Smaller compression levels are nowadays |
| // pointless. |
| const int kCompressionLevel = 9; |
| |
| } // namespace |
| |
| namespace bsdiff { |
| |
| BZ2Compressor::BZ2Compressor() : comp_buffer_(1024 * 1024) { |
| memset(&bz_strm_, 0, sizeof(bz_strm_)); |
| int bz_err = BZ2_bzCompressInit(&bz_strm_, kCompressionLevel, |
| 0 /* verbosity */, 0 /* workFactor */); |
| if (bz_err != BZ_OK) { |
| LOG(ERROR) << "Initializing bz_strm, bz_error=" << bz_err; |
| } else { |
| bz_strm_initialized_ = true; |
| } |
| } |
| |
| BZ2Compressor::~BZ2Compressor() { |
| if (!bz_strm_initialized_) |
| return; |
| int bz_err = BZ2_bzCompressEnd(&bz_strm_); |
| if (bz_err != BZ_OK) { |
| LOG(ERROR) << "Deleting the compressor stream, bz_error=" << bz_err; |
| } |
| } |
| |
| bool BZ2Compressor::Write(const uint8_t* buf, size_t size) { |
| if (!bz_strm_initialized_) |
| return false; |
| |
| // The bz_stream struct defines the next_in as a non-const data pointer, |
| // although the documentation says it won't modify it. |
| bz_strm_.next_in = reinterpret_cast<char*>(const_cast<uint8_t*>(buf)); |
| bz_strm_.avail_in = size; |
| |
| while (bz_strm_.avail_in) { |
| bz_strm_.next_out = reinterpret_cast<char*>(comp_buffer_.buffer_data()); |
| bz_strm_.avail_out = comp_buffer_.buffer_size(); |
| int bz_err = BZ2_bzCompress(&bz_strm_, BZ_RUN); |
| if (bz_err != BZ_RUN_OK) { |
| LOG(ERROR) << "Compressing data, bz_error=" << bz_err; |
| return false; |
| } |
| |
| uint64_t output_bytes = comp_buffer_.buffer_size() - bz_strm_.avail_out; |
| if (output_bytes) { |
| comp_buffer_.AddDataToChunks(output_bytes); |
| } |
| } |
| return true; |
| } |
| |
| bool BZ2Compressor::Finish() { |
| if (!bz_strm_initialized_) |
| return false; |
| bz_strm_.next_in = nullptr; |
| bz_strm_.avail_in = 0; |
| |
| int bz_err = BZ_FINISH_OK; |
| while (bz_err == BZ_FINISH_OK) { |
| bz_strm_.next_out = reinterpret_cast<char*>(comp_buffer_.buffer_data()); |
| bz_strm_.avail_out = comp_buffer_.buffer_size(); |
| bz_err = BZ2_bzCompress(&bz_strm_, BZ_FINISH); |
| |
| uint64_t output_bytes = comp_buffer_.buffer_size() - bz_strm_.avail_out; |
| if (output_bytes) { |
| comp_buffer_.AddDataToChunks(output_bytes); |
| } |
| } |
| if (bz_err != BZ_STREAM_END) { |
| LOG(ERROR) << "Finishing compressing data, bz_error=" << bz_err; |
| return false; |
| } |
| bz_err = BZ2_bzCompressEnd(&bz_strm_); |
| bz_strm_initialized_ = false; |
| if (bz_err != BZ_OK) { |
| LOG(ERROR) << "Deleting the compressor stream, bz_error=" << bz_err; |
| return false; |
| } |
| return true; |
| } |
| |
| const std::vector<uint8_t>& BZ2Compressor::GetCompressedData() { |
| return comp_buffer_.GetCompressedData(); |
| } |
| |
| } // namespace bsdiff |