xref: /aosp_15_r20/external/puffin/src/puffdiff.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 "puffin/src/include/puffin/puffdiff.h"
6*07fb1d06SElliott Hughes 
7*07fb1d06SElliott Hughes #include <endian.h>
8*07fb1d06SElliott Hughes #include <inttypes.h>
9*07fb1d06SElliott Hughes #include <unistd.h>
10*07fb1d06SElliott Hughes 
11*07fb1d06SElliott Hughes #include <string>
12*07fb1d06SElliott Hughes #include <vector>
13*07fb1d06SElliott Hughes 
14*07fb1d06SElliott Hughes #include "bsdiff/bsdiff.h"
15*07fb1d06SElliott Hughes #include "bsdiff/patch_writer_factory.h"
16*07fb1d06SElliott Hughes #include "zucchini/buffer_view.h"
17*07fb1d06SElliott Hughes #include "zucchini/patch_writer.h"
18*07fb1d06SElliott Hughes #include "zucchini/zucchini.h"
19*07fb1d06SElliott Hughes 
20*07fb1d06SElliott Hughes #include "puffin/file_stream.h"
21*07fb1d06SElliott Hughes #include "puffin/memory_stream.h"
22*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/brotli_util.h"
23*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/common.h"
24*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/puffer.h"
25*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/puffpatch.h"
26*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/utils.h"
27*07fb1d06SElliott Hughes #include "puffin/src/logging.h"
28*07fb1d06SElliott Hughes #include "puffin/src/puffin.pb.h"
29*07fb1d06SElliott Hughes #include "puffin/src/puffin_stream.h"
30*07fb1d06SElliott Hughes 
31*07fb1d06SElliott Hughes using std::string;
32*07fb1d06SElliott Hughes using std::vector;
33*07fb1d06SElliott Hughes 
34*07fb1d06SElliott Hughes namespace puffin {
35*07fb1d06SElliott Hughes 
36*07fb1d06SElliott Hughes namespace {
37*07fb1d06SElliott Hughes const int kBrotliCompressionQuality = 11;
38*07fb1d06SElliott Hughes 
39*07fb1d06SElliott Hughes template <typename T>
CopyVectorToRpf(const T & from,google::protobuf::RepeatedPtrField<metadata::BitExtent> * to,size_t coef)40*07fb1d06SElliott Hughes void CopyVectorToRpf(
41*07fb1d06SElliott Hughes     const T& from,
42*07fb1d06SElliott Hughes     google::protobuf::RepeatedPtrField<metadata::BitExtent>* to,
43*07fb1d06SElliott Hughes     size_t coef) {
44*07fb1d06SElliott Hughes   to->Reserve(from.size());
45*07fb1d06SElliott Hughes   for (const auto& ext : from) {
46*07fb1d06SElliott Hughes     auto tmp = to->Add();
47*07fb1d06SElliott Hughes     tmp->set_offset(ext.offset * coef);
48*07fb1d06SElliott Hughes     tmp->set_length(ext.length * coef);
49*07fb1d06SElliott Hughes   }
50*07fb1d06SElliott Hughes }
51*07fb1d06SElliott Hughes 
52*07fb1d06SElliott Hughes // Structure of a puffin patch
53*07fb1d06SElliott Hughes // +-------+------------------+-------------+--------------+
54*07fb1d06SElliott Hughes // |P|U|F|1| PatchHeader Size | PatchHeader | raw patch |
55*07fb1d06SElliott Hughes // +-------+------------------+-------------+--------------+
CreatePatch(const Buffer & raw_patch,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const vector<ByteExtent> & src_puffs,const vector<ByteExtent> & dst_puffs,uint64_t src_puff_size,uint64_t dst_puff_size,PatchAlgorithm patchAlgorithm,Buffer * patch)56*07fb1d06SElliott Hughes bool CreatePatch(const Buffer& raw_patch,
57*07fb1d06SElliott Hughes                  const vector<BitExtent>& src_deflates,
58*07fb1d06SElliott Hughes                  const vector<BitExtent>& dst_deflates,
59*07fb1d06SElliott Hughes                  const vector<ByteExtent>& src_puffs,
60*07fb1d06SElliott Hughes                  const vector<ByteExtent>& dst_puffs,
61*07fb1d06SElliott Hughes                  uint64_t src_puff_size,
62*07fb1d06SElliott Hughes                  uint64_t dst_puff_size,
63*07fb1d06SElliott Hughes                  PatchAlgorithm patchAlgorithm,
64*07fb1d06SElliott Hughes                  Buffer* patch) {
65*07fb1d06SElliott Hughes   metadata::PatchHeader header;
66*07fb1d06SElliott Hughes   header.set_version(1);
67*07fb1d06SElliott Hughes 
68*07fb1d06SElliott Hughes   CopyVectorToRpf(src_deflates, header.mutable_src()->mutable_deflates(), 1);
69*07fb1d06SElliott Hughes   CopyVectorToRpf(dst_deflates, header.mutable_dst()->mutable_deflates(), 1);
70*07fb1d06SElliott Hughes   CopyVectorToRpf(src_puffs, header.mutable_src()->mutable_puffs(), 8);
71*07fb1d06SElliott Hughes   CopyVectorToRpf(dst_puffs, header.mutable_dst()->mutable_puffs(), 8);
72*07fb1d06SElliott Hughes 
73*07fb1d06SElliott Hughes   header.mutable_src()->set_puff_length(src_puff_size);
74*07fb1d06SElliott Hughes   header.mutable_dst()->set_puff_length(dst_puff_size);
75*07fb1d06SElliott Hughes   header.set_type(static_cast<metadata::PatchHeader_PatchType>(patchAlgorithm));
76*07fb1d06SElliott Hughes 
77*07fb1d06SElliott Hughes   const size_t header_size_long = header.ByteSizeLong();
78*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(header_size_long <= UINT32_MAX);
79*07fb1d06SElliott Hughes   const uint32_t header_size = header_size_long;
80*07fb1d06SElliott Hughes 
81*07fb1d06SElliott Hughes   uint64_t offset = 0;
82*07fb1d06SElliott Hughes   patch->resize(kMagicLength + sizeof(header_size) + header_size +
83*07fb1d06SElliott Hughes                 raw_patch.size());
84*07fb1d06SElliott Hughes 
85*07fb1d06SElliott Hughes   memcpy(patch->data() + offset, kMagic, kMagicLength);
86*07fb1d06SElliott Hughes   offset += kMagicLength;
87*07fb1d06SElliott Hughes 
88*07fb1d06SElliott Hughes   // Read header size from big-endian mode.
89*07fb1d06SElliott Hughes   uint32_t be_header_size = htobe32(header_size);
90*07fb1d06SElliott Hughes   memcpy(patch->data() + offset, &be_header_size, sizeof(be_header_size));
91*07fb1d06SElliott Hughes   offset += 4;
92*07fb1d06SElliott Hughes 
93*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(
94*07fb1d06SElliott Hughes       header.SerializeToArray(patch->data() + offset, header_size));
95*07fb1d06SElliott Hughes   offset += header_size;
96*07fb1d06SElliott Hughes 
97*07fb1d06SElliott Hughes   memcpy(patch->data() + offset, raw_patch.data(), raw_patch.size());
98*07fb1d06SElliott Hughes 
99*07fb1d06SElliott Hughes   if (raw_patch.size() > patch->size()) {
100*07fb1d06SElliott Hughes     LOG(ERROR) << "Puffin patch is invalid";
101*07fb1d06SElliott Hughes   }
102*07fb1d06SElliott Hughes   return true;
103*07fb1d06SElliott Hughes }
104*07fb1d06SElliott Hughes 
105*07fb1d06SElliott Hughes }  // namespace
106*07fb1d06SElliott Hughes 
PuffDiff(UniqueStreamPtr src,UniqueStreamPtr dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const vector<bsdiff::CompressorType> & compressors,PatchAlgorithm patchAlgorithm,const string & tmp_filepath,Buffer * patch)107*07fb1d06SElliott Hughes bool PuffDiff(UniqueStreamPtr src,
108*07fb1d06SElliott Hughes               UniqueStreamPtr dst,
109*07fb1d06SElliott Hughes               const vector<BitExtent>& src_deflates,
110*07fb1d06SElliott Hughes               const vector<BitExtent>& dst_deflates,
111*07fb1d06SElliott Hughes               const vector<bsdiff::CompressorType>& compressors,
112*07fb1d06SElliott Hughes               PatchAlgorithm patchAlgorithm,
113*07fb1d06SElliott Hughes               const string& tmp_filepath,
114*07fb1d06SElliott Hughes               Buffer* patch) {
115*07fb1d06SElliott Hughes   auto puffer = std::make_shared<Puffer>();
116*07fb1d06SElliott Hughes   auto puff_deflate_stream =
117*07fb1d06SElliott Hughes       [&puffer](UniqueStreamPtr stream, const vector<BitExtent>& deflates,
118*07fb1d06SElliott Hughes                 Buffer* puff_buffer, vector<ByteExtent>* puffs) {
119*07fb1d06SElliott Hughes         uint64_t puff_size;
120*07fb1d06SElliott Hughes         TEST_AND_RETURN_FALSE(stream->Seek(0));
121*07fb1d06SElliott Hughes         TEST_AND_RETURN_FALSE(
122*07fb1d06SElliott Hughes             FindPuffLocations(stream, deflates, puffs, &puff_size));
123*07fb1d06SElliott Hughes         TEST_AND_RETURN_FALSE(stream->Seek(0));
124*07fb1d06SElliott Hughes         auto src_puffin_stream = PuffinStream::CreateForPuff(
125*07fb1d06SElliott Hughes             std::move(stream), puffer, puff_size, deflates, *puffs);
126*07fb1d06SElliott Hughes         puff_buffer->resize(puff_size);
127*07fb1d06SElliott Hughes         TEST_AND_RETURN_FALSE(
128*07fb1d06SElliott Hughes             src_puffin_stream->Read(puff_buffer->data(), puff_buffer->size()));
129*07fb1d06SElliott Hughes         return true;
130*07fb1d06SElliott Hughes       };
131*07fb1d06SElliott Hughes 
132*07fb1d06SElliott Hughes   Buffer src_puff_buffer;
133*07fb1d06SElliott Hughes   Buffer dst_puff_buffer;
134*07fb1d06SElliott Hughes   vector<ByteExtent> src_puffs, dst_puffs;
135*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(src), src_deflates,
136*07fb1d06SElliott Hughes                                             &src_puff_buffer, &src_puffs));
137*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(dst), dst_deflates,
138*07fb1d06SElliott Hughes                                             &dst_puff_buffer, &dst_puffs));
139*07fb1d06SElliott Hughes 
140*07fb1d06SElliott Hughes   if (patchAlgorithm == PatchAlgorithm::kBsdiff) {
141*07fb1d06SElliott Hughes     auto bsdiff_patch_writer = bsdiff::CreateBSDF2PatchWriter(
142*07fb1d06SElliott Hughes         tmp_filepath, compressors, kBrotliCompressionQuality);
143*07fb1d06SElliott Hughes 
144*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(
145*07fb1d06SElliott Hughes         0 == bsdiff::bsdiff(src_puff_buffer.data(), src_puff_buffer.size(),
146*07fb1d06SElliott Hughes                             dst_puff_buffer.data(), dst_puff_buffer.size(),
147*07fb1d06SElliott Hughes                             bsdiff_patch_writer.get(), nullptr));
148*07fb1d06SElliott Hughes 
149*07fb1d06SElliott Hughes     auto bsdiff_patch = FileStream::Open(tmp_filepath, true, false);
150*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(bsdiff_patch);
151*07fb1d06SElliott Hughes     uint64_t patch_size;
152*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(bsdiff_patch->GetSize(&patch_size));
153*07fb1d06SElliott Hughes     Buffer bsdiff_patch_buf(patch_size);
154*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(
155*07fb1d06SElliott Hughes         bsdiff_patch->Read(bsdiff_patch_buf.data(), bsdiff_patch_buf.size()));
156*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(bsdiff_patch->Close());
157*07fb1d06SElliott Hughes 
158*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(CreatePatch(
159*07fb1d06SElliott Hughes         bsdiff_patch_buf, src_deflates, dst_deflates, src_puffs, dst_puffs,
160*07fb1d06SElliott Hughes         src_puff_buffer.size(), dst_puff_buffer.size(), patchAlgorithm, patch));
161*07fb1d06SElliott Hughes   } else if (patchAlgorithm == PatchAlgorithm::kZucchini) {
162*07fb1d06SElliott Hughes     zucchini::ConstBufferView src_bytes(src_puff_buffer.data(),
163*07fb1d06SElliott Hughes                                         src_puff_buffer.size());
164*07fb1d06SElliott Hughes     zucchini::ConstBufferView dst_bytes(dst_puff_buffer.data(),
165*07fb1d06SElliott Hughes                                         dst_puff_buffer.size());
166*07fb1d06SElliott Hughes 
167*07fb1d06SElliott Hughes     zucchini::EnsemblePatchWriter patch_writer(src_bytes, dst_bytes);
168*07fb1d06SElliott Hughes     auto status = zucchini::GenerateBuffer(src_bytes, dst_bytes, &patch_writer);
169*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(status == zucchini::status::kStatusSuccess);
170*07fb1d06SElliott Hughes 
171*07fb1d06SElliott Hughes     Buffer zucchini_patch_buf(patch_writer.SerializedSize());
172*07fb1d06SElliott Hughes     patch_writer.SerializeInto(
173*07fb1d06SElliott Hughes         {zucchini_patch_buf.data(), zucchini_patch_buf.size()});
174*07fb1d06SElliott Hughes 
175*07fb1d06SElliott Hughes     // Use brotli to compress the zucchini patch.
176*07fb1d06SElliott Hughes     // TODO(197361113) respect the CompressorType parameter for zucchini.
177*07fb1d06SElliott Hughes     Buffer compressed_patch;
178*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(BrotliEncode(zucchini_patch_buf.data(),
179*07fb1d06SElliott Hughes                                        zucchini_patch_buf.size(),
180*07fb1d06SElliott Hughes                                        &compressed_patch));
181*07fb1d06SElliott Hughes 
182*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(CreatePatch(
183*07fb1d06SElliott Hughes         compressed_patch, src_deflates, dst_deflates, src_puffs, dst_puffs,
184*07fb1d06SElliott Hughes         src_puff_buffer.size(), dst_puff_buffer.size(), patchAlgorithm, patch));
185*07fb1d06SElliott Hughes   } else {
186*07fb1d06SElliott Hughes     LOG(ERROR) << "unsupported type " << static_cast<int>(patchAlgorithm);
187*07fb1d06SElliott Hughes     return false;
188*07fb1d06SElliott Hughes   }
189*07fb1d06SElliott Hughes 
190*07fb1d06SElliott Hughes   return true;
191*07fb1d06SElliott Hughes }
192*07fb1d06SElliott Hughes 
PuffDiff(UniqueStreamPtr src,UniqueStreamPtr dst,const std::vector<BitExtent> & src_deflates,const std::vector<BitExtent> & dst_deflates,const std::vector<bsdiff::CompressorType> & compressors,const std::string & tmp_filepath,Buffer * patch)193*07fb1d06SElliott Hughes bool PuffDiff(UniqueStreamPtr src,
194*07fb1d06SElliott Hughes               UniqueStreamPtr dst,
195*07fb1d06SElliott Hughes               const std::vector<BitExtent>& src_deflates,
196*07fb1d06SElliott Hughes               const std::vector<BitExtent>& dst_deflates,
197*07fb1d06SElliott Hughes               const std::vector<bsdiff::CompressorType>& compressors,
198*07fb1d06SElliott Hughes               const std::string& tmp_filepath,
199*07fb1d06SElliott Hughes               Buffer* patch) {
200*07fb1d06SElliott Hughes   return PuffDiff(std::move(src), std::move(dst), src_deflates, dst_deflates,
201*07fb1d06SElliott Hughes                   compressors, PatchAlgorithm::kBsdiff, tmp_filepath, patch);
202*07fb1d06SElliott Hughes }
203*07fb1d06SElliott Hughes 
PuffDiff(const Buffer & src,const Buffer & dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const vector<bsdiff::CompressorType> & compressors,const string & tmp_filepath,Buffer * patch)204*07fb1d06SElliott Hughes bool PuffDiff(const Buffer& src,
205*07fb1d06SElliott Hughes               const Buffer& dst,
206*07fb1d06SElliott Hughes               const vector<BitExtent>& src_deflates,
207*07fb1d06SElliott Hughes               const vector<BitExtent>& dst_deflates,
208*07fb1d06SElliott Hughes               const vector<bsdiff::CompressorType>& compressors,
209*07fb1d06SElliott Hughes               const string& tmp_filepath,
210*07fb1d06SElliott Hughes               Buffer* patch) {
211*07fb1d06SElliott Hughes   return PuffDiff(MemoryStream::CreateForRead(src),
212*07fb1d06SElliott Hughes                   MemoryStream::CreateForRead(dst), src_deflates, dst_deflates,
213*07fb1d06SElliott Hughes                   compressors, PatchAlgorithm::kBsdiff, tmp_filepath, patch);
214*07fb1d06SElliott Hughes }
215*07fb1d06SElliott Hughes 
PuffDiff(const Buffer & src,const Buffer & dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const string & tmp_filepath,Buffer * patch)216*07fb1d06SElliott Hughes bool PuffDiff(const Buffer& src,
217*07fb1d06SElliott Hughes               const Buffer& dst,
218*07fb1d06SElliott Hughes               const vector<BitExtent>& src_deflates,
219*07fb1d06SElliott Hughes               const vector<BitExtent>& dst_deflates,
220*07fb1d06SElliott Hughes               const string& tmp_filepath,
221*07fb1d06SElliott Hughes               Buffer* patch) {
222*07fb1d06SElliott Hughes   return PuffDiff(
223*07fb1d06SElliott Hughes       src, dst, src_deflates, dst_deflates,
224*07fb1d06SElliott Hughes       {bsdiff::CompressorType::kBZ2, bsdiff::CompressorType::kBrotli},
225*07fb1d06SElliott Hughes       tmp_filepath, patch);
226*07fb1d06SElliott Hughes }
227*07fb1d06SElliott Hughes 
228*07fb1d06SElliott Hughes }  // namespace puffin
229