xref: /aosp_15_r20/external/bsdiff/endsley_patch_writer.cc (revision a3a45f308bd90ef1a6e6a5e8fb92fe449b895909)
1*a3a45f30SXin Li // Copyright 2017 The Chromium OS Authors. All rights reserved.
2*a3a45f30SXin Li // Use of this source code is governed by a BSD-style license that can be
3*a3a45f30SXin Li // found in the LICENSE file.
4*a3a45f30SXin Li 
5*a3a45f30SXin Li #include "bsdiff/endsley_patch_writer.h"
6*a3a45f30SXin Li 
7*a3a45f30SXin Li #include <string.h>
8*a3a45f30SXin Li 
9*a3a45f30SXin Li #include <algorithm>
10*a3a45f30SXin Li 
11*a3a45f30SXin Li #include "bsdiff/brotli_compressor.h"
12*a3a45f30SXin Li #include "bsdiff/bz2_compressor.h"
13*a3a45f30SXin Li #include "bsdiff/logging.h"
14*a3a45f30SXin Li 
15*a3a45f30SXin Li namespace {
16*a3a45f30SXin Li 
17*a3a45f30SXin Li constexpr uint8_t kEndsleyMagicHeader[] = "ENDSLEY/BSDIFF43";
18*a3a45f30SXin Li 
EncodeInt64(int64_t x,uint8_t * buf)19*a3a45f30SXin Li void EncodeInt64(int64_t x, uint8_t* buf) {
20*a3a45f30SXin Li   uint64_t y = x < 0 ? (1ULL << 63ULL) - x : x;
21*a3a45f30SXin Li   for (int i = 0; i < 8; ++i) {
22*a3a45f30SXin Li     buf[i] = y & 0xff;
23*a3a45f30SXin Li     y /= 256;
24*a3a45f30SXin Li   }
25*a3a45f30SXin Li }
26*a3a45f30SXin Li 
27*a3a45f30SXin Li // The minimum size that we would consider flushing out.
28*a3a45f30SXin Li constexpr size_t kMinimumFlushSize = 1024 * 1024;  // 1 MiB
29*a3a45f30SXin Li 
30*a3a45f30SXin Li }  // namespace
31*a3a45f30SXin Li 
32*a3a45f30SXin Li namespace bsdiff {
33*a3a45f30SXin Li 
Init(size_t new_size)34*a3a45f30SXin Li bool EndsleyPatchWriter::Init(size_t new_size) {
35*a3a45f30SXin Li   switch (compressor_type_) {
36*a3a45f30SXin Li     case CompressorType::kNoCompression:
37*a3a45f30SXin Li       // The patch is uncompressed and it will need exactly:
38*a3a45f30SXin Li       //   new_size + 24 * len(control_entries) + sizeof(header)
39*a3a45f30SXin Li       // We don't know the length of the control entries yet, but we can reserve
40*a3a45f30SXin Li       // enough space to hold at least |new_size|.
41*a3a45f30SXin Li       patch_->clear();
42*a3a45f30SXin Li       patch_->reserve(new_size);
43*a3a45f30SXin Li       break;
44*a3a45f30SXin Li     case CompressorType::kBrotli:
45*a3a45f30SXin Li       compressor_.reset(new BrotliCompressor(brotli_quality_));
46*a3a45f30SXin Li       if (!compressor_) {
47*a3a45f30SXin Li         LOG(ERROR) << "Error creating brotli compressor.";
48*a3a45f30SXin Li         return false;
49*a3a45f30SXin Li       }
50*a3a45f30SXin Li       break;
51*a3a45f30SXin Li     case CompressorType::kBZ2:
52*a3a45f30SXin Li       compressor_.reset(new BZ2Compressor());
53*a3a45f30SXin Li       if (!compressor_) {
54*a3a45f30SXin Li         LOG(ERROR) << "Error creating BZ2 compressor.";
55*a3a45f30SXin Li         return false;
56*a3a45f30SXin Li       }
57*a3a45f30SXin Li       break;
58*a3a45f30SXin Li   }
59*a3a45f30SXin Li 
60*a3a45f30SXin Li   // Header is the magic followed by the new length.
61*a3a45f30SXin Li   uint8_t header[24];
62*a3a45f30SXin Li   memcpy(header, kEndsleyMagicHeader, 16);
63*a3a45f30SXin Li   EncodeInt64(new_size, header + 16);
64*a3a45f30SXin Li   EmitBuffer(header, sizeof(header));
65*a3a45f30SXin Li   return true;
66*a3a45f30SXin Li }
67*a3a45f30SXin Li 
WriteDiffStream(const uint8_t * data,size_t size)68*a3a45f30SXin Li bool EndsleyPatchWriter::WriteDiffStream(const uint8_t* data, size_t size) {
69*a3a45f30SXin Li   if (!size)
70*a3a45f30SXin Li     return true;
71*a3a45f30SXin Li   // Speed-up the common case where the diff stream data is added right after
72*a3a45f30SXin Li   // the control entry that refers to it.
73*a3a45f30SXin Li   if (control_.empty() && pending_diff_ >= size) {
74*a3a45f30SXin Li     pending_diff_ -= size;
75*a3a45f30SXin Li     EmitBuffer(data, size);
76*a3a45f30SXin Li     return true;
77*a3a45f30SXin Li   }
78*a3a45f30SXin Li 
79*a3a45f30SXin Li   diff_data_.insert(diff_data_.end(), data, data + size);
80*a3a45f30SXin Li   return true;
81*a3a45f30SXin Li }
82*a3a45f30SXin Li 
WriteExtraStream(const uint8_t * data,size_t size)83*a3a45f30SXin Li bool EndsleyPatchWriter::WriteExtraStream(const uint8_t* data, size_t size) {
84*a3a45f30SXin Li   if (!size)
85*a3a45f30SXin Li     return true;
86*a3a45f30SXin Li   // Speed-up the common case where the extra stream data is added right after
87*a3a45f30SXin Li   // the diff stream data and the control entry that refers to it. Note that
88*a3a45f30SXin Li   // the diff data comes first so we need to make sure it is all out.
89*a3a45f30SXin Li   if (control_.empty() && !pending_diff_ && pending_extra_ >= size) {
90*a3a45f30SXin Li     pending_extra_ -= size;
91*a3a45f30SXin Li     EmitBuffer(data, size);
92*a3a45f30SXin Li     return true;
93*a3a45f30SXin Li   }
94*a3a45f30SXin Li 
95*a3a45f30SXin Li   extra_data_.insert(extra_data_.end(), data, data + size);
96*a3a45f30SXin Li   return true;
97*a3a45f30SXin Li }
98*a3a45f30SXin Li 
AddControlEntry(const ControlEntry & entry)99*a3a45f30SXin Li bool EndsleyPatchWriter::AddControlEntry(const ControlEntry& entry) {
100*a3a45f30SXin Li   // Speed-up the common case where the control entry is added when there's
101*a3a45f30SXin Li   // nothing else pending.
102*a3a45f30SXin Li   if (control_.empty() && diff_data_.empty() && extra_data_.empty() &&
103*a3a45f30SXin Li       !pending_diff_ && !pending_extra_) {
104*a3a45f30SXin Li     pending_diff_ = entry.diff_size;
105*a3a45f30SXin Li     pending_extra_ = entry.extra_size;
106*a3a45f30SXin Li     EmitControlEntry(entry);
107*a3a45f30SXin Li     return true;
108*a3a45f30SXin Li   }
109*a3a45f30SXin Li 
110*a3a45f30SXin Li   control_.push_back(entry);
111*a3a45f30SXin Li   pending_control_data_ += entry.diff_size + entry.extra_size;
112*a3a45f30SXin Li 
113*a3a45f30SXin Li   // Check whether it is worth Flushing the entries now that the we have more
114*a3a45f30SXin Li   // control entries. We need control entries to write enough output data, and
115*a3a45f30SXin Li   // we need that output data to be at least 50% of the available diff and extra
116*a3a45f30SXin Li   // data. This last requirement is to reduce the overhead of removing the
117*a3a45f30SXin Li   // flushed data.
118*a3a45f30SXin Li   if (pending_control_data_ > kMinimumFlushSize &&
119*a3a45f30SXin Li       (diff_data_.size() + extra_data_.size()) / 2 <= pending_control_data_) {
120*a3a45f30SXin Li     Flush();
121*a3a45f30SXin Li   }
122*a3a45f30SXin Li 
123*a3a45f30SXin Li   return true;
124*a3a45f30SXin Li }
125*a3a45f30SXin Li 
Close()126*a3a45f30SXin Li bool EndsleyPatchWriter::Close() {
127*a3a45f30SXin Li   // Flush any pending data.
128*a3a45f30SXin Li   Flush();
129*a3a45f30SXin Li 
130*a3a45f30SXin Li   if (pending_diff_ || pending_extra_ || !control_.empty()) {
131*a3a45f30SXin Li     LOG(ERROR) << "Insufficient data sent to diff/extra streams";
132*a3a45f30SXin Li     return false;
133*a3a45f30SXin Li   }
134*a3a45f30SXin Li 
135*a3a45f30SXin Li   if (!diff_data_.empty() || !extra_data_.empty()) {
136*a3a45f30SXin Li     LOG(ERROR) << "Pending data to diff/extra not flushed out on Close()";
137*a3a45f30SXin Li     return false;
138*a3a45f30SXin Li   }
139*a3a45f30SXin Li 
140*a3a45f30SXin Li   if (compressor_) {
141*a3a45f30SXin Li     if (!compressor_->Finish())
142*a3a45f30SXin Li       return false;
143*a3a45f30SXin Li     *patch_ = compressor_->GetCompressedData();
144*a3a45f30SXin Li   }
145*a3a45f30SXin Li 
146*a3a45f30SXin Li   return true;
147*a3a45f30SXin Li }
148*a3a45f30SXin Li 
EmitControlEntry(const ControlEntry & entry)149*a3a45f30SXin Li void EndsleyPatchWriter::EmitControlEntry(const ControlEntry& entry) {
150*a3a45f30SXin Li   // Generate the 24 byte control entry.
151*a3a45f30SXin Li   uint8_t buf[24];
152*a3a45f30SXin Li   EncodeInt64(entry.diff_size, buf);
153*a3a45f30SXin Li   EncodeInt64(entry.extra_size, buf + 8);
154*a3a45f30SXin Li   EncodeInt64(entry.offset_increment, buf + 16);
155*a3a45f30SXin Li   EmitBuffer(buf, sizeof(buf));
156*a3a45f30SXin Li }
157*a3a45f30SXin Li 
EmitBuffer(const uint8_t * data,size_t size)158*a3a45f30SXin Li void EndsleyPatchWriter::EmitBuffer(const uint8_t* data, size_t size) {
159*a3a45f30SXin Li   if (compressor_) {
160*a3a45f30SXin Li     compressor_->Write(data, size);
161*a3a45f30SXin Li   } else {
162*a3a45f30SXin Li     patch_->insert(patch_->end(), data, data + size);
163*a3a45f30SXin Li   }
164*a3a45f30SXin Li }
165*a3a45f30SXin Li 
Flush()166*a3a45f30SXin Li void EndsleyPatchWriter::Flush() {
167*a3a45f30SXin Li   size_t used_diff = 0;
168*a3a45f30SXin Li   size_t used_extra = 0;
169*a3a45f30SXin Li   size_t used_control = 0;
170*a3a45f30SXin Li   do {
171*a3a45f30SXin Li     if (!pending_diff_ && !pending_extra_ && used_control < control_.size()) {
172*a3a45f30SXin Li       // We can emit a control entry in these conditions.
173*a3a45f30SXin Li       const ControlEntry& entry = control_[used_control];
174*a3a45f30SXin Li       used_control++;
175*a3a45f30SXin Li 
176*a3a45f30SXin Li       pending_diff_ = entry.diff_size;
177*a3a45f30SXin Li       pending_extra_ = entry.extra_size;
178*a3a45f30SXin Li       pending_control_data_ -= entry.extra_size + entry.diff_size;
179*a3a45f30SXin Li       EmitControlEntry(entry);
180*a3a45f30SXin Li     }
181*a3a45f30SXin Li 
182*a3a45f30SXin Li     if (pending_diff_) {
183*a3a45f30SXin Li       size_t diff_size = std::min(diff_data_.size() - used_diff, pending_diff_);
184*a3a45f30SXin Li       EmitBuffer(diff_data_.data() + used_diff, diff_size);
185*a3a45f30SXin Li       pending_diff_ -= diff_size;
186*a3a45f30SXin Li       used_diff += diff_size;
187*a3a45f30SXin Li     }
188*a3a45f30SXin Li 
189*a3a45f30SXin Li     if (!pending_diff_ && pending_extra_) {
190*a3a45f30SXin Li       size_t extra_size =
191*a3a45f30SXin Li           std::min(extra_data_.size() - used_extra, pending_extra_);
192*a3a45f30SXin Li       EmitBuffer(extra_data_.data() + used_extra, extra_size);
193*a3a45f30SXin Li       pending_extra_ -= extra_size;
194*a3a45f30SXin Li       used_extra += extra_size;
195*a3a45f30SXin Li     }
196*a3a45f30SXin Li   } while (!pending_diff_ && !pending_extra_ && used_control < control_.size());
197*a3a45f30SXin Li 
198*a3a45f30SXin Li   if (used_diff)
199*a3a45f30SXin Li     diff_data_.erase(diff_data_.begin(), diff_data_.begin() + used_diff);
200*a3a45f30SXin Li   if (used_extra)
201*a3a45f30SXin Li     extra_data_.erase(extra_data_.begin(), extra_data_.begin() + used_extra);
202*a3a45f30SXin Li   if (used_control)
203*a3a45f30SXin Li     control_.erase(control_.begin(), control_.begin() + used_control);
204*a3a45f30SXin Li }
205*a3a45f30SXin Li 
206*a3a45f30SXin Li }  // namespace bsdiff
207