xref: /aosp_15_r20/external/puffin/src/main.cc (revision 07fb1d065b7cfb4729786fadd42a612532d2f466)
1*07fb1d06SElliott Hughes // Copyright 2017 The ChromiumOS Authors
2*07fb1d06SElliott Hughes // Use of this source code is governed by a BSD-style license that can be
3*07fb1d06SElliott Hughes // found in the LICENSE file.
4*07fb1d06SElliott Hughes 
5*07fb1d06SElliott Hughes #include <algorithm>
6*07fb1d06SElliott Hughes #include <fstream>
7*07fb1d06SElliott Hughes #include <iostream>
8*07fb1d06SElliott Hughes #include <sstream>
9*07fb1d06SElliott Hughes 
10*07fb1d06SElliott Hughes #ifdef USE_BRILLO
11*07fb1d06SElliott Hughes #include "brillo/flag_helper.h"
12*07fb1d06SElliott Hughes #else
13*07fb1d06SElliott Hughes #include "gflags/gflags.h"
14*07fb1d06SElliott Hughes #endif
15*07fb1d06SElliott Hughes 
16*07fb1d06SElliott Hughes #include "puffin/file_stream.h"
17*07fb1d06SElliott Hughes #include "puffin/memory_stream.h"
18*07fb1d06SElliott Hughes #include "puffin/src/extent_stream.h"
19*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/common.h"
20*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/huffer.h"
21*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/puffdiff.h"
22*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/puffer.h"
23*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/puffpatch.h"
24*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/utils.h"
25*07fb1d06SElliott Hughes #include "puffin/src/logging.h"
26*07fb1d06SElliott Hughes #include "puffin/src/puffin_stream.h"
27*07fb1d06SElliott Hughes 
28*07fb1d06SElliott Hughes using puffin::BitExtent;
29*07fb1d06SElliott Hughes using puffin::Buffer;
30*07fb1d06SElliott Hughes using puffin::ByteExtent;
31*07fb1d06SElliott Hughes using puffin::ExtentStream;
32*07fb1d06SElliott Hughes using puffin::FileStream;
33*07fb1d06SElliott Hughes using puffin::Huffer;
34*07fb1d06SElliott Hughes using puffin::MemoryStream;
35*07fb1d06SElliott Hughes using puffin::Puffer;
36*07fb1d06SElliott Hughes using puffin::PuffinStream;
37*07fb1d06SElliott Hughes using puffin::UniqueStreamPtr;
38*07fb1d06SElliott Hughes using std::string;
39*07fb1d06SElliott Hughes using std::stringstream;
40*07fb1d06SElliott Hughes using std::vector;
41*07fb1d06SElliott Hughes 
42*07fb1d06SElliott Hughes namespace {
43*07fb1d06SElliott Hughes 
44*07fb1d06SElliott Hughes constexpr char kExtentDelimeter = ',';
45*07fb1d06SElliott Hughes constexpr char kOffsetLengthDelimeter = ':';
46*07fb1d06SElliott Hughes 
47*07fb1d06SElliott Hughes template <typename T>
StringToExtents(const string & str)48*07fb1d06SElliott Hughes vector<T> StringToExtents(const string& str) {
49*07fb1d06SElliott Hughes   vector<T> extents;
50*07fb1d06SElliott Hughes   if (!str.empty()) {
51*07fb1d06SElliott Hughes     stringstream ss(str);
52*07fb1d06SElliott Hughes     string extent_str;
53*07fb1d06SElliott Hughes     while (getline(ss, extent_str, kExtentDelimeter)) {
54*07fb1d06SElliott Hughes       stringstream extent_ss(extent_str);
55*07fb1d06SElliott Hughes       string offset_str, length_str;
56*07fb1d06SElliott Hughes       getline(extent_ss, offset_str, kOffsetLengthDelimeter);
57*07fb1d06SElliott Hughes       getline(extent_ss, length_str, kOffsetLengthDelimeter);
58*07fb1d06SElliott Hughes       extents.emplace_back(stoull(offset_str), stoull(length_str));
59*07fb1d06SElliott Hughes     }
60*07fb1d06SElliott Hughes   }
61*07fb1d06SElliott Hughes   return extents;
62*07fb1d06SElliott Hughes }
63*07fb1d06SElliott Hughes 
64*07fb1d06SElliott Hughes const uint64_t kDefaultPuffCacheSize = 50 * 1024 * 1024;  // 50 MB
65*07fb1d06SElliott Hughes 
66*07fb1d06SElliott Hughes // An enum representing the type of compressed files.
67*07fb1d06SElliott Hughes enum class FileType { kDeflate, kZlib, kGzip, kZip, kRaw, kUnknown };
68*07fb1d06SElliott Hughes 
69*07fb1d06SElliott Hughes // Returns a file type based on the input string |file_type| (normally the final
70*07fb1d06SElliott Hughes // extension of the file).
StringToFileType(const string & file_type)71*07fb1d06SElliott Hughes FileType StringToFileType(const string& file_type) {
72*07fb1d06SElliott Hughes   if (file_type == "raw") {
73*07fb1d06SElliott Hughes     return FileType::kRaw;
74*07fb1d06SElliott Hughes   }
75*07fb1d06SElliott Hughes   if (file_type == "deflate") {
76*07fb1d06SElliott Hughes     return FileType::kDeflate;
77*07fb1d06SElliott Hughes   } else if (file_type == "zlib") {
78*07fb1d06SElliott Hughes     return FileType::kZlib;
79*07fb1d06SElliott Hughes   } else if (file_type == "gzip" || file_type == "gz" || file_type == "tgz") {
80*07fb1d06SElliott Hughes     return FileType::kGzip;
81*07fb1d06SElliott Hughes   } else if (file_type == "zip" || file_type == "apk" || file_type == "jar") {
82*07fb1d06SElliott Hughes     return FileType::kZip;
83*07fb1d06SElliott Hughes   }
84*07fb1d06SElliott Hughes   return FileType::kUnknown;
85*07fb1d06SElliott Hughes }
86*07fb1d06SElliott Hughes 
87*07fb1d06SElliott Hughes // Finds the location of deflates in |stream|. If |file_type_to_override| is
88*07fb1d06SElliott Hughes // non-empty, it infers the file type based on that, otherwise, it infers the
89*07fb1d06SElliott Hughes // file type based on the final extension of |file_name|. It returns false if
90*07fb1d06SElliott Hughes // file type cannot be inferred from any of the input arguments. |deflates|
91*07fb1d06SElliott Hughes // is filled with byte-aligned location of deflates.
LocateDeflatesBasedOnFileType(const UniqueStreamPtr & stream,const string & file_name,const string & file_type_to_override,vector<BitExtent> * deflates)92*07fb1d06SElliott Hughes bool LocateDeflatesBasedOnFileType(const UniqueStreamPtr& stream,
93*07fb1d06SElliott Hughes                                    const string& file_name,
94*07fb1d06SElliott Hughes                                    const string& file_type_to_override,
95*07fb1d06SElliott Hughes                                    vector<BitExtent>* deflates) {
96*07fb1d06SElliott Hughes   auto file_type = FileType::kUnknown;
97*07fb1d06SElliott Hughes 
98*07fb1d06SElliott Hughes   auto last_dot = file_name.find_last_of(".");
99*07fb1d06SElliott Hughes   if (last_dot == string::npos) {
100*07fb1d06SElliott Hughes     // Could not find a dot so we assume there is no extension.
101*07fb1d06SElliott Hughes     return false;
102*07fb1d06SElliott Hughes   }
103*07fb1d06SElliott Hughes   auto extension = file_name.substr(last_dot + 1);
104*07fb1d06SElliott Hughes   file_type = StringToFileType(extension);
105*07fb1d06SElliott Hughes 
106*07fb1d06SElliott Hughes   if (!file_type_to_override.empty()) {
107*07fb1d06SElliott Hughes     auto override_file_type = StringToFileType(file_type_to_override);
108*07fb1d06SElliott Hughes     if (override_file_type == FileType::kUnknown) {
109*07fb1d06SElliott Hughes       LOG(ERROR) << "Overriden file type " << file_type_to_override
110*07fb1d06SElliott Hughes                  << " does not exist.";
111*07fb1d06SElliott Hughes       return false;
112*07fb1d06SElliott Hughes     }
113*07fb1d06SElliott Hughes     if (file_type != FileType::kUnknown && file_type != override_file_type) {
114*07fb1d06SElliott Hughes       LOG(WARNING) << "Based on the file name, the file type is " << extension
115*07fb1d06SElliott Hughes                    << ", But the overriden file type is "
116*07fb1d06SElliott Hughes                    << file_type_to_override << ". Is this intentional?";
117*07fb1d06SElliott Hughes     }
118*07fb1d06SElliott Hughes     file_type = override_file_type;
119*07fb1d06SElliott Hughes   }
120*07fb1d06SElliott Hughes 
121*07fb1d06SElliott Hughes   if (file_type == FileType::kRaw) {
122*07fb1d06SElliott Hughes     // Do not need to populate |deflates|.
123*07fb1d06SElliott Hughes     return true;
124*07fb1d06SElliott Hughes   }
125*07fb1d06SElliott Hughes 
126*07fb1d06SElliott Hughes   uint64_t stream_size;
127*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(stream->GetSize(&stream_size));
128*07fb1d06SElliott Hughes   Buffer data(stream_size);
129*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(stream->Read(data.data(), data.size()));
130*07fb1d06SElliott Hughes   switch (file_type) {
131*07fb1d06SElliott Hughes     case FileType::kDeflate:
132*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(puffin::LocateDeflatesInDeflateStream(
133*07fb1d06SElliott Hughes           data.data(), data.size(), 0, deflates, nullptr));
134*07fb1d06SElliott Hughes       break;
135*07fb1d06SElliott Hughes     case FileType::kZlib:
136*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(puffin::LocateDeflatesInZlib(data, deflates));
137*07fb1d06SElliott Hughes       break;
138*07fb1d06SElliott Hughes     case FileType::kGzip:
139*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(puffin::LocateDeflatesInGzip(data, deflates));
140*07fb1d06SElliott Hughes       break;
141*07fb1d06SElliott Hughes     case FileType::kZip:
142*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(puffin::LocateDeflatesInZipArchive(data, deflates));
143*07fb1d06SElliott Hughes       break;
144*07fb1d06SElliott Hughes     default:
145*07fb1d06SElliott Hughes       LOG(ERROR) << "Unknown file type: (" << file_type_to_override << ") nor ("
146*07fb1d06SElliott Hughes                  << extension << ").";
147*07fb1d06SElliott Hughes       return false;
148*07fb1d06SElliott Hughes   }
149*07fb1d06SElliott Hughes   // Return the stream to its zero offset in case we used it.
150*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(stream->Seek(0));
151*07fb1d06SElliott Hughes 
152*07fb1d06SElliott Hughes   return true;
153*07fb1d06SElliott Hughes }
154*07fb1d06SElliott Hughes 
155*07fb1d06SElliott Hughes }  // namespace
156*07fb1d06SElliott Hughes 
157*07fb1d06SElliott Hughes #define SETUP_FLAGS                                                          \
158*07fb1d06SElliott Hughes   DEFINE_string(src_file, "", "Source file");                                \
159*07fb1d06SElliott Hughes   DEFINE_string(dst_file, "", "Target file");                                \
160*07fb1d06SElliott Hughes   DEFINE_string(patch_file, "", "patch file");                               \
161*07fb1d06SElliott Hughes   DEFINE_string(                                                             \
162*07fb1d06SElliott Hughes       src_deflates_byte, "",                                                 \
163*07fb1d06SElliott Hughes       "Source deflate byte locations in the format offset:length,...");      \
164*07fb1d06SElliott Hughes   DEFINE_string(                                                             \
165*07fb1d06SElliott Hughes       dst_deflates_byte, "",                                                 \
166*07fb1d06SElliott Hughes       "Target deflate byte locations in the format offset:length,...");      \
167*07fb1d06SElliott Hughes   DEFINE_string(                                                             \
168*07fb1d06SElliott Hughes       src_deflates_bit, "",                                                  \
169*07fb1d06SElliott Hughes       "Source deflate bit locations in the format offset:length,...");       \
170*07fb1d06SElliott Hughes   DEFINE_string(                                                             \
171*07fb1d06SElliott Hughes       dst_deflates_bit, "",                                                  \
172*07fb1d06SElliott Hughes       "Target deflatebit locations in the format offset:length,...");        \
173*07fb1d06SElliott Hughes   DEFINE_string(src_puffs, "",                                               \
174*07fb1d06SElliott Hughes                 "Source puff locations in the format offset:length,...");    \
175*07fb1d06SElliott Hughes   DEFINE_string(dst_puffs, "",                                               \
176*07fb1d06SElliott Hughes                 "Target puff locations in the format offset:length,...");    \
177*07fb1d06SElliott Hughes   DEFINE_string(src_extents, "",                                             \
178*07fb1d06SElliott Hughes                 "Source extents in the format of offset:length,...");        \
179*07fb1d06SElliott Hughes   DEFINE_string(dst_extents, "",                                             \
180*07fb1d06SElliott Hughes                 "Target extents in the format of offset:length,...");        \
181*07fb1d06SElliott Hughes   DEFINE_string(operation, "",                                               \
182*07fb1d06SElliott Hughes                 "Type of the operation: puff, huff, puffdiff, puffpatch, "   \
183*07fb1d06SElliott Hughes                 "puffhuff");                                                 \
184*07fb1d06SElliott Hughes   DEFINE_string(src_file_type, "",                                           \
185*07fb1d06SElliott Hughes                 "Type of the input source file: deflate, gzip, "             \
186*07fb1d06SElliott Hughes                 "zlib or zip");                                              \
187*07fb1d06SElliott Hughes   DEFINE_string(dst_file_type, "",                                           \
188*07fb1d06SElliott Hughes                 "Same as src_file_type but for the target file");            \
189*07fb1d06SElliott Hughes   DEFINE_bool(verbose, false,                                                \
190*07fb1d06SElliott Hughes               "Logs all the given parameters including internally "          \
191*07fb1d06SElliott Hughes               "generated ones");                                             \
192*07fb1d06SElliott Hughes   DEFINE_uint64(cache_size, kDefaultPuffCacheSize,                           \
193*07fb1d06SElliott Hughes                 "Maximum size to cache the puff stream. Used in puffpatch"); \
194*07fb1d06SElliott Hughes   DEFINE_int32(patch_algorithm, 0,                                           \
195*07fb1d06SElliott Hughes                "Type of raw diff algorithm to use. The current supported "   \
196*07fb1d06SElliott Hughes                "ones are 0: bsdiff, 1: zucchini.");
197*07fb1d06SElliott Hughes #ifndef USE_BRILLO
198*07fb1d06SElliott Hughes SETUP_FLAGS;
199*07fb1d06SElliott Hughes #endif
200*07fb1d06SElliott Hughes 
201*07fb1d06SElliott Hughes // Main entry point to the application.
Main(int argc,char ** argv)202*07fb1d06SElliott Hughes bool Main(int argc, char** argv) {
203*07fb1d06SElliott Hughes #ifdef USE_BRILLO
204*07fb1d06SElliott Hughes   SETUP_FLAGS;
205*07fb1d06SElliott Hughes   brillo::FlagHelper::Init(argc, argv, "Puffin tool");
206*07fb1d06SElliott Hughes #else
207*07fb1d06SElliott Hughes   // google::InitGoogleLogging(argv[0]);
208*07fb1d06SElliott Hughes   google::ParseCommandLineFlags(&argc, &argv, true);
209*07fb1d06SElliott Hughes #endif
210*07fb1d06SElliott Hughes 
211*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(!FLAGS_operation.empty());
212*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(!FLAGS_src_file.empty());
213*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(!FLAGS_dst_file.empty());
214*07fb1d06SElliott Hughes 
215*07fb1d06SElliott Hughes   auto src_deflates_byte = StringToExtents<ByteExtent>(FLAGS_src_deflates_byte);
216*07fb1d06SElliott Hughes   auto dst_deflates_byte = StringToExtents<ByteExtent>(FLAGS_dst_deflates_byte);
217*07fb1d06SElliott Hughes   auto src_deflates_bit = StringToExtents<BitExtent>(FLAGS_src_deflates_bit);
218*07fb1d06SElliott Hughes   auto dst_deflates_bit = StringToExtents<BitExtent>(FLAGS_dst_deflates_bit);
219*07fb1d06SElliott Hughes   auto src_puffs = StringToExtents<ByteExtent>(FLAGS_src_puffs);
220*07fb1d06SElliott Hughes   auto dst_puffs = StringToExtents<ByteExtent>(FLAGS_dst_puffs);
221*07fb1d06SElliott Hughes   auto src_extents = StringToExtents<ByteExtent>(FLAGS_src_extents);
222*07fb1d06SElliott Hughes   auto dst_extents = StringToExtents<ByteExtent>(FLAGS_dst_extents);
223*07fb1d06SElliott Hughes 
224*07fb1d06SElliott Hughes   auto src_stream = FileStream::Open(FLAGS_src_file, true, false);
225*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(src_stream);
226*07fb1d06SElliott Hughes   if (!src_extents.empty()) {
227*07fb1d06SElliott Hughes     src_stream =
228*07fb1d06SElliott Hughes         ExtentStream::CreateForRead(std::move(src_stream), src_extents);
229*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(src_stream);
230*07fb1d06SElliott Hughes   }
231*07fb1d06SElliott Hughes 
232*07fb1d06SElliott Hughes   if (FLAGS_operation == "puff" || FLAGS_operation == "puffhuff") {
233*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(LocateDeflatesBasedOnFileType(
234*07fb1d06SElliott Hughes         src_stream, FLAGS_src_file, FLAGS_src_file_type, &src_deflates_bit));
235*07fb1d06SElliott Hughes 
236*07fb1d06SElliott Hughes     if (src_deflates_bit.empty() && src_deflates_byte.empty()) {
237*07fb1d06SElliott Hughes       LOG(WARNING) << "You should pass source deflates, is this intentional?";
238*07fb1d06SElliott Hughes     }
239*07fb1d06SElliott Hughes     if (src_deflates_bit.empty()) {
240*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(FindDeflateSubBlocks(src_stream, src_deflates_byte,
241*07fb1d06SElliott Hughes                                                  &src_deflates_bit));
242*07fb1d06SElliott Hughes     }
243*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(dst_puffs.empty());
244*07fb1d06SElliott Hughes     uint64_t dst_puff_size;
245*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(FindPuffLocations(src_stream, src_deflates_bit,
246*07fb1d06SElliott Hughes                                             &dst_puffs, &dst_puff_size));
247*07fb1d06SElliott Hughes 
248*07fb1d06SElliott Hughes     auto dst_stream = FileStream::Open(FLAGS_dst_file, false, true);
249*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(dst_stream);
250*07fb1d06SElliott Hughes     auto puffer = std::make_shared<Puffer>();
251*07fb1d06SElliott Hughes     auto reader =
252*07fb1d06SElliott Hughes         PuffinStream::CreateForPuff(std::move(src_stream), puffer,
253*07fb1d06SElliott Hughes                                     dst_puff_size, src_deflates_bit, dst_puffs);
254*07fb1d06SElliott Hughes 
255*07fb1d06SElliott Hughes     Buffer puff_buffer;
256*07fb1d06SElliott Hughes     auto writer = FLAGS_operation == "puffhuff"
257*07fb1d06SElliott Hughes                       ? MemoryStream::CreateForWrite(&puff_buffer)
258*07fb1d06SElliott Hughes                       : std::move(dst_stream);
259*07fb1d06SElliott Hughes 
260*07fb1d06SElliott Hughes     Buffer buffer(1024 * 1024);
261*07fb1d06SElliott Hughes     uint64_t bytes_wrote = 0;
262*07fb1d06SElliott Hughes     while (bytes_wrote < dst_puff_size) {
263*07fb1d06SElliott Hughes       auto write_size = std::min(static_cast<uint64_t>(buffer.size()),
264*07fb1d06SElliott Hughes                                  dst_puff_size - bytes_wrote);
265*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(reader->Read(buffer.data(), write_size));
266*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(writer->Write(buffer.data(), write_size));
267*07fb1d06SElliott Hughes       bytes_wrote += write_size;
268*07fb1d06SElliott Hughes     }
269*07fb1d06SElliott Hughes 
270*07fb1d06SElliott Hughes     // puffhuff operation puffs a stream and huffs it back to the target stream
271*07fb1d06SElliott Hughes     // to make sure we can get to the original stream.
272*07fb1d06SElliott Hughes     if (FLAGS_operation == "puffhuff") {
273*07fb1d06SElliott Hughes       src_puffs = dst_puffs;
274*07fb1d06SElliott Hughes       dst_deflates_byte = src_deflates_byte;
275*07fb1d06SElliott Hughes       dst_deflates_bit = src_deflates_bit;
276*07fb1d06SElliott Hughes 
277*07fb1d06SElliott Hughes       auto read_puff_stream = MemoryStream::CreateForRead(puff_buffer);
278*07fb1d06SElliott Hughes       auto huffer = std::make_shared<Huffer>();
279*07fb1d06SElliott Hughes       auto huff_writer = PuffinStream::CreateForHuff(
280*07fb1d06SElliott Hughes           std::move(dst_stream), huffer, dst_puff_size, dst_deflates_bit,
281*07fb1d06SElliott Hughes           src_puffs);
282*07fb1d06SElliott Hughes 
283*07fb1d06SElliott Hughes       uint64_t bytes_read = 0;
284*07fb1d06SElliott Hughes       while (bytes_read < dst_puff_size) {
285*07fb1d06SElliott Hughes         auto read_size = std::min(static_cast<uint64_t>(buffer.size()),
286*07fb1d06SElliott Hughes                                   dst_puff_size - bytes_read);
287*07fb1d06SElliott Hughes         TEST_AND_RETURN_FALSE(read_puff_stream->Read(buffer.data(), read_size));
288*07fb1d06SElliott Hughes         TEST_AND_RETURN_FALSE(huff_writer->Write(buffer.data(), read_size));
289*07fb1d06SElliott Hughes         bytes_read += read_size;
290*07fb1d06SElliott Hughes       }
291*07fb1d06SElliott Hughes     }
292*07fb1d06SElliott Hughes   } else if (FLAGS_operation == "huff") {
293*07fb1d06SElliott Hughes     if (dst_deflates_bit.empty() && src_puffs.empty()) {
294*07fb1d06SElliott Hughes       LOG(WARNING) << "You should pass source puffs and destination deflates"
295*07fb1d06SElliott Hughes                    << ", is this intentional?";
296*07fb1d06SElliott Hughes     }
297*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(src_puffs.size() == dst_deflates_bit.size());
298*07fb1d06SElliott Hughes     uint64_t src_stream_size;
299*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(src_stream->GetSize(&src_stream_size));
300*07fb1d06SElliott Hughes     auto dst_file = FileStream::Open(FLAGS_dst_file, false, true);
301*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(dst_file);
302*07fb1d06SElliott Hughes 
303*07fb1d06SElliott Hughes     auto huffer = std::make_shared<Huffer>();
304*07fb1d06SElliott Hughes     auto dst_stream = PuffinStream::CreateForHuff(std::move(dst_file), huffer,
305*07fb1d06SElliott Hughes                                                   src_stream_size,
306*07fb1d06SElliott Hughes                                                   dst_deflates_bit, src_puffs);
307*07fb1d06SElliott Hughes 
308*07fb1d06SElliott Hughes     Buffer buffer(1024 * 1024);
309*07fb1d06SElliott Hughes     uint64_t bytes_read = 0;
310*07fb1d06SElliott Hughes     while (bytes_read < src_stream_size) {
311*07fb1d06SElliott Hughes       auto read_size = std::min(static_cast<uint64_t>(buffer.size()),
312*07fb1d06SElliott Hughes                                 src_stream_size - bytes_read);
313*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(src_stream->Read(buffer.data(), read_size));
314*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(dst_stream->Write(buffer.data(), read_size));
315*07fb1d06SElliott Hughes       bytes_read += read_size;
316*07fb1d06SElliott Hughes     }
317*07fb1d06SElliott Hughes   } else if (FLAGS_operation == "puffdiff") {
318*07fb1d06SElliott Hughes     auto dst_stream = FileStream::Open(FLAGS_dst_file, true, false);
319*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(dst_stream);
320*07fb1d06SElliott Hughes 
321*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(LocateDeflatesBasedOnFileType(
322*07fb1d06SElliott Hughes         src_stream, FLAGS_src_file, FLAGS_src_file_type, &src_deflates_bit));
323*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(LocateDeflatesBasedOnFileType(
324*07fb1d06SElliott Hughes         dst_stream, FLAGS_dst_file, FLAGS_dst_file_type, &dst_deflates_bit));
325*07fb1d06SElliott Hughes 
326*07fb1d06SElliott Hughes     if (src_deflates_bit.empty() && src_deflates_byte.empty()) {
327*07fb1d06SElliott Hughes       LOG(WARNING) << "You should pass source deflates, is this intentional?";
328*07fb1d06SElliott Hughes     }
329*07fb1d06SElliott Hughes     if (dst_deflates_bit.empty() && dst_deflates_byte.empty()) {
330*07fb1d06SElliott Hughes       LOG(WARNING) << "You should pass target deflates, is this intentional?";
331*07fb1d06SElliott Hughes     }
332*07fb1d06SElliott Hughes     if (!dst_extents.empty()) {
333*07fb1d06SElliott Hughes       dst_stream =
334*07fb1d06SElliott Hughes           ExtentStream::CreateForWrite(std::move(dst_stream), dst_extents);
335*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(dst_stream);
336*07fb1d06SElliott Hughes     }
337*07fb1d06SElliott Hughes 
338*07fb1d06SElliott Hughes     if (src_deflates_bit.empty()) {
339*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(FindDeflateSubBlocks(src_stream, src_deflates_byte,
340*07fb1d06SElliott Hughes                                                  &src_deflates_bit));
341*07fb1d06SElliott Hughes     }
342*07fb1d06SElliott Hughes 
343*07fb1d06SElliott Hughes     if (dst_deflates_bit.empty()) {
344*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(FindDeflateSubBlocks(dst_stream, dst_deflates_byte,
345*07fb1d06SElliott Hughes                                                  &dst_deflates_bit));
346*07fb1d06SElliott Hughes     }
347*07fb1d06SElliott Hughes 
348*07fb1d06SElliott Hughes     if (FLAGS_patch_algorithm != 0 && FLAGS_patch_algorithm != 1) {
349*07fb1d06SElliott Hughes       LOG(ERROR)
350*07fb1d06SElliott Hughes           << "The supported patch algorithms are 0: bsdiff, 1: zucchini.";
351*07fb1d06SElliott Hughes       return false;
352*07fb1d06SElliott Hughes     }
353*07fb1d06SElliott Hughes     // TODO(xunchang) add flags to select the bsdiff compressors.
354*07fb1d06SElliott Hughes     Buffer puffdiff_delta;
355*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(puffin::PuffDiff(
356*07fb1d06SElliott Hughes         std::move(src_stream), std::move(dst_stream), src_deflates_bit,
357*07fb1d06SElliott Hughes         dst_deflates_bit,
358*07fb1d06SElliott Hughes         {bsdiff::CompressorType::kBZ2, bsdiff::CompressorType::kBrotli},
359*07fb1d06SElliott Hughes         static_cast<puffin::PatchAlgorithm>(FLAGS_patch_algorithm),
360*07fb1d06SElliott Hughes         "/tmp/patch.tmp", &puffdiff_delta));
361*07fb1d06SElliott Hughes     if (FLAGS_verbose) {
362*07fb1d06SElliott Hughes       LOG(INFO) << "patch_size: " << puffdiff_delta.size();
363*07fb1d06SElliott Hughes     }
364*07fb1d06SElliott Hughes     auto patch_stream = FileStream::Open(FLAGS_patch_file, false, true);
365*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(patch_stream);
366*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(
367*07fb1d06SElliott Hughes         patch_stream->Write(puffdiff_delta.data(), puffdiff_delta.size()));
368*07fb1d06SElliott Hughes   } else if (FLAGS_operation == "puffpatch") {
369*07fb1d06SElliott Hughes     auto patch_stream = FileStream::Open(FLAGS_patch_file, true, false);
370*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(patch_stream);
371*07fb1d06SElliott Hughes     uint64_t patch_size;
372*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(patch_stream->GetSize(&patch_size));
373*07fb1d06SElliott Hughes 
374*07fb1d06SElliott Hughes     Buffer puffdiff_delta(patch_size);
375*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(
376*07fb1d06SElliott Hughes         patch_stream->Read(puffdiff_delta.data(), puffdiff_delta.size()));
377*07fb1d06SElliott Hughes     auto dst_stream = FileStream::Open(FLAGS_dst_file, false, true);
378*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(dst_stream);
379*07fb1d06SElliott Hughes     if (!dst_extents.empty()) {
380*07fb1d06SElliott Hughes       dst_stream =
381*07fb1d06SElliott Hughes           ExtentStream::CreateForWrite(std::move(dst_stream), dst_extents);
382*07fb1d06SElliott Hughes       TEST_AND_RETURN_FALSE(dst_stream);
383*07fb1d06SElliott Hughes     }
384*07fb1d06SElliott Hughes     // Apply the patch. Use 50MB cache, it should be enough for most of the
385*07fb1d06SElliott Hughes     // operations.
386*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(puffin::PuffPatch(
387*07fb1d06SElliott Hughes         std::move(src_stream), std::move(dst_stream), puffdiff_delta.data(),
388*07fb1d06SElliott Hughes         puffdiff_delta.size(), FLAGS_cache_size));
389*07fb1d06SElliott Hughes   }
390*07fb1d06SElliott Hughes 
391*07fb1d06SElliott Hughes   if (FLAGS_verbose) {
392*07fb1d06SElliott Hughes     LOG(INFO) << "src_deflates_byte: "
393*07fb1d06SElliott Hughes               << puffin::ExtentsToString(src_deflates_byte);
394*07fb1d06SElliott Hughes     LOG(INFO) << "dst_deflates_byte: "
395*07fb1d06SElliott Hughes               << puffin::ExtentsToString(dst_deflates_byte);
396*07fb1d06SElliott Hughes     LOG(INFO) << "src_deflates_bit: "
397*07fb1d06SElliott Hughes               << puffin::ExtentsToString(src_deflates_bit);
398*07fb1d06SElliott Hughes     LOG(INFO) << "dst_deflates_bit: "
399*07fb1d06SElliott Hughes               << puffin::ExtentsToString(dst_deflates_bit);
400*07fb1d06SElliott Hughes     LOG(INFO) << "src_puffs: " << puffin::ExtentsToString(src_puffs);
401*07fb1d06SElliott Hughes     LOG(INFO) << "dst_puffs: " << puffin::ExtentsToString(dst_puffs);
402*07fb1d06SElliott Hughes     LOG(INFO) << "src_extents: " << puffin::ExtentsToString(src_extents);
403*07fb1d06SElliott Hughes     LOG(INFO) << "dst_extents: " << puffin::ExtentsToString(dst_extents);
404*07fb1d06SElliott Hughes   }
405*07fb1d06SElliott Hughes   return true;
406*07fb1d06SElliott Hughes }
407*07fb1d06SElliott Hughes 
main(int argc,char ** argv)408*07fb1d06SElliott Hughes int main(int argc, char** argv) {
409*07fb1d06SElliott Hughes   if (!Main(argc, argv)) {
410*07fb1d06SElliott Hughes     return 1;
411*07fb1d06SElliott Hughes   }
412*07fb1d06SElliott Hughes   return 0;
413*07fb1d06SElliott Hughes }
414