mirror of
https://github.com/expo/bsdiff.git
synced 2026-11-14 17:49:03 -10:00
Merge changes Icd4322f2,I441ae663
am: 0525134d89
Change-Id: Ia592ec1577686a176aef4732cbcc945a8de68c00
This commit is contained in:
commit
8ccc9ed515
11 files changed
+284
-96
No files matched your search
+1
-1
@@ -30,7 +30,7 @@ class BrotliCompressor : public CompressorInterface {
|
||||
bool Write(const uint8_t* buf, size_t size) override;
|
||||
bool Finish() override;
|
||||
const std::vector<uint8_t>& GetCompressedData() override;
|
||||
CompressorType Type() override { return CompressorType::kBrotli; }
|
||||
CompressorType Type() const override { return CompressorType::kBrotli; }
|
||||
|
||||
private:
|
||||
BrotliEncoderState* brotli_encoder_state_;
|
||||
|
||||
+56
-25
@@ -41,21 +41,34 @@ const uint32_t kBrotliDefaultQuality = BROTLI_MAX_QUALITY;
|
||||
|
||||
namespace bsdiff {
|
||||
|
||||
std::vector<CompressorType> BsdiffArguments::compressor_types() const {
|
||||
return std::vector<CompressorType>(compressor_types_.begin(),
|
||||
compressor_types_.end());
|
||||
}
|
||||
|
||||
bool BsdiffArguments::IsValid() const {
|
||||
if (compressor_type_ == CompressorType::kBrotli &&
|
||||
if (compressor_types_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsCompressorSupported(CompressorType::kBrotli) &&
|
||||
(brotli_quality_ < BROTLI_MIN_QUALITY ||
|
||||
brotli_quality_ > BROTLI_MAX_QUALITY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (format_ == BsdiffFormat::kLegacy) {
|
||||
return compressor_type_ == CompressorType::kBZ2;
|
||||
return compressor_types_.size() == 1 &&
|
||||
IsCompressorSupported(CompressorType::kBZ2);
|
||||
} else if (format_ == BsdiffFormat::kBsdf2) {
|
||||
return (compressor_type_ == CompressorType::kBZ2 ||
|
||||
compressor_type_ == CompressorType::kBrotli);
|
||||
} else if (format_ == BsdiffFormat::kEndsley) {
|
||||
// All compression options are valid for this format.
|
||||
if (IsCompressorSupported(CompressorType::kNoCompression)) {
|
||||
std::cerr << "no compression is not supported in Bsdf2 format\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (format_ == BsdiffFormat::kEndsley) {
|
||||
// Only one compressor is supported for this format.
|
||||
return compressor_types_.size() == 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -78,7 +91,7 @@ bool BsdiffArguments::ParseCommandLine(int argc, char** argv) {
|
||||
return false;
|
||||
}
|
||||
} else if (name == "type") {
|
||||
if (!ParseCompressorType(optarg, &compressor_type_)) {
|
||||
if (!ParseCompressorTypes(optarg, &compressor_types_)) {
|
||||
return false;
|
||||
}
|
||||
} else if (name == "brotli_quality") {
|
||||
@@ -94,9 +107,9 @@ bool BsdiffArguments::ParseCommandLine(int argc, char** argv) {
|
||||
|
||||
// If quality is uninitialized for brotli, set it to default value.
|
||||
if (format_ != BsdiffFormat::kLegacy &&
|
||||
compressor_type_ == CompressorType::kBrotli && brotli_quality_ == -1) {
|
||||
IsCompressorSupported(CompressorType::kBrotli) && brotli_quality_ == -1) {
|
||||
brotli_quality_ = kBrotliDefaultQuality;
|
||||
} else if (compressor_type_ != CompressorType::kBrotli &&
|
||||
} else if (!IsCompressorSupported(CompressorType::kBrotli) &&
|
||||
brotli_quality_ != -1) {
|
||||
std::cerr << "Warning: Brotli quality is only used in the brotli"
|
||||
" compressor.\n";
|
||||
@@ -105,23 +118,37 @@ bool BsdiffArguments::ParseCommandLine(int argc, char** argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffArguments::ParseCompressorType(const string& str,
|
||||
CompressorType* type) {
|
||||
string type_string = str;
|
||||
std::transform(type_string.begin(), type_string.end(), type_string.begin(),
|
||||
::tolower);
|
||||
if (type_string == kNoCompressionString) {
|
||||
*type = CompressorType::kNoCompression;
|
||||
return true;
|
||||
} else if (type_string == kBZ2String) {
|
||||
*type = CompressorType::kBZ2;
|
||||
return true;
|
||||
} else if (type_string == kBrotliString) {
|
||||
*type = CompressorType::kBrotli;
|
||||
return true;
|
||||
bool BsdiffArguments::ParseCompressorTypes(const string& str,
|
||||
std::set<CompressorType>* types) {
|
||||
types->clear();
|
||||
// The expected types string is separated by ":", e.g. bz2:brotli
|
||||
std::vector<std::string> type_list;
|
||||
size_t base = 0;
|
||||
size_t found;
|
||||
while (true) {
|
||||
found = str.find(":", base);
|
||||
type_list.emplace_back(str, base, found - base);
|
||||
|
||||
if (found == str.npos)
|
||||
break;
|
||||
base = found + 1;
|
||||
}
|
||||
std::cerr << "Failed to parse compressor type in " << str << endl;
|
||||
return false;
|
||||
|
||||
for (auto& type : type_list) {
|
||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||
if (type == kNoCompressionString) {
|
||||
types->emplace(CompressorType::kNoCompression);
|
||||
} else if (type == kBZ2String) {
|
||||
types->emplace(CompressorType::kBZ2);
|
||||
} else if (type == kBrotliString) {
|
||||
types->emplace(CompressorType::kBrotli);
|
||||
} else {
|
||||
std::cerr << "Failed to parse compressor type in " << str << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffArguments::ParseMinLength(const string& str, size_t* len) {
|
||||
@@ -182,4 +209,8 @@ bool BsdiffArguments::ParseQuality(const string& str,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffArguments::IsCompressorSupported(CompressorType type) const {
|
||||
return compressor_types_.find(type) != compressor_types_.end();
|
||||
}
|
||||
|
||||
} // namespace bsdiff
|
||||
+16
-10
@@ -7,7 +7,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "bsdiff/constants.h"
|
||||
#include "bsdiff/patch_writer_interface.h"
|
||||
@@ -18,14 +20,15 @@ namespace bsdiff {
|
||||
// brotli_quality.
|
||||
class BsdiffArguments {
|
||||
public:
|
||||
BsdiffArguments()
|
||||
: format_(BsdiffFormat::kLegacy),
|
||||
compressor_type_(CompressorType::kBZ2),
|
||||
brotli_quality_(-1) {}
|
||||
BsdiffArguments() : format_(BsdiffFormat::kLegacy), brotli_quality_(-1) {
|
||||
compressor_types_.emplace(CompressorType::kBZ2);
|
||||
}
|
||||
|
||||
BsdiffArguments(BsdiffFormat format, CompressorType type, int brotli_quality)
|
||||
BsdiffArguments(BsdiffFormat format,
|
||||
std::set<CompressorType> types,
|
||||
int brotli_quality)
|
||||
: format_(format),
|
||||
compressor_type_(type),
|
||||
compressor_types_(std::move(types)),
|
||||
brotli_quality_(brotli_quality) {}
|
||||
|
||||
// Check if the compressor type is compatible with the bsdiff format.
|
||||
@@ -36,7 +39,7 @@ class BsdiffArguments {
|
||||
|
||||
int min_length() const { return min_length_; }
|
||||
|
||||
CompressorType compressor_type() const { return compressor_type_; }
|
||||
std::vector<CompressorType> compressor_types() const;
|
||||
|
||||
int brotli_quality() const { return brotli_quality_; }
|
||||
|
||||
@@ -45,7 +48,8 @@ class BsdiffArguments {
|
||||
bool ParseCommandLine(int argc, char** argv);
|
||||
|
||||
// Parse the compression type from string.
|
||||
static bool ParseCompressorType(const std::string& str, CompressorType* type);
|
||||
static bool ParseCompressorTypes(const std::string& str,
|
||||
std::set<CompressorType>* types);
|
||||
|
||||
// Parse the minimum length parameter from string.
|
||||
static bool ParseMinLength(const std::string& str, size_t* len);
|
||||
@@ -61,11 +65,13 @@ class BsdiffArguments {
|
||||
int max);
|
||||
|
||||
private:
|
||||
bool IsCompressorSupported(CompressorType type) const;
|
||||
|
||||
// Current format supported are the legacy "BSDIFF40" or "BSDF2".
|
||||
BsdiffFormat format_;
|
||||
|
||||
// The algorithm to compress the patch, i.e. BZ2 or Brotli.
|
||||
CompressorType compressor_type_;
|
||||
// The algorithms to compress the patch, e.g. bz2, brotli.
|
||||
std::set<CompressorType> compressor_types_;
|
||||
|
||||
// The quality of brotli compressor.
|
||||
int brotli_quality_;
|
||||
|
||||
@@ -4,21 +4,41 @@
|
||||
|
||||
#include "bsdiff/bsdiff_arguments.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace bsdiff {
|
||||
|
||||
TEST(BsdiffArgumentsTest, ParseCompressorTypeTest) {
|
||||
CompressorType type;
|
||||
EXPECT_TRUE(BsdiffArguments::ParseCompressorType("Brotli", &type));
|
||||
EXPECT_EQ(CompressorType::kBrotli, type);
|
||||
std::set<CompressorType> types;
|
||||
EXPECT_TRUE(BsdiffArguments::ParseCompressorTypes("Brotli", &types));
|
||||
EXPECT_EQ(1U, types.size());
|
||||
EXPECT_NE(types.end(), types.find(CompressorType::kBrotli));
|
||||
|
||||
EXPECT_TRUE(BsdiffArguments::ParseCompressorType("bz2", &type));
|
||||
EXPECT_EQ(CompressorType::kBZ2, type);
|
||||
types.clear();
|
||||
|
||||
EXPECT_FALSE(BsdiffArguments::ParseCompressorType("invalid", &type));
|
||||
EXPECT_TRUE(BsdiffArguments::ParseCompressorTypes("bz2", &types));
|
||||
EXPECT_EQ(1U, types.size());
|
||||
EXPECT_NE(types.end(), types.find(CompressorType::kBZ2));
|
||||
|
||||
types.clear();
|
||||
|
||||
EXPECT_FALSE(BsdiffArguments::ParseCompressorTypes("invalid", &types));
|
||||
}
|
||||
|
||||
TEST(BsdiffArgumentsTest, ParseMultipleCompressorTypeTest) {
|
||||
std::set<CompressorType> types;
|
||||
EXPECT_TRUE(BsdiffArguments::ParseCompressorTypes("bz2:brotli:nocompression",
|
||||
&types));
|
||||
EXPECT_EQ(3U, types.size());
|
||||
EXPECT_NE(types.end(), types.find(CompressorType::kBrotli));
|
||||
EXPECT_NE(types.end(), types.find(CompressorType::kBZ2));
|
||||
EXPECT_NE(types.end(), types.find(CompressorType::kNoCompression));
|
||||
|
||||
types.clear();
|
||||
|
||||
// No space in the type string.
|
||||
EXPECT_FALSE(
|
||||
BsdiffArguments::ParseCompressorTypes("bz2 : nocompression", &types));
|
||||
}
|
||||
|
||||
TEST(BsdiffArgumentsTest, ParseBsdiffFormatTest) {
|
||||
@@ -66,28 +86,34 @@ TEST(BsdiffArgumentsTest, ArgumentsValidTest) {
|
||||
|
||||
// brotli is not supported for BsdiffFormat::kLegacy.
|
||||
EXPECT_FALSE(
|
||||
BsdiffArguments(BsdiffFormat::kLegacy, CompressorType::kBrotli, -1)
|
||||
BsdiffArguments(BsdiffFormat::kLegacy, {CompressorType::kBrotli}, -1)
|
||||
.IsValid());
|
||||
|
||||
EXPECT_TRUE(BsdiffArguments(BsdiffFormat::kBsdf2, CompressorType::kBrotli, 9)
|
||||
.IsValid());
|
||||
EXPECT_TRUE(
|
||||
BsdiffArguments(BsdiffFormat::kBsdf2, {CompressorType::kBrotli}, 9)
|
||||
.IsValid());
|
||||
|
||||
// Compression quality out of range for brotli.
|
||||
EXPECT_FALSE(
|
||||
BsdiffArguments(BsdiffFormat::kBsdf2, CompressorType::kBrotli, 20)
|
||||
BsdiffArguments(BsdiffFormat::kBsdf2, {CompressorType::kBrotli}, 20)
|
||||
.IsValid());
|
||||
}
|
||||
|
||||
TEST(BsdiffArgumentsTest, ParseArgumentsSmokeTest) {
|
||||
std::vector<const char*> args = {"bsdiff", "--format=bsdf2", "--type=brotli",
|
||||
"--brotli_quality=9", "--minlen=12"};
|
||||
std::vector<const char*> args = {"bsdiff", "--format=bsdf2",
|
||||
"--type=brotli:bz2", "--brotli_quality=9",
|
||||
"--minlen=12"};
|
||||
|
||||
BsdiffArguments arguments;
|
||||
EXPECT_TRUE(
|
||||
arguments.ParseCommandLine(args.size(), const_cast<char**>(args.data())));
|
||||
|
||||
EXPECT_EQ(BsdiffFormat::kBsdf2, arguments.format());
|
||||
EXPECT_EQ(CompressorType::kBrotli, arguments.compressor_type());
|
||||
|
||||
std::vector<CompressorType> types = {CompressorType::kBZ2,
|
||||
CompressorType::kBrotli};
|
||||
EXPECT_EQ(types, arguments.compressor_types());
|
||||
|
||||
EXPECT_EQ(9, arguments.brotli_quality());
|
||||
EXPECT_EQ(12, arguments.min_length());
|
||||
}
|
||||
|
||||
+4
-3
@@ -80,11 +80,11 @@ int GenerateBsdiffFromFiles(const char* old_filename,
|
||||
patch_writer = bsdiff::CreateBsdiffPatchWriter(patch_filename);
|
||||
} else if (arguments.format() == bsdiff::BsdiffFormat::kBsdf2) {
|
||||
patch_writer = bsdiff::CreateBSDF2PatchWriter(patch_filename,
|
||||
arguments.compressor_type(),
|
||||
arguments.compressor_types(),
|
||||
arguments.brotli_quality());
|
||||
} else if (arguments.format() == bsdiff::BsdiffFormat::kEndsley) {
|
||||
patch_writer = bsdiff::CreateEndsleyPatchWriter(
|
||||
&raw_data, arguments.compressor_type(), arguments.brotli_quality());
|
||||
&raw_data, arguments.compressor_types()[0], arguments.brotli_quality());
|
||||
} else {
|
||||
std::cerr << "unexpected bsdiff format." << std::endl;
|
||||
return 1;
|
||||
@@ -120,7 +120,8 @@ void PrintUsage(const std::string& proc_name) {
|
||||
<< " --minlen LEN The minimum match length "
|
||||
"required to consider a match in the algorithm.\n"
|
||||
<< " --type <bz2|brotli|nocompression> The algorithm to compress "
|
||||
"the patch, bsdf2 format only.\n"
|
||||
"the patch, bsdf2 format only. Multiple supported compressors "
|
||||
"should be split by ':', e.g. bz2:brotli.\n"
|
||||
<< " --brotli_quality Quality of the brotli "
|
||||
"compressor.\n";
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class BZ2Compressor : public CompressorInterface {
|
||||
bool Write(const uint8_t* buf, size_t size) override;
|
||||
bool Finish() override;
|
||||
const std::vector<uint8_t>& GetCompressedData() override;
|
||||
CompressorType Type() override { return CompressorType::kBZ2; }
|
||||
CompressorType Type() const override { return CompressorType::kBZ2; }
|
||||
|
||||
private:
|
||||
// The low-level bzip2 stream.
|
||||
|
||||
@@ -29,7 +29,7 @@ class CompressorInterface {
|
||||
virtual const std::vector<uint8_t>& GetCompressedData() = 0;
|
||||
|
||||
// Return the type of the current compressor.
|
||||
virtual CompressorType Type() = 0;
|
||||
virtual CompressorType Type() const = 0;
|
||||
|
||||
protected:
|
||||
CompressorInterface() = default;
|
||||
|
||||
@@ -30,6 +30,14 @@ std::unique_ptr<PatchWriterInterface> CreateBSDF2PatchWriter(
|
||||
CompressorType type,
|
||||
int brotli_quality);
|
||||
|
||||
// Create a patch writer using the "BSDF2" patch format. It also tries all the
|
||||
// compressors in |types| to generate the smallest patch.
|
||||
BSDIFF_EXPORT
|
||||
std::unique_ptr<PatchWriterInterface> CreateBSDF2PatchWriter(
|
||||
const std::string& patch_filename,
|
||||
const std::vector<CompressorType>& types,
|
||||
int brotli_quality);
|
||||
|
||||
// Create a patch writer compatible with Android Play Store bsdiff patches.
|
||||
// The data will be written to the passed |patch| vector, which must be valid
|
||||
// until Close() is called or this patch is destroyed. The data will be
|
||||
|
||||
+118
-31
@@ -5,6 +5,7 @@
|
||||
#include "bsdiff/patch_writer.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <limits>
|
||||
|
||||
#include "bsdiff/brotli_compressor.h"
|
||||
#include "bsdiff/bz2_compressor.h"
|
||||
@@ -27,30 +28,64 @@ void EncodeInt64(int64_t x, uint8_t* buf) {
|
||||
namespace bsdiff {
|
||||
|
||||
BsdiffPatchWriter::BsdiffPatchWriter(const std::string& patch_filename)
|
||||
: patch_filename_(patch_filename), format_(BsdiffFormat::kLegacy) {
|
||||
ctrl_stream_.reset(new BZ2Compressor());
|
||||
diff_stream_.reset(new BZ2Compressor());
|
||||
extra_stream_.reset(new BZ2Compressor());
|
||||
: patch_filename_(patch_filename),
|
||||
format_(BsdiffFormat::kLegacy),
|
||||
brotli_quality_(-1) {
|
||||
types_.emplace_back(CompressorType::kBZ2);
|
||||
}
|
||||
|
||||
|
||||
BsdiffPatchWriter::BsdiffPatchWriter(const std::string& patch_filename,
|
||||
CompressorType type,
|
||||
const std::vector<CompressorType>& types,
|
||||
int brotli_quality)
|
||||
: patch_filename_(patch_filename), format_(BsdiffFormat::kBsdf2) {
|
||||
if (type == CompressorType::kBZ2) {
|
||||
ctrl_stream_.reset(new BZ2Compressor());
|
||||
diff_stream_.reset(new BZ2Compressor());
|
||||
extra_stream_.reset(new BZ2Compressor());
|
||||
} else if (type == CompressorType::kBrotli) {
|
||||
ctrl_stream_.reset(new BrotliCompressor(brotli_quality));
|
||||
diff_stream_.reset(new BrotliCompressor(brotli_quality));
|
||||
extra_stream_.reset(new BrotliCompressor(brotli_quality));
|
||||
: patch_filename_(patch_filename),
|
||||
format_(BsdiffFormat::kBsdf2),
|
||||
types_(types),
|
||||
brotli_quality_(brotli_quality) {}
|
||||
|
||||
bool BsdiffPatchWriter::InitializeCompressorList(
|
||||
std::vector<std::unique_ptr<bsdiff::CompressorInterface>>*
|
||||
compressor_list) {
|
||||
if (types_.empty()) {
|
||||
LOG(ERROR) << "Patch writer expects at least one compressor.";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& type : types_) {
|
||||
switch (type) {
|
||||
case CompressorType::kBZ2:
|
||||
compressor_list->emplace_back(new BZ2Compressor());
|
||||
break;
|
||||
case CompressorType::kBrotli:
|
||||
compressor_list->emplace_back(new BrotliCompressor(brotli_quality_));
|
||||
break;
|
||||
case CompressorType::kNoCompression:
|
||||
LOG(ERROR) << "Unsupported compression type " << static_cast<int>(type);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& compressor : *compressor_list) {
|
||||
if (!compressor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::Init(size_t /* new_size */) {
|
||||
if (!(ctrl_stream_ && diff_stream_ && extra_stream_)) {
|
||||
LOG(ERROR) << "uninitialized compressor stream";
|
||||
if (!InitializeCompressorList(&ctrl_stream_list_)) {
|
||||
LOG(ERROR) << "Failed to initialize control stream compressors.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitializeCompressorList(&diff_stream_list_)) {
|
||||
LOG(ERROR) << "Failed to initialize diff stream compressors.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitializeCompressorList(&extra_stream_list_)) {
|
||||
LOG(ERROR) << "Failed to initialize extra stream compressors.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -63,11 +98,23 @@ bool BsdiffPatchWriter::Init(size_t /* new_size */) {
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::WriteDiffStream(const uint8_t* data, size_t size) {
|
||||
return diff_stream_->Write(data, size);
|
||||
for (const auto& compressor : diff_stream_list_) {
|
||||
if (!compressor->Write(data, size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::WriteExtraStream(const uint8_t* data, size_t size) {
|
||||
return extra_stream_->Write(data, size);
|
||||
for (const auto& compressor : extra_stream_list_) {
|
||||
if (!compressor->Write(data, size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::AddControlEntry(const ControlEntry& entry) {
|
||||
@@ -76,29 +123,69 @@ bool BsdiffPatchWriter::AddControlEntry(const ControlEntry& entry) {
|
||||
EncodeInt64(entry.diff_size, buf);
|
||||
EncodeInt64(entry.extra_size, buf + 8);
|
||||
EncodeInt64(entry.offset_increment, buf + 16);
|
||||
if (!ctrl_stream_->Write(buf, sizeof(buf)))
|
||||
return false;
|
||||
|
||||
for (const auto& compressor : ctrl_stream_list_) {
|
||||
if (!compressor->Write(buf, sizeof(buf))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
written_output_ += entry.diff_size + entry.extra_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::SelectSmallestResult(
|
||||
const std::vector<std::unique_ptr<CompressorInterface>>& compressor_list,
|
||||
CompressorInterface** smallest_compressor) {
|
||||
size_t min_size = std::numeric_limits<size_t>::max();
|
||||
for (const auto& compressor : compressor_list) {
|
||||
if (!compressor->Finish()) {
|
||||
LOG(ERROR) << "Failed to finalize compressed streams.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update |smallest_compressor| if the current compressor produces a
|
||||
// smaller result.
|
||||
if (compressor->GetCompressedData().size() < min_size) {
|
||||
min_size = compressor->GetCompressedData().size();
|
||||
*smallest_compressor = compressor.get();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::Close() {
|
||||
if (!fp_) {
|
||||
LOG(ERROR) << "File not open.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ctrl_stream_->Finish() || !diff_stream_->Finish() ||
|
||||
!extra_stream_->Finish()) {
|
||||
LOG(ERROR) << "Finalizing compressed streams.";
|
||||
CompressorInterface* ctrl_stream = nullptr;
|
||||
if (!SelectSmallestResult(ctrl_stream_list_, &ctrl_stream) || !ctrl_stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctrl_data = ctrl_stream_->GetCompressedData();
|
||||
auto diff_data = diff_stream_->GetCompressedData();
|
||||
auto extra_data = extra_stream_->GetCompressedData();
|
||||
CompressorInterface* diff_stream = nullptr;
|
||||
if (!SelectSmallestResult(diff_stream_list_, &diff_stream) || !diff_stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WriteHeader(ctrl_data.size(), diff_data.size()))
|
||||
CompressorInterface* extra_stream = nullptr;
|
||||
if (!SelectSmallestResult(extra_stream_list_, &extra_stream) ||
|
||||
!extra_stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctrl_data = ctrl_stream->GetCompressedData();
|
||||
auto diff_data = diff_stream->GetCompressedData();
|
||||
auto extra_data = extra_stream->GetCompressedData();
|
||||
|
||||
uint8_t types[3] = {static_cast<uint8_t>(ctrl_stream->Type()),
|
||||
static_cast<uint8_t>(diff_stream->Type()),
|
||||
static_cast<uint8_t>(extra_stream->Type())};
|
||||
|
||||
if (!WriteHeader(types, ctrl_data.size(), diff_data.size()))
|
||||
return false;
|
||||
|
||||
if (fwrite(ctrl_data.data(), 1, ctrl_data.size(), fp_) != ctrl_data.size()) {
|
||||
@@ -122,7 +209,9 @@ bool BsdiffPatchWriter::Close() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::WriteHeader(uint64_t ctrl_size, uint64_t diff_size) {
|
||||
bool BsdiffPatchWriter::WriteHeader(uint8_t types[3],
|
||||
uint64_t ctrl_size,
|
||||
uint64_t diff_size) {
|
||||
/* Header format is
|
||||
* 0 8 magic header
|
||||
* 8 8 length of compressed ctrl block
|
||||
@@ -146,9 +235,7 @@ bool BsdiffPatchWriter::WriteHeader(uint64_t ctrl_size, uint64_t diff_size) {
|
||||
// 6 1 compressed type for diff stream
|
||||
// 7 1 compressed type for extra stream
|
||||
memcpy(header, kBSDF2MagicHeader, 5);
|
||||
header[5] = static_cast<uint8_t>(ctrl_stream_->Type());
|
||||
header[6] = static_cast<uint8_t>(diff_stream_->Type());
|
||||
header[7] = static_cast<uint8_t>(extra_stream_->Type());
|
||||
memcpy(header + 5, types, 3);
|
||||
} else {
|
||||
LOG(ERROR) << "Unsupported bsdiff format.";
|
||||
return false;
|
||||
|
||||
+29
-8
@@ -26,7 +26,7 @@ class BsdiffPatchWriter : public PatchWriterInterface {
|
||||
// with algorithm |type|; and quality |brotli_quality| if it's brotli. This
|
||||
// writer also writes the patch data to the file |patch_filename|.
|
||||
BsdiffPatchWriter(const std::string& patch_filename,
|
||||
CompressorType type,
|
||||
const std::vector<CompressorType>& types,
|
||||
int brotli_quality);
|
||||
|
||||
// PatchWriterInterface overrides.
|
||||
@@ -37,9 +37,24 @@ class BsdiffPatchWriter : public PatchWriterInterface {
|
||||
bool Close() override;
|
||||
|
||||
private:
|
||||
// Write the BSDIFF patch header to the |fp_| given the size of the compressed
|
||||
// control block and the compressed diff block.
|
||||
bool WriteHeader(uint64_t ctrl_size, uint64_t diff_size);
|
||||
// Add supported compressors to |compressor_list|; return false if we failed
|
||||
// to initialize one of them.
|
||||
bool InitializeCompressorList(
|
||||
std::vector<std::unique_ptr<CompressorInterface>>* compressor_list);
|
||||
|
||||
// Select the compressor in |compressor_list| that produces the smallest
|
||||
// patch, and put the result in |smallest_compressor|.
|
||||
bool SelectSmallestResult(
|
||||
const std::vector<std::unique_ptr<CompressorInterface>>& compressor_list,
|
||||
CompressorInterface** smallest_compressor);
|
||||
|
||||
|
||||
// Write the BSDIFF patch header to the |fp_|.
|
||||
// Arguments:
|
||||
// A three bytes array with the compressor types of ctrl|diff|extra stream
|
||||
// Size of the compressed control block
|
||||
// Size of the compressed diff block.
|
||||
bool WriteHeader(uint8_t types[3], uint64_t ctrl_size, uint64_t diff_size);
|
||||
|
||||
// Bytes of the new files already written. Needed to store the new length in
|
||||
// the header of the file.
|
||||
@@ -52,10 +67,16 @@ class BsdiffPatchWriter : public PatchWriterInterface {
|
||||
// The format of bsdiff we're using.
|
||||
BsdiffFormat format_;
|
||||
|
||||
// The three internal compressed streams.
|
||||
std::unique_ptr<CompressorInterface> ctrl_stream_{nullptr};
|
||||
std::unique_ptr<CompressorInterface> diff_stream_{nullptr};
|
||||
std::unique_ptr<CompressorInterface> extra_stream_{nullptr};
|
||||
// The compressors we're using.
|
||||
std::vector<CompressorType> types_;
|
||||
|
||||
// The compression quality of the brotli compressor.
|
||||
int brotli_quality_;
|
||||
|
||||
// The list of compressors to try for each stream.
|
||||
std::vector<std::unique_ptr<CompressorInterface>> ctrl_stream_list_;
|
||||
std::vector<std::unique_ptr<CompressorInterface>> diff_stream_list_;
|
||||
std::vector<std::unique_ptr<CompressorInterface>> extra_stream_list_;
|
||||
};
|
||||
|
||||
} // namespace bsdiff
|
||||
|
||||
@@ -19,8 +19,16 @@ std::unique_ptr<PatchWriterInterface> CreateBSDF2PatchWriter(
|
||||
const std::string& patch_filename,
|
||||
CompressorType type,
|
||||
int brotli_quality) {
|
||||
return CreateBSDF2PatchWriter(
|
||||
patch_filename, std::vector<CompressorType>{type}, brotli_quality);
|
||||
}
|
||||
|
||||
std::unique_ptr<PatchWriterInterface> CreateBSDF2PatchWriter(
|
||||
const std::string& patch_filename,
|
||||
const std::vector<CompressorType>& types,
|
||||
int brotli_quality) {
|
||||
return std::unique_ptr<PatchWriterInterface>(
|
||||
new BsdiffPatchWriter(patch_filename, type, brotli_quality));
|
||||
new BsdiffPatchWriter(patch_filename, types, brotli_quality));
|
||||
}
|
||||
|
||||
std::unique_ptr<PatchWriterInterface> CreateEndsleyPatchWriter(
|
||||
|
||||
Reference in new issue
Block a user