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/bz2_compressor.h" 6 7 #include <string.h> 8 9 #include "bsdiff/logging.h" 10 11 namespace { 12 13 // The BZ2 compression level used. Smaller compression levels are nowadays 14 // pointless. 15 const int kCompressionLevel = 9; 16 17 } // namespace 18 19 namespace bsdiff { 20 21 BZ2Compressor::BZ2Compressor() : comp_buffer_(1024 * 1024) { 22 memset(&bz_strm_, 0, sizeof(bz_strm_)); 23 int bz_err = BZ2_bzCompressInit(&bz_strm_, kCompressionLevel, 24 0 /* verbosity */, 0 /* workFactor */); 25 if (bz_err != BZ_OK) { 26 LOG(ERROR) << "Initializing bz_strm, bz_error=" << bz_err; 27 } else { 28 bz_strm_initialized_ = true; 29 } 30 } 31 32 BZ2Compressor::~BZ2Compressor() { 33 if (!bz_strm_initialized_) 34 return; 35 int bz_err = BZ2_bzCompressEnd(&bz_strm_); 36 if (bz_err != BZ_OK) { 37 LOG(ERROR) << "Deleting the compressor stream, bz_error=" << bz_err; 38 } 39 } 40 41 bool BZ2Compressor::Write(const uint8_t* buf, size_t size) { 42 if (!bz_strm_initialized_) 43 return false; 44 45 // The bz_stream struct defines the next_in as a non-const data pointer, 46 // although the documentation says it won't modify it. 47 bz_strm_.next_in = reinterpret_cast<char*>(const_cast<uint8_t*>(buf)); 48 bz_strm_.avail_in = size; 49 50 while (bz_strm_.avail_in) { 51 bz_strm_.next_out = reinterpret_cast<char*>(comp_buffer_.buffer_data()); 52 bz_strm_.avail_out = comp_buffer_.buffer_size(); 53 int bz_err = BZ2_bzCompress(&bz_strm_, BZ_RUN); 54 if (bz_err != BZ_RUN_OK) { 55 LOG(ERROR) << "Compressing data, bz_error=" << bz_err; 56 return false; 57 } 58 59 uint64_t output_bytes = comp_buffer_.buffer_size() - bz_strm_.avail_out; 60 if (output_bytes) { 61 comp_buffer_.AddDataToChunks(output_bytes); 62 } 63 } 64 return true; 65 } 66 67 bool BZ2Compressor::Finish() { 68 if (!bz_strm_initialized_) 69 return false; 70 bz_strm_.next_in = nullptr; 71 bz_strm_.avail_in = 0; 72 73 int bz_err = BZ_FINISH_OK; 74 while (bz_err == BZ_FINISH_OK) { 75 bz_strm_.next_out = reinterpret_cast<char*>(comp_buffer_.buffer_data()); 76 bz_strm_.avail_out = comp_buffer_.buffer_size(); 77 bz_err = BZ2_bzCompress(&bz_strm_, BZ_FINISH); 78 79 uint64_t output_bytes = comp_buffer_.buffer_size() - bz_strm_.avail_out; 80 if (output_bytes) { 81 comp_buffer_.AddDataToChunks(output_bytes); 82 } 83 } 84 if (bz_err != BZ_STREAM_END) { 85 LOG(ERROR) << "Finishing compressing data, bz_error=" << bz_err; 86 return false; 87 } 88 bz_err = BZ2_bzCompressEnd(&bz_strm_); 89 bz_strm_initialized_ = false; 90 if (bz_err != BZ_OK) { 91 LOG(ERROR) << "Deleting the compressor stream, bz_error=" << bz_err; 92 return false; 93 } 94 return true; 95 } 96 97 const std::vector<uint8_t>& BZ2Compressor::GetCompressedData() { 98 return comp_buffer_.GetCompressedData(); 99 } 100 101 } // namespace bsdiff 102