Merge "Add flags to bsdiff main to generate the Endsley format." am: aa3cb25c11 am: db3afbd069
am: 90e451a183

Change-Id: If1fd2ad6fe890fb787387e7337a74963b344b753
diff --git a/bsdiff_arguments.cc b/bsdiff_arguments.cc
index d6ca2be..ebcddf1 100644
--- a/bsdiff_arguments.cc
+++ b/bsdiff_arguments.cc
@@ -25,6 +25,7 @@
 constexpr char kLegacyString[] = "legacy";
 constexpr char kBsdf2String[] = "bsdf2";
 constexpr char kBsdiff40String[] = "bsdiff40";
+constexpr char kEndsleyString[] = "endsley";
 
 const struct option OPTIONS[] = {
     {"format", required_argument, nullptr, 0},
@@ -41,16 +42,20 @@
 namespace bsdiff {
 
 bool BsdiffArguments::IsValid() const {
+  if (compressor_type_ == CompressorType::kBrotli &&
+      (compression_quality_ < BROTLI_MIN_QUALITY ||
+       compression_quality_ > BROTLI_MAX_QUALITY)) {
+    return false;
+  }
+
   if (format_ == BsdiffFormat::kLegacy) {
-    return (compressor_type_ == CompressorType::kBZ2);
+    return compressor_type_ == CompressorType::kBZ2;
   } else if (format_ == BsdiffFormat::kBsdf2) {
-    if (compressor_type_ == CompressorType::kBZ2) {
-      return true;
-    }
-    if (compressor_type_ == CompressorType::kBrotli) {
-      return (compression_quality_ >= BROTLI_MIN_QUALITY &&
-              compression_quality_ <= BROTLI_MAX_QUALITY);
-    }
+    return (compressor_type_ == CompressorType::kBZ2 ||
+            compressor_type_ == CompressorType::kBrotli);
+  } else if (format_ == BsdiffFormat::kEndsley) {
+    // All compression options are valid for this format.
+    return true;
   }
   return false;
 }
@@ -87,7 +92,7 @@
   }
 
   // If quality is uninitialized for brotli, set it to default value.
-  if (format_ == BsdiffFormat::kBsdf2 &&
+  if (format_ != BsdiffFormat::kLegacy &&
       compressor_type_ == CompressorType::kBrotli &&
       compression_quality_ == -1) {
     compression_quality_ = kBrotliDefaultQuality;
@@ -148,6 +153,9 @@
   } else if (format_string == kBsdf2String) {
     *format = BsdiffFormat::kBsdf2;
     return true;
+  } else if (format_string == kEndsleyString) {
+    *format = BsdiffFormat::kEndsley;
+    return true;
   }
   std::cerr << "Failed to parse bsdiff format in " << str << endl;
   return false;
diff --git a/bsdiff_arguments_unittest.cc b/bsdiff_arguments_unittest.cc
index 3f061c9..412b8e9 100644
--- a/bsdiff_arguments_unittest.cc
+++ b/bsdiff_arguments_unittest.cc
@@ -32,6 +32,9 @@
   EXPECT_TRUE(BsdiffArguments::ParseBsdiffFormat("bsdiff40", &format));
   EXPECT_EQ(BsdiffFormat::kLegacy, format);
 
+  EXPECT_TRUE(BsdiffArguments::ParseBsdiffFormat("endsley", &format));
+  EXPECT_EQ(BsdiffFormat::kEndsley, format);
+
   EXPECT_FALSE(BsdiffArguments::ParseBsdiffFormat("Other", &format));
 }
 
diff --git a/bsdiff_main.cc b/bsdiff_main.cc
index 6747055..bed38b6 100644
--- a/bsdiff_main.cc
+++ b/bsdiff_main.cc
@@ -74,12 +74,18 @@
   }
 
   std::unique_ptr<bsdiff::PatchWriterInterface> patch_writer;
+  std::vector<uint8_t> raw_data;
+
   if (arguments.format() == bsdiff::BsdiffFormat::kLegacy) {
     patch_writer = bsdiff::CreateBsdiffPatchWriter(patch_filename);
   } else if (arguments.format() == bsdiff::BsdiffFormat::kBsdf2) {
     patch_writer = bsdiff::CreateBSDF2PatchWriter(
         patch_filename, arguments.compressor_type(),
         arguments.compression_quality());
+  } else if (arguments.format() == bsdiff::BsdiffFormat::kEndsley) {
+    patch_writer =
+        bsdiff::CreateEndsleyPatchWriter(&raw_data, arguments.compressor_type(),
+                                         arguments.compression_quality());
   } else {
     std::cerr << "unexpected bsdiff format." << std::endl;
     return 1;
@@ -91,20 +97,33 @@
   munmap(old_buf, oldsize);
   munmap(new_buf, newsize);
 
+  if (!ret && arguments.format() == bsdiff::BsdiffFormat::kEndsley) {
+    // Store the raw_data on disk.
+    FILE* fp = fopen(patch_filename, "wb");
+    if (!fp) {
+      perror("Opening the patch file");
+      return 1;
+    }
+    if (raw_data.size() != fwrite(raw_data.data(), 1, raw_data.size(), fp)) {
+      perror("Writing to the patch file");
+      ret = 1;
+    }
+    fclose(fp);
+  }
   return ret;
 }
 
 void PrintUsage(const std::string& proc_name) {
   std::cerr << "usage: " << proc_name
             << " [options] oldfile newfile patchfile\n";
-  std::cerr << "  --format <legacy|bsdiff40|bsdf2>  The format of the bsdiff"
-               " patch.\n"
-            << "  --minlen LEN             The minimum match length required "
-               "to consider a match in the algorithm.\n"
-            << "  --type <bz2|brotli>      The algorithm to compress the "
-               "patch, bsdf2 format only.\n"
-            << "  --quality                Quality of the patch compression,"
-               " brotli only.\n";
+  std::cerr << "  --format <legacy|bsdiff40|bsdf2|endsley>  The format of the"
+               " bsdiff patch.\n"
+            << "  --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"
+            << "  --quality                          Quality of the patch "
+               "compression, brotli only.\n";
 }
 
 }  // namespace
diff --git a/include/bsdiff/patch_writer_interface.h b/include/bsdiff/patch_writer_interface.h
index 31fc3d8..bc59b36 100644
--- a/include/bsdiff/patch_writer_interface.h
+++ b/include/bsdiff/patch_writer_interface.h
@@ -15,6 +15,7 @@
 enum class BsdiffFormat {
   kLegacy,
   kBsdf2,
+  kEndsley,
 };
 
 class PatchWriterInterface {