169 const std::vector<uint8_t> &target,
170 std::vector<uint8_t> &patch) {
172 patch.insert(patch.end(), {
'B',
'P',
'S',
'1'});
174 encode(source.size(), patch);
175 encode(target.size(), patch);
178 size_t source_offset = 0;
179 size_t target_offset = 0;
180 int64_t source_rel_offset = 0;
181 int64_t target_rel_offset = 0;
183 while (target_offset < target.size()) {
184 if (source_offset < source.size() &&
185 source[source_offset] == target[target_offset]) {
187 while (source_offset + length < source.size() &&
188 target_offset + length < target.size() &&
189 source[source_offset + length] == target[target_offset + length]) {
192 encode((length - 1) << 2 | 0, patch);
193 source_offset += length;
194 target_offset += length;
198 target_offset + length < target.size() &&
199 (source_offset + length >= source.size() ||
200 source[source_offset + length] != target[target_offset + length])) {
204 encode((length - 1) << 2 | 1, patch);
205 for (
size_t i = 0; i < length; i++) {
206 patch.push_back(target[target_offset + i]);
208 target_offset += length;
213 if (source_offset < source.size()) {
215 int64_t offset = source_offset - source_rel_offset;
216 while (source_offset + length < source.size() &&
217 target_offset + length < target.size() &&
218 source[source_offset + length] == target[target_offset + length]) {
222 encode((length - 1) << 2 | 2, patch);
223 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
224 source_offset += length;
225 target_offset += length;
226 source_rel_offset = source_offset;
231 if (target_offset > 0) {
233 int64_t offset = target_offset - target_rel_offset;
234 while (target_offset + length < target.size() &&
235 target[target_offset - 1] == target[target_offset + length]) {
239 encode((length - 1) << 2 | 3, patch);
240 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
241 target_offset += length;
242 target_rel_offset = target_offset;
247 patch.resize(patch.size() + 12);
248 uint32_t source_checksum = crc32(source);
249 uint32_t target_checksum = crc32(target);
250 uint32_t patch_checksum = crc32(patch);
252 memcpy(patch.data() + patch.size() - 12, &source_checksum,
sizeof(uint32_t));
253 memcpy(patch.data() + patch.size() - 8, &target_checksum,
sizeof(uint32_t));
254 memcpy(patch.data() + patch.size() - 4, &patch_checksum,
sizeof(uint32_t));
258 const std::vector<uint8_t> &patch,
259 std::vector<uint8_t> &target) {
260 if (patch.size() < 4 || patch[0] !=
'B' || patch[1] !=
'P' ||
261 patch[2] !=
'S' || patch[3] !=
'1') {
262 throw std::runtime_error(
"Invalid patch format");
265 size_t patch_offset = 4;
266 uint64_t target_size = decode(patch, patch_offset);
267 uint64_t metadata_size = decode(patch, patch_offset);
268 patch_offset += metadata_size;
270 target.resize(target_size);
271 size_t source_offset = 0;
272 size_t target_offset = 0;
273 int64_t source_rel_offset = 0;
274 int64_t target_rel_offset = 0;
276 while (patch_offset < patch.size() - 12) {
277 uint64_t data = decode(patch, patch_offset);
278 uint64_t command = data & 3;
279 uint64_t length = (data >> 2) + 1;
284 target[target_offset++] = source[source_offset++];
289 target[target_offset++] = patch[patch_offset++];
294 int64_t offsetData = decode(patch, patch_offset);
295 source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
297 target[target_offset++] = source[source_rel_offset++];
302 uint64_t offsetData = decode(patch, patch_offset);
303 target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
305 target[target_offset++] = target[target_rel_offset++];
309 throw std::runtime_error(
"Invalid patch command");
313 uint32_t source_checksum;
314 uint32_t target_checksum;
315 uint32_t patch_checksum;
316 memcpy(&source_checksum, patch.data() + patch.size() - 12,
sizeof(uint32_t));
317 memcpy(&target_checksum, patch.data() + patch.size() - 8,
sizeof(uint32_t));
318 memcpy(&patch_checksum, patch.data() + patch.size() - 4,
sizeof(uint32_t));
320 if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
322 crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
323 throw std::runtime_error(
"Checksum mismatch");