Add a brotli decompressor in bsdiff

This CL implements the brotli decompressor used in PatchReader. Also
extract the CompressorType and patch's MagicHeader to a common header file.

Bug: 34220646
Test: Run bsdiff/bspatch with brotli compressor/decompressor over a list
of files from angler's system image.

Change-Id: I76e11168075c6481490ffbc025ec4ca81e828732
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) {