49 const std::vector<uint8_t> &target,
50 std::vector<uint8_t> &patch) {
52 patch.insert(patch.end(), {
'B',
'P',
'S',
'1'});
54 encode(source.size(), patch);
55 encode(target.size(), patch);
58 size_t source_offset = 0;
59 size_t target_offset = 0;
60 int64_t source_rel_offset = 0;
61 int64_t target_rel_offset = 0;
63 while (target_offset < target.size()) {
64 if (source_offset < source.size() &&
65 source[source_offset] == target[target_offset]) {
67 while (source_offset + length < source.size() &&
68 target_offset + length < target.size() &&
69 source[source_offset + length] == target[target_offset + length]) {
72 encode((length - 1) << 2 | 0, patch);
73 source_offset += length;
74 target_offset += length;
78 target_offset + length < target.size() &&
79 (source_offset + length >= source.size() ||
80 source[source_offset + length] != target[target_offset + length])) {
84 encode((length - 1) << 2 | 1, patch);
85 for (
size_t i = 0; i < length; i++) {
86 patch.push_back(target[target_offset + i]);
88 target_offset += length;
93 if (source_offset < source.size()) {
95 int64_t offset = source_offset - source_rel_offset;
96 while (source_offset + length < source.size() &&
97 target_offset + length < target.size() &&
98 source[source_offset + length] == target[target_offset + length]) {
102 encode((length - 1) << 2 | 2, patch);
103 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
104 source_offset += length;
105 target_offset += length;
106 source_rel_offset = source_offset;
111 if (target_offset > 0) {
113 int64_t offset = target_offset - target_rel_offset;
114 while (target_offset + length < target.size() &&
115 target[target_offset - 1] == target[target_offset + length]) {
119 encode((length - 1) << 2 | 3, patch);
120 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
121 target_offset += length;
122 target_rel_offset = target_offset;
127 patch.resize(patch.size() + 12);
128 uint32_t source_checksum = crc32(source);
129 uint32_t target_checksum = crc32(target);
130 uint32_t patch_checksum = crc32(patch);
132 memcpy(patch.data() + patch.size() - 12, &source_checksum,
sizeof(uint32_t));
133 memcpy(patch.data() + patch.size() - 8, &target_checksum,
sizeof(uint32_t));
134 memcpy(patch.data() + patch.size() - 4, &patch_checksum,
sizeof(uint32_t));
138 const std::vector<uint8_t> &patch,
139 std::vector<uint8_t> &target) {
140 if (patch.size() < 4 || patch[0] !=
'B' || patch[1] !=
'P' ||
141 patch[2] !=
'S' || patch[3] !=
'1') {
142 throw std::runtime_error(
"Invalid patch format");
145 size_t patch_offset = 4;
146 uint64_t target_size = decode(patch, patch_offset);
147 uint64_t metadata_size = decode(patch, patch_offset);
148 patch_offset += metadata_size;
150 target.resize(target_size);
151 size_t source_offset = 0;
152 size_t target_offset = 0;
153 int64_t source_rel_offset = 0;
154 int64_t target_rel_offset = 0;
156 while (patch_offset < patch.size() - 12) {
157 uint64_t data = decode(patch, patch_offset);
158 uint64_t command = data & 3;
159 uint64_t length = (data >> 2) + 1;
164 target[target_offset++] = source[source_offset++];
169 target[target_offset++] = patch[patch_offset++];
174 int64_t offsetData = decode(patch, patch_offset);
175 source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
177 target[target_offset++] = source[source_rel_offset++];
182 uint64_t offsetData = decode(patch, patch_offset);
183 target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
185 target[target_offset++] = target[target_rel_offset++];
189 throw std::runtime_error(
"Invalid patch command");
193 uint32_t source_checksum;
194 uint32_t target_checksum;
195 uint32_t patch_checksum;
196 memcpy(&source_checksum, patch.data() + patch.size() - 12,
sizeof(uint32_t));
197 memcpy(&target_checksum, patch.data() + patch.size() - 8,
sizeof(uint32_t));
198 memcpy(&patch_checksum, patch.data() + patch.size() - 4,
sizeof(uint32_t));
200 if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
202 crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
203 throw std::runtime_error(
"Checksum mismatch");