Merge "Add a brotli decompressor in bsdiff"
diff --git a/Android.bp b/Android.bp
index fc224e9..0910688 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,9 +35,11 @@
defaults: ["bsdiff_defaults"],
srcs: [
+ "brotli_decompressor.cc",
"bspatch.cc",
"bz2_decompressor.cc",
"buffer_file.cc",
+ "decompressor_interface.cc",
"extents.cc",
"extents_file.cc",
"file.cc",
diff --git a/Makefile b/Makefile
index 6a06440..4906dd8 100644
--- a/Makefile
+++ b/Makefile
@@ -47,9 +47,11 @@
# "bspatch" program.
bspatch_src_files := \
+ brotli_decompressor.cc \
bspatch.cc \
bz2_decompressor.cc \
buffer_file.cc \
+ decompressor_interface.cc \
extents.cc \
extents_file.cc \
file.cc \
diff --git a/brotli_compressor_unittest.cc b/brotli_compressor_unittest.cc
index 5b968c3..079b985 100644
--- a/brotli_compressor_unittest.cc
+++ b/brotli_compressor_unittest.cc
@@ -6,9 +6,11 @@
#include <gtest/gtest.h>
+#include "bsdiff/brotli_decompressor.h"
+
namespace {
-const uint8_t kHelloWorld[] = {
+constexpr uint8_t kHelloWorld[] = {
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x0a,
};
} // namespace
@@ -22,8 +24,17 @@
std::vector<uint8_t> compressed_data = brotli_compressor.GetCompressedData();
EXPECT_GT(compressed_data.size(), static_cast<size_t>(0));
- // TODO(xunchang) run brotli decompressor and check we can get back
- // kHelloWorld.
+ // Run decompressor and check we can get the exact same data as kHelloWorld.
+ std::vector<uint8_t> decompressed_data(sizeof(kHelloWorld));
+ BrotliDecompressor brotli_decompressor;
+ EXPECT_TRUE(brotli_decompressor.SetInputData(compressed_data.data(),
+ compressed_data.size()));
+ EXPECT_TRUE(
+ brotli_decompressor.Read(decompressed_data.data(), sizeof(kHelloWorld)));
+ EXPECT_TRUE(brotli_decompressor.Close());
+ EXPECT_EQ(
+ std::vector<uint8_t>(kHelloWorld, kHelloWorld + sizeof(kHelloWorld)),
+ decompressed_data);
}
} // namespace bsdiff
\ No newline at end of file
diff --git a/brotli_decompressor.cc b/brotli_decompressor.cc
new file mode 100644
index 0000000..683e391
--- /dev/null
+++ b/brotli_decompressor.cc
@@ -0,0 +1,61 @@
+// 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/brotli_decompressor.h"
+
+#include "bsdiff/logging.h"
+
+using std::endl;
+
+namespace bsdiff {
+
+bool BrotliDecompressor::SetInputData(const uint8_t* input_data, size_t size) {
+ brotli_decoder_state_ =
+ BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+ if (brotli_decoder_state_ == nullptr) {
+ LOG(ERROR) << "Failed to initialize brotli decoder." << endl;
+ return false;
+ }
+ next_in_ = input_data;
+ available_in_ = size;
+ return true;
+}
+
+bool BrotliDecompressor::Read(uint8_t* output_data, size_t bytes_to_output) {
+ auto next_out = output_data;
+ size_t available_out = bytes_to_output;
+
+ while (available_out > 0) {
+ // The brotli decoder will update |available_in_|, |available_in_|,
+ // |next_out| and |available_out|.
+ BrotliDecoderResult result = BrotliDecoderDecompressStream(
+ brotli_decoder_state_, &available_in_, &next_in_, &available_out,
+ &next_out, nullptr);
+
+ if (result == BROTLI_DECODER_RESULT_ERROR) {
+ LOG(ERROR) << "Decompression failed with "
+ << BrotliDecoderErrorString(
+ BrotliDecoderGetErrorCode(brotli_decoder_state_))
+ << endl;
+ return false;
+ } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
+ LOG(ERROR) << "Decompressor reached EOF while reading from input stream."
+ << endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool BrotliDecompressor::Close() {
+ if (!BrotliDecoderIsFinished(brotli_decoder_state_)) {
+ LOG(ERROR) << "Unfinished brotli decoder." << endl;
+ return false;
+ }
+
+ BrotliDecoderDestroyInstance(brotli_decoder_state_);
+ return true;
+}
+
+} // namespace bsdiff
\ No newline at end of file
diff --git a/brotli_decompressor.h b/brotli_decompressor.h
new file mode 100644
index 0000000..ccfcb2d
--- /dev/null
+++ b/brotli_decompressor.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef _BSDIFF_BROTLI_DECOMPRESSOR_H_
+#define _BSDIFF_BROTLI_DECOMPRESSOR_H_
+
+#include <brotli/decode.h>
+
+#include "bsdiff/decompressor_interface.h"
+
+namespace bsdiff {
+
+class BrotliDecompressor : public DecompressorInterface {
+ public:
+ BrotliDecompressor()
+ : brotli_decoder_state_(nullptr), next_in_(nullptr), available_in_(0) {}
+
+ // DecompressorInterface overrides.
+ bool SetInputData(const uint8_t* input_data, size_t size) override;
+ bool Read(uint8_t* output_data, size_t bytes_to_output) override;
+ bool Close() override;
+
+ private:
+ BrotliDecoderState* brotli_decoder_state_;
+ const uint8_t* next_in_;
+ size_t available_in_;
+};
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_BROTLI_DECOMPRESSOR_H_
\ No newline at end of file
diff --git a/bsdiff.gyp b/bsdiff.gyp
index a2eeb47..61d2430 100644
--- a/bsdiff.gyp
+++ b/bsdiff.gyp
@@ -93,9 +93,11 @@
},
},
'sources': [
+ 'brotli_decompressor.cc',
'bspatch.cc',
'buffer_file.cc',
'bz2_decompressor.cc',
+ 'decompressor_interface.cc',
'extents.cc',
'extents_file.cc',
'file.cc',
diff --git a/compressor_interface.h b/compressor_interface.h
index 558a93e..53a33c8 100644
--- a/compressor_interface.h
+++ b/compressor_interface.h
@@ -11,12 +11,9 @@
#include <memory>
#include <vector>
-namespace bsdiff {
+#include "bsdiff/constants.h"
-enum class CompressorType {
- kBrotli,
- kBZ2,
-};
+namespace bsdiff {
class CompressorInterface {
public:
diff --git a/constants.h b/constants.h
new file mode 100644
index 0000000..fd0da26
--- /dev/null
+++ b/constants.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef _BSDIFF_CONSTANTS_H_
+#define _BSDIFF_CONSTANTS_H_
+
+#include <stdint.h>
+
+namespace bsdiff {
+
+enum class CompressorType {
+ kBrotli,
+ kBZ2,
+};
+
+// The header of the upstream's "BSDIFF40" format using BZ2 as compressor.
+constexpr uint8_t kMagicHeader[] = "BSDIFF40";
+
+} // namespace bsdiff
+
+
+#endif // _BSDIFF_CONSTANTS_H_
\ No newline at end of file
diff --git a/decompressor_interface.cc b/decompressor_interface.cc
new file mode 100644
index 0000000..69c611f
--- /dev/null
+++ b/decompressor_interface.cc
@@ -0,0 +1,20 @@
+// 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/decompressor_interface.h"
+
+#include "bsdiff/brotli_decompressor.h"
+#include "bsdiff/bz2_decompressor.h"
+
+namespace bsdiff {
+
+std::unique_ptr<DecompressorInterface> CreateDecompressor(CompressorType type) {
+ if (type == CompressorType::kBrotli) {
+ return std::unique_ptr<DecompressorInterface>(new BrotliDecompressor());
+ }
+ // Use BZ2 as a default decompressor.
+ return std::unique_ptr<DecompressorInterface>(new BZ2Decompressor());
+}
+
+} // namespace bsdiff
\ No newline at end of file
diff --git a/decompressor_interface.h b/decompressor_interface.h
index c072dbf..46ee444 100644
--- a/decompressor_interface.h
+++ b/decompressor_interface.h
@@ -8,6 +8,10 @@
#include <stddef.h>
#include <stdint.h>
+#include <memory>
+
+#include "bsdiff/constants.h"
+
namespace bsdiff {
class DecompressorInterface {
@@ -28,5 +32,7 @@
virtual bool Close() = 0;
};
+std::unique_ptr<DecompressorInterface> CreateDecompressor(CompressorType type);
+
} // namespace bsdiff
#endif // _BSDIFF_DECOMPRESSOR_INTERFACE_H_
diff --git a/patch_reader.cc b/patch_reader.cc
index ee9e9c4..5fd0ac6 100644
--- a/patch_reader.cc
+++ b/patch_reader.cc
@@ -8,8 +8,10 @@
#include <limits>
+#include "bsdiff/brotli_decompressor.h"
#include "bsdiff/bspatch.h"
#include "bsdiff/bz2_decompressor.h"
+#include "bsdiff/constants.h"
#include "bsdiff/logging.h"
#include "bsdiff/utils.h"
@@ -17,8 +19,6 @@
namespace bsdiff {
-const uint8_t kMagicHeader[] = "BSDIFF40";
-
bool BsdiffPatchReader::Init(const uint8_t* patch_data, size_t patch_size) {
// File format:
// 0 8 "BSDIFF40"
@@ -54,9 +54,9 @@
// TODO(xunchang) set the correct decompressor based on the info in the
// header.
- ctrl_stream_.reset(new BZ2Decompressor());
- diff_stream_.reset(new BZ2Decompressor());
- extra_stream_.reset(new BZ2Decompressor());
+ ctrl_stream_ = CreateDecompressor(CompressorType::kBZ2);
+ diff_stream_ = CreateDecompressor(CompressorType::kBZ2);
+ extra_stream_ = CreateDecompressor(CompressorType::kBZ2);
int64_t offset = 32;
if (!ctrl_stream_->SetInputData(const_cast<uint8_t*>(patch_data) + offset,
diff --git a/patch_reader.h b/patch_reader.h
index 766058e..fe0451b 100644
--- a/patch_reader.h
+++ b/patch_reader.h
@@ -16,8 +16,6 @@
namespace bsdiff {
-extern const uint8_t kMagicHeader[];
-
// A wrapper class to read and parse the data in a bsdiff patch. The reader
// class contains the concatenated streams of control, diff, and extra data.
// After initialization, this class provides the function to read the metadata
diff --git a/patch_writer.cc b/patch_writer.cc
index ea38c37..8354825 100644
--- a/patch_writer.cc
+++ b/patch_writer.cc
@@ -8,6 +8,7 @@
#include "bsdiff/brotli_compressor.h"
#include "bsdiff/bz2_compressor.h"
+#include "bsdiff/constants.h"
#include "bsdiff/control_entry.h"
#include "bsdiff/logging.h"
@@ -15,8 +16,6 @@
namespace {
-constexpr uint8_t kMagicHeader[] = "BSDIFF40";
-
void EncodeInt64(int64_t x, uint8_t* buf) {
uint64_t y = x < 0 ? (1ULL << 63ULL) - x : x;
for (int i = 0; i < 8; ++i) {