aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md11
-rw-r--r--element_detection_unittest.cc1
-rw-r--r--fuzzers/apply_fuzzer.cc2
-rw-r--r--fuzzers/raw_gen_fuzzer.cc2
-rw-r--r--fuzzers/ztf_gen_fuzzer.cc2
-rw-r--r--integration_test.cc10
-rw-r--r--main_utils.cc2
-rw-r--r--mapped_file.cc4
-rw-r--r--mapped_file.h4
-rw-r--r--zucchini.h33
-rw-r--r--zucchini_apply.cc6
-rw-r--r--zucchini_commands.cc66
-rw-r--r--zucchini_gen.cc39
-rw-r--r--zucchini_integration.cc154
-rw-r--r--zucchini_integration.h53
15 files changed, 229 insertions, 160 deletions
diff --git a/README.md b/README.md
index 42f3b3e..b864704 100644
--- a/README.md
+++ b/README.md
@@ -145,6 +145,17 @@ values that describe content on a higher level of abstraction, masking away
undesirable noise in raw content. Notably, the projection encodes references
based on their associated label.
+## Interfaces
+
+zucchini_lib: Core Zucchini library that operate on buffers to generate and
+apply patches.
+
+zucchini_io: Wrapper on zucchini_lib that handles file I/O, using memory-mapped
+I/O to interface with zucchini_lib.
+
+zucchini: Stand-alone executable that parses command-line arguments, and passes
+the results to zucchini_io. Also implements various helper flows.
+
## Zucchini Ensemble Patch Format
### Types
diff --git a/element_detection_unittest.cc b/element_detection_unittest.cc
index 6dbfa3f..769c839 100644
--- a/element_detection_unittest.cc
+++ b/element_detection_unittest.cc
@@ -4,6 +4,7 @@
#include "components/zucchini/element_detection.h"
+#include <map>
#include <vector>
#include "base/bind.h"
diff --git a/fuzzers/apply_fuzzer.cc b/fuzzers/apply_fuzzer.cc
index 4e9b342..baad978 100644
--- a/fuzzers/apply_fuzzer.cc
+++ b/fuzzers/apply_fuzzer.cc
@@ -54,6 +54,6 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) {
zucchini::MutableBufferView new_image(new_data.data(), new_size);
// Fuzz target.
- zucchini::Apply(old_image, *patch_reader, new_image);
+ zucchini::ApplyBuffer(old_image, *patch_reader, new_image);
// No need to check whether output exist, or if so, whether it's valid.
}
diff --git a/fuzzers/raw_gen_fuzzer.cc b/fuzzers/raw_gen_fuzzer.cc
index b27042e..de63d95 100644
--- a/fuzzers/raw_gen_fuzzer.cc
+++ b/fuzzers/raw_gen_fuzzer.cc
@@ -56,7 +56,7 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) {
zucchini::EnsemblePatchWriter patch_writer(old_image, new_image);
// Fuzz Target.
- zucchini::GenerateRaw(old_image, new_image, &patch_writer);
+ zucchini::GenerateBufferRaw(old_image, new_image, &patch_writer);
// Check that the patch size is sane. Crash the fuzzer if this isn't the case
// as it is a failure in Zucchini's patch performance that is worth
diff --git a/fuzzers/ztf_gen_fuzzer.cc b/fuzzers/ztf_gen_fuzzer.cc
index 785aed4..ee2d47c 100644
--- a/fuzzers/ztf_gen_fuzzer.cc
+++ b/fuzzers/ztf_gen_fuzzer.cc
@@ -57,7 +57,7 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) {
zucchini::EnsemblePatchWriter patch_writer(old_image, new_image);
// Fuzz Target.
- zucchini::GenerateEnsemble(old_image, new_image, &patch_writer);
+ zucchini::GenerateBuffer(old_image, new_image, &patch_writer);
// Write to buffer to avoid IO.
size_t patch_size = patch_writer.SerializedSize();
diff --git a/integration_test.cc b/integration_test.cc
index c4c7004..e7df927 100644
--- a/integration_test.cc
+++ b/integration_test.cc
@@ -49,8 +49,8 @@ void TestGenApply(const std::string& old_filename,
// Generate patch from "old" to "new".
ASSERT_EQ(status::kStatusSuccess,
- raw ? GenerateRaw(old_region, new_region, &patch_writer)
- : GenerateEnsemble(old_region, new_region, &patch_writer));
+ raw ? GenerateBufferRaw(old_region, new_region, &patch_writer)
+ : GenerateBuffer(old_region, new_region, &patch_writer));
size_t patch_size = patch_writer.SerializedSize();
EXPECT_GE(patch_size, 80U); // Minimum size is empty patch.
@@ -73,9 +73,9 @@ void TestGenApply(const std::string& old_filename,
// Apply patch to "old" to get "patched new", ensure it's identical to "new".
std::vector<uint8_t> patched_new_buffer(new_region.size());
- ASSERT_EQ(status::kStatusSuccess,
- Apply(old_region, *patch_reader,
- {patched_new_buffer.data(), patched_new_buffer.size()}));
+ ASSERT_EQ(status::kStatusSuccess, ApplyBuffer(old_region, *patch_reader,
+ {patched_new_buffer.data(),
+ patched_new_buffer.size()}));
// Note that |new_region| and |patched_new_buffer| are the same size.
EXPECT_TRUE(std::equal(new_region.begin(), new_region.end(),
diff --git a/main_utils.cc b/main_utils.cc
index b6b5642..6a09b32 100644
--- a/main_utils.cc
+++ b/main_utils.cc
@@ -70,7 +70,7 @@ constexpr Command kCommands[] = {
{"apply", "-apply <old_file> <patch_file> <new_file> [-keep]", 3,
&MainApply},
{"read", "-read <exe> [-dump]", 1, &MainRead},
- {"detect", "-detect <archive_file> [-dd=format#]", 1, &MainDetect},
+ {"detect", "-detect <archive_file>", 1, &MainDetect},
{"match", "-match <old_file> <new_file> [-impose=#+#=#+#,#+#=#+#,...]", 2,
&MainMatch},
{"crc32", "-crc32 <file>", 1, &MainCrc32},
diff --git a/mapped_file.cc b/mapped_file.cc
index 13c1afd..497b42a 100644
--- a/mapped_file.cc
+++ b/mapped_file.cc
@@ -12,7 +12,7 @@
namespace zucchini {
-MappedFileReader::MappedFileReader(base::File&& file) {
+MappedFileReader::MappedFileReader(base::File file) {
if (!file.IsValid()) {
error_ = "Invalid file.";
return; // |buffer_| will be uninitialized, and therefore invalid.
@@ -23,7 +23,7 @@ MappedFileReader::MappedFileReader(base::File&& file) {
}
MappedFileWriter::MappedFileWriter(const base::FilePath& file_path,
- base::File&& file,
+ base::File file,
size_t length)
: file_path_(file_path), delete_behavior_(kManualDeleteOnClose) {
if (!file.IsValid()) {
diff --git a/mapped_file.h b/mapped_file.h
index 540f947..e8cd590 100644
--- a/mapped_file.h
+++ b/mapped_file.h
@@ -23,7 +23,7 @@ class MappedFileReader {
public:
// Maps |file| to memory for reading. Also validates |file|. Errors are
// available via HasError() and error().
- explicit MappedFileReader(base::File&& file);
+ explicit MappedFileReader(base::File file);
const uint8_t* data() const { return buffer_.data(); }
size_t length() const { return buffer_.length(); }
@@ -47,7 +47,7 @@ class MappedFileWriter {
// UNIX systems, but can be empty if auto delete is not needed. Errors are
// available via HasError() and error().
MappedFileWriter(const base::FilePath& file_path,
- base::File&& file,
+ base::File file,
size_t length);
~MappedFileWriter();
diff --git a/zucchini.h b/zucchini.h
index e9093eb..9847440 100644
--- a/zucchini.h
+++ b/zucchini.h
@@ -11,7 +11,11 @@
#include "components/zucchini/patch_reader.h"
#include "components/zucchini/patch_writer.h"
-// Definitions, structures, and interfaces for the Zucchini library.
+// Core Zucchini library, consisting of:
+// - Global constants.
+// - Patch gen and apply functions, where "old" and "new" data are represented
+// as buffers, and patch data represented as EnsemblePatchWriter or
+// EnsemblePatchReader.
namespace zucchini {
@@ -36,9 +40,9 @@ enum Code {
// Generates ensemble patch from |old_image| to |new_image| using the default
// element detection and matching heuristics, writes the results to
// |patch_writer|, and returns a status::Code.
-status::Code GenerateEnsemble(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer);
+status::Code GenerateBuffer(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer);
// Same as GenerateEnsemble(), but if |imposed_matches| is non-empty, then
// overrides default element detection and matching heuristics with custom
@@ -46,23 +50,22 @@ status::Code GenerateEnsemble(ConstBufferView old_image,
// "#+#=#+#,#+#=#+#,..." (e.g., "1+2=3+4", "1+2=3+4,5+6=7+8"),
// where "#+#=#+#" encodes a match as 4 unsigned integers:
// [offset in "old", size in "old", offset in "new", size in "new"].
-status::Code GenerateEnsembleWithImposedMatches(
- ConstBufferView old_image,
- ConstBufferView new_image,
- std::string imposed_matches,
- EnsemblePatchWriter* patch_writer);
+status::Code GenerateBufferImposed(ConstBufferView old_image,
+ ConstBufferView new_image,
+ std::string imposed_matches,
+ EnsemblePatchWriter* patch_writer);
// Generates raw patch from |old_image| to |new_image|, and writes it to
// |patch_writer|.
-status::Code GenerateRaw(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer);
+status::Code GenerateBufferRaw(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer);
// Applies |patch_reader| to |old_image| to build |new_image|, which refers to
// preallocated memory of sufficient size.
-status::Code Apply(ConstBufferView old_image,
- const EnsemblePatchReader& patch_reader,
- MutableBufferView new_image);
+status::Code ApplyBuffer(ConstBufferView old_image,
+ const EnsemblePatchReader& patch_reader,
+ MutableBufferView new_image);
} // namespace zucchini
diff --git a/zucchini_apply.cc b/zucchini_apply.cc
index af4eab9..f6226bc 100644
--- a/zucchini_apply.cc
+++ b/zucchini_apply.cc
@@ -188,9 +188,9 @@ bool ApplyElement(ExecutableType exe_type,
/******** Exported Functions ********/
-status::Code Apply(ConstBufferView old_image,
- const EnsemblePatchReader& patch_reader,
- MutableBufferView new_image) {
+status::Code ApplyBuffer(ConstBufferView old_image,
+ const EnsemblePatchReader& patch_reader,
+ MutableBufferView new_image) {
if (!patch_reader.CheckOldFile(old_image)) {
LOG(ERROR) << "Invalid old_image.";
return status::kStatusInvalidOldImage;
diff --git a/zucchini_commands.cc b/zucchini_commands.cc
index 62dd20d..1abe08f 100644
--- a/zucchini_commands.cc
+++ b/zucchini_commands.cc
@@ -38,67 +38,11 @@ constexpr char kSwitchRaw[] = "raw";
zucchini::status::Code MainGen(MainParams params) {
CHECK_EQ(3U, params.file_paths.size());
-
- // TODO(huangs): Move implementation to zucchini_integration.cc.
- using base::File;
- File old_file(params.file_paths[0], File::FLAG_OPEN | File::FLAG_READ);
- zucchini::MappedFileReader old_image(std::move(old_file));
- if (old_image.HasError()) {
- LOG(ERROR) << "Error with file " << params.file_paths[0].value() << ": "
- << old_image.error();
- return zucchini::status::kStatusFileReadError;
- }
- File new_file(params.file_paths[1], File::FLAG_OPEN | File::FLAG_READ);
- zucchini::MappedFileReader new_image(std::move(new_file));
- if (new_image.HasError()) {
- LOG(ERROR) << "Error with file " << params.file_paths[1].value() << ": "
- << new_image.error();
- return zucchini::status::kStatusFileReadError;
- }
- zucchini::EnsemblePatchWriter patch_writer(old_image.region(),
- new_image.region());
-
- zucchini::status::Code result = zucchini::status::kStatusSuccess;
- if (params.command_line.HasSwitch(kSwitchRaw)) {
- result = GenerateRaw(old_image.region(), new_image.region(), &patch_writer);
- } else {
- // May be empty.
- std::string imposed_matches =
- params.command_line.GetSwitchValueASCII(kSwitchImpose);
- result = GenerateEnsembleWithImposedMatches(
- old_image.region(), new_image.region(), std::move(imposed_matches),
- &patch_writer);
- }
-
- if (result != zucchini::status::kStatusSuccess) {
- params.out << "Fatal error encountered when generating patch." << std::endl;
- return result;
- }
-
- // By default, delete patch on destruction, to avoid having lingering files in
- // case of a failure. On Windows deletion can be done by the OS.
- File patch_file(params.file_paths[2], File::FLAG_CREATE_ALWAYS |
- File::FLAG_READ | File::FLAG_WRITE |
- File::FLAG_SHARE_DELETE |
- File::FLAG_CAN_DELETE_ON_CLOSE);
- zucchini::MappedFileWriter patch(params.file_paths[2], std::move(patch_file),
- patch_writer.SerializedSize());
- if (patch.HasError()) {
- LOG(ERROR) << "Error with file " << params.file_paths[2].value() << ": "
- << patch.error();
- return zucchini::status::kStatusFileWriteError;
- }
-
- if (params.command_line.HasSwitch(kSwitchKeep))
- patch.Keep();
-
- if (!patch_writer.SerializeInto(patch.region()))
- return zucchini::status::kStatusPatchWriteError;
-
- // Successfully created patch. Explicitly request file to be kept.
- if (!patch.Keep())
- return zucchini::status::kStatusFileWriteError;
- return zucchini::status::kStatusSuccess;
+ return zucchini::Generate(
+ params.file_paths[0], params.file_paths[1], params.file_paths[2],
+ params.command_line.HasSwitch(kSwitchKeep),
+ params.command_line.HasSwitch(kSwitchRaw),
+ params.command_line.GetSwitchValueASCII(kSwitchImpose));
}
zucchini::status::Code MainApply(MainParams params) {
diff --git a/zucchini_gen.cc b/zucchini_gen.cc
index 29be814..19da0af 100644
--- a/zucchini_gen.cc
+++ b/zucchini_gen.cc
@@ -319,13 +319,13 @@ bool GenerateExecutableElement(ExecutableType exe_type,
reference_bytes_mixer.get(), patch_writer);
}
-status::Code GenerateEnsembleCommon(ConstBufferView old_image,
- ConstBufferView new_image,
- std::unique_ptr<EnsembleMatcher> matcher,
- EnsemblePatchWriter* patch_writer) {
+status::Code GenerateBufferCommon(ConstBufferView old_image,
+ ConstBufferView new_image,
+ std::unique_ptr<EnsembleMatcher> matcher,
+ EnsemblePatchWriter* patch_writer) {
if (!matcher->RunMatch(old_image, new_image)) {
LOG(INFO) << "RunMatch() failed, generating raw patch.";
- return GenerateRaw(old_image, new_image, patch_writer);
+ return GenerateBufferRaw(old_image, new_image, patch_writer);
}
const std::vector<ElementMatch>& matches = matcher->matches();
@@ -335,7 +335,7 @@ status::Code GenerateEnsembleCommon(ConstBufferView old_image,
size_t num_elements = matches.size();
if (num_elements == 0) {
LOG(INFO) << "No nontrival matches, generating raw patch.";
- return GenerateRaw(old_image, new_image, patch_writer);
+ return GenerateBufferRaw(old_image, new_image, patch_writer);
}
// "Gaps" are |new_image| bytes not covered by new_elements in |matches|.
@@ -421,30 +421,29 @@ status::Code GenerateEnsembleCommon(ConstBufferView old_image,
/******** Exported Functions ********/
-status::Code GenerateEnsemble(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer) {
- return GenerateEnsembleCommon(
+status::Code GenerateBuffer(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer) {
+ return GenerateBufferCommon(
old_image, new_image, std::make_unique<HeuristicEnsembleMatcher>(nullptr),
patch_writer);
}
-status::Code GenerateEnsembleWithImposedMatches(
- ConstBufferView old_image,
- ConstBufferView new_image,
- std::string imposed_matches,
- EnsemblePatchWriter* patch_writer) {
+status::Code GenerateBufferImposed(ConstBufferView old_image,
+ ConstBufferView new_image,
+ std::string imposed_matches,
+ EnsemblePatchWriter* patch_writer) {
if (imposed_matches.empty())
- return GenerateEnsemble(old_image, new_image, patch_writer);
+ return GenerateBuffer(old_image, new_image, patch_writer);
- return GenerateEnsembleCommon(
+ return GenerateBufferCommon(
old_image, new_image,
std::make_unique<ImposedEnsembleMatcher>(imposed_matches), patch_writer);
}
-status::Code GenerateRaw(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer) {
+status::Code GenerateBufferRaw(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer) {
ImageIndex old_image_index(old_image);
EncodedView old_view(old_image_index);
std::vector<offset_t> old_sa =
diff --git a/zucchini_integration.cc b/zucchini_integration.cc
index 1149658..97790a3 100644
--- a/zucchini_integration.cc
+++ b/zucchini_integration.cc
@@ -19,92 +19,172 @@ struct FileNames {
FileNames() : is_dummy(true) {
// Use fake names.
old_name = old_name.AppendASCII("old_name");
- patch_name = patch_name.AppendASCII("patch_name");
new_name = new_name.AppendASCII("new_name");
+ patch_name = patch_name.AppendASCII("patch_name");
}
FileNames(const base::FilePath& old_name,
- const base::FilePath& patch_name,
- const base::FilePath& new_name)
+ const base::FilePath& new_name,
+ const base::FilePath& patch_name)
: old_name(old_name),
- patch_name(patch_name),
new_name(new_name),
+ patch_name(patch_name),
is_dummy(false) {}
base::FilePath old_name;
- base::FilePath patch_name;
base::FilePath new_name;
+ base::FilePath patch_name;
// A flag to decide whether the filenames are only for error output.
const bool is_dummy;
};
-status::Code ApplyCommon(base::File&& old_file_handle,
- base::File&& patch_file_handle,
- base::File&& new_file_handle,
+status::Code GenerateCommon(base::File old_file,
+ base::File new_file,
+ base::File patch_file,
+ const FileNames& names,
+ bool force_keep,
+ bool is_raw,
+ std::string imposed_matches) {
+ MappedFileReader mapped_old(std::move(old_file));
+ if (mapped_old.HasError()) {
+ LOG(ERROR) << "Error with file " << names.old_name.value() << ": "
+ << mapped_old.error();
+ return status::kStatusFileReadError;
+ }
+
+ MappedFileReader mapped_new(std::move(new_file));
+ if (mapped_new.HasError()) {
+ LOG(ERROR) << "Error with file " << names.new_name.value() << ": "
+ << mapped_new.error();
+ return status::kStatusFileReadError;
+ }
+
+ status::Code result = status::kStatusSuccess;
+ EnsemblePatchWriter patch_writer(mapped_old.region(), mapped_new.region());
+ if (is_raw) {
+ result = GenerateBufferRaw(mapped_old.region(), mapped_new.region(),
+ &patch_writer);
+ } else {
+ result = GenerateBufferImposed(mapped_old.region(), mapped_new.region(),
+ std::move(imposed_matches), &patch_writer);
+ }
+ if (result != status::kStatusSuccess) {
+ LOG(ERROR) << "Fatal error encountered when generating patch.";
+ return result;
+ }
+
+ // By default, delete patch on destruction, to avoid having lingering files in
+ // case of a failure. On Windows deletion can be done by the OS.
+ MappedFileWriter mapped_patch(names.patch_name, std::move(patch_file),
+ patch_writer.SerializedSize());
+ if (mapped_patch.HasError()) {
+ LOG(ERROR) << "Error with file " << names.patch_name.value() << ": "
+ << mapped_patch.error();
+ return status::kStatusFileWriteError;
+ }
+ if (force_keep)
+ mapped_patch.Keep();
+
+ if (!patch_writer.SerializeInto(mapped_patch.region()))
+ return status::kStatusPatchWriteError;
+
+ // Successfully created patch. Explicitly request file to be kept.
+ if (!mapped_patch.Keep())
+ return status::kStatusFileWriteError;
+ return status::kStatusSuccess;
+}
+
+status::Code ApplyCommon(base::File old_file,
+ base::File patch_file,
+ base::File new_file,
const FileNames& names,
bool force_keep) {
- MappedFileReader patch_file(std::move(patch_file_handle));
- if (patch_file.HasError()) {
+ MappedFileReader mapped_patch(std::move(patch_file));
+ if (mapped_patch.HasError()) {
LOG(ERROR) << "Error with file " << names.patch_name.value() << ": "
- << patch_file.error();
+ << mapped_patch.error();
return status::kStatusFileReadError;
}
- auto patch_reader =
- zucchini::EnsemblePatchReader::Create(patch_file.region());
+ auto patch_reader = EnsemblePatchReader::Create(mapped_patch.region());
if (!patch_reader.has_value()) {
LOG(ERROR) << "Error reading patch header.";
return status::kStatusPatchReadError;
}
- MappedFileReader old_file(std::move(old_file_handle));
- if (old_file.HasError()) {
+ MappedFileReader mapped_old(std::move(old_file));
+ if (mapped_old.HasError()) {
LOG(ERROR) << "Error with file " << names.old_name.value() << ": "
- << old_file.error();
+ << mapped_old.error();
return status::kStatusFileReadError;
}
- zucchini::PatchHeader header = patch_reader->header();
+ PatchHeader header = patch_reader->header();
// By default, delete output on destruction, to avoid having lingering files
// in case of a failure. On Windows deletion can be done by the OS.
- base::FilePath file_path;
- if (!names.is_dummy)
- file_path = base::FilePath(names.new_name);
-
- MappedFileWriter new_file(file_path, std::move(new_file_handle),
- header.new_size);
- if (new_file.HasError()) {
+ MappedFileWriter mapped_new(names.new_name, std::move(new_file),
+ header.new_size);
+ if (mapped_new.HasError()) {
LOG(ERROR) << "Error with file " << names.new_name.value() << ": "
- << new_file.error();
+ << mapped_new.error();
return status::kStatusFileWriteError;
}
-
if (force_keep)
- new_file.Keep();
+ mapped_new.Keep();
- zucchini::status::Code result =
- zucchini::Apply(old_file.region(), *patch_reader, new_file.region());
+ status::Code result =
+ ApplyBuffer(mapped_old.region(), *patch_reader, mapped_new.region());
if (result != status::kStatusSuccess) {
LOG(ERROR) << "Fatal error encountered while applying patch.";
return result;
}
- // Successfully patch |new_file|. Explicitly request file to be kept.
- if (!new_file.Keep())
+ // Successfully patch |mapped_new|. Explicitly request file to be kept.
+ if (!mapped_new.Keep())
return status::kStatusFileWriteError;
return status::kStatusSuccess;
}
} // namespace
-status::Code Apply(base::File&& old_file_handle,
- base::File&& patch_file_handle,
- base::File&& new_file_handle,
+status::Code Generate(base::File old_file,
+ base::File new_file,
+ base::File patch_file,
+ bool force_keep,
+ bool is_raw,
+ std::string imposed_matches) {
+ const FileNames file_names;
+ return GenerateCommon(std::move(old_file), std::move(new_file),
+ std::move(patch_file), file_names, force_keep, is_raw,
+ std::move(imposed_matches));
+}
+
+status::Code Generate(const base::FilePath& old_path,
+ const base::FilePath& new_path,
+ const base::FilePath& patch_path,
+ bool force_keep,
+ bool is_raw,
+ std::string imposed_matches) {
+ using base::File;
+ File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ);
+ File new_file(new_path, File::FLAG_OPEN | File::FLAG_READ);
+ File patch_file(patch_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
+ File::FLAG_WRITE | File::FLAG_SHARE_DELETE |
+ File::FLAG_CAN_DELETE_ON_CLOSE);
+ const FileNames file_names(old_path, new_path, patch_path);
+ return GenerateCommon(std::move(old_file), std::move(new_file),
+ std::move(patch_file), file_names, force_keep, is_raw,
+ std::move(imposed_matches));
+}
+
+status::Code Apply(base::File old_file,
+ base::File patch_file,
+ base::File new_file,
bool force_keep) {
const FileNames file_names;
- return ApplyCommon(std::move(old_file_handle), std::move(patch_file_handle),
- std::move(new_file_handle), file_names, force_keep);
+ return ApplyCommon(std::move(old_file), std::move(patch_file),
+ std::move(new_file), file_names, force_keep);
}
status::Code Apply(const base::FilePath& old_path,
@@ -117,7 +197,7 @@ status::Code Apply(const base::FilePath& old_path,
File new_file(new_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
File::FLAG_WRITE | File::FLAG_SHARE_DELETE |
File::FLAG_CAN_DELETE_ON_CLOSE);
- const FileNames file_names(old_path, patch_path, new_path);
+ const FileNames file_names(old_path, new_path, patch_path);
return ApplyCommon(std::move(old_file), std::move(patch_file),
std::move(new_file), file_names, force_keep);
}
diff --git a/zucchini_integration.h b/zucchini_integration.h
index ce98b28..2ae6091 100644
--- a/zucchini_integration.h
+++ b/zucchini_integration.h
@@ -5,28 +5,59 @@
#ifndef COMPONENTS_ZUCCHINI_ZUCCHINI_INTEGRATION_H_
#define COMPONENTS_ZUCCHINI_ZUCCHINI_INTEGRATION_H_
+#include <string>
+
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "components/zucchini/zucchini.h"
+// Zucchini integration interface to wrap core Zucchini library with file I/O.
+
namespace zucchini {
-// Applies the patch in |patch_file| to the bytes in |old_file| and writes the
-// result to |new_file|. Since this uses memory mapped files, crashes are
-// expected in case of I/O errors. On Windows, |new_file| is kept iff returned
+// Generates a patch to transform |old_file| to |new_file|, and writes the
+// result to |patch_file|. Since this uses memory mapped files, crashes are
+// expected in case of I/O errors. On Windows, |patch_file| is kept iff returned
// code is kStatusSuccess or if |force_keep == true|, and is deleted otherwise.
// For UNIX systems the caller needs to do cleanup since it has ownership of the
-// base::File params and Zucchini has no knowledge of which base::FilePath to
+// base::File params, and Zucchini has no knowledge of which base::FilePath to
+// delete. If |is_raw == true| then uses Raw Zucchini. If |imposed_matches| is
+// non-empty, then overrides default element detection and matching heuristics
+// with custom element matching encoded in |imposed_matches|, which should be
+// formatted as:
+// "#+#=#+#,#+#=#+#,..." (e.g., "1+2=3+4", "1+2=3+4,5+6=7+8"),
+// where "#+#=#+#" encodes a match as 4 unsigned integers:
+// [offset in "old", size in "old", offset in "new", size in "new"].
+status::Code Generate(base::File old_file,
+ base::File new_file,
+ base::File patch_file,
+ bool force_keep = false,
+ bool is_raw = false,
+ std::string imposed_matches = "");
+
+// Alternative Generate() interface that takes base::FilePath as arguments.
+// Performs proper cleanup in Windows and UNIX if failure occurs.
+status::Code Generate(const base::FilePath& old_path,
+ const base::FilePath& new_path,
+ const base::FilePath& patch_path,
+ bool force_keep = false,
+ bool is_raw = false,
+ std::string imposed_matches = "");
+
+// Applies the patch in |patch_file| to |old_file|, and writes the result to
+// |new_file|. Since this uses memory mapped files, crashes are expected in case
+// of I/O errors. On Windows, |new_file| is kept iff returned code is
+// kStatusSuccess or if |force_keep == true|, and is deleted otherwise. For UNIX
+// systems the caller needs to do cleanup since it has ownership of the
+// base::File params, and Zucchini has no knowledge of which base::FilePath to
// delete.
-status::Code Apply(base::File&& old_file,
- base::File&& patch_file,
- base::File&& new_file,
+status::Code Apply(base::File old_file,
+ base::File patch_file,
+ base::File new_file,
bool force_keep = false);
-// Applies the patch in |patch_path| to the bytes in |old_path| and writes the
-// result to |new_path|. Since this uses memory mapped files, crashes are
-// expected in case of I/O errors. |new_path| is kept iff returned code is
-// kStatusSuccess or if |force_keep == true|, and is deleted otherwise.
+// Alternative Apply() interface that takes base::FilePath as arguments.
+// Performs proper cleanup in Windows and UNIX if failure occurs.
status::Code Apply(const base::FilePath& old_path,
const base::FilePath& patch_path,
const base::FilePath& new_path,