48 const std::vector<uint8_t> &target,
49 std::vector<uint8_t> &patch) {
51 patch.insert(patch.end(), {
'B',
'P',
'S',
'1'});
53 encode(source.size(), patch);
54 encode(target.size(), patch);
57 size_t source_offset = 0;
58 size_t target_offset = 0;
59 int64_t source_rel_offset = 0;
60 int64_t target_rel_offset = 0;
62 while (target_offset < target.size()) {
63 if (source_offset < source.size() &&
64 source[source_offset] == target[target_offset]) {
66 while (source_offset + length < source.size() &&
67 target_offset + length < target.size() &&
68 source[source_offset + length] == target[target_offset + length]) {
71 encode((length - 1) << 2 | 0, patch);
72 source_offset += length;
73 target_offset += length;
77 target_offset + length < target.size() &&
78 (source_offset + length >= source.size() ||
79 source[source_offset + length] != target[target_offset + length])) {
83 encode((length - 1) << 2 | 1, patch);
84 for (
size_t i = 0; i < length; i++) {
85 patch.push_back(target[target_offset + i]);
87 target_offset += length;
92 if (source_offset < source.size()) {
94 int64_t offset = source_offset - source_rel_offset;
95 while (source_offset + length < source.size() &&
96 target_offset + length < target.size() &&
97 source[source_offset + length] == target[target_offset + length]) {
101 encode((length - 1) << 2 | 2, patch);
102 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
103 source_offset += length;
104 target_offset += length;
105 source_rel_offset = source_offset;
110 if (target_offset > 0) {
112 int64_t offset = target_offset - target_rel_offset;
113 while (target_offset + length < target.size() &&
114 target[target_offset - 1] == target[target_offset + length]) {
118 encode((length - 1) << 2 | 3, patch);
119 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
120 target_offset += length;
121 target_rel_offset = target_offset;
126 patch.resize(patch.size() + 12);
127 uint32_t source_checksum = crc32(source);
128 uint32_t target_checksum = crc32(target);
129 uint32_t patch_checksum = crc32(patch);
131 memcpy(patch.data() + patch.size() - 12, &source_checksum,
sizeof(uint32_t));
132 memcpy(patch.data() + patch.size() - 8, &target_checksum,
sizeof(uint32_t));
133 memcpy(patch.data() + patch.size() - 4, &patch_checksum,
sizeof(uint32_t));
137 const std::vector<uint8_t> &patch,
138 std::vector<uint8_t> &target) {
139 if (patch.size() < 4 || patch[0] !=
'B' || patch[1] !=
'P' ||
140 patch[2] !=
'S' || patch[3] !=
'1') {
141 throw std::runtime_error(
"Invalid patch format");
144 size_t patch_offset = 4;
145 uint64_t target_size = decode(patch, patch_offset);
146 uint64_t metadata_size = decode(patch, patch_offset);
147 patch_offset += metadata_size;
149 target.resize(target_size);
150 size_t source_offset = 0;
151 size_t target_offset = 0;
152 int64_t source_rel_offset = 0;
153 int64_t target_rel_offset = 0;
155 while (patch_offset < patch.size() - 12) {
156 uint64_t data = decode(patch, patch_offset);
157 uint64_t command = data & 3;
158 uint64_t length = (data >> 2) + 1;
163 target[target_offset++] = source[source_offset++];
168 target[target_offset++] = patch[patch_offset++];
173 int64_t offsetData = decode(patch, patch_offset);
174 source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
176 target[target_offset++] = source[source_rel_offset++];
181 uint64_t offsetData = decode(patch, patch_offset);
182 target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
184 target[target_offset++] = target[target_rel_offset++];
188 throw std::runtime_error(
"Invalid patch command");
192 uint32_t source_checksum;
193 uint32_t target_checksum;
194 uint32_t patch_checksum;
195 memcpy(&source_checksum, patch.data() + patch.size() - 12,
sizeof(uint32_t));
196 memcpy(&target_checksum, patch.data() + patch.size() - 8,
sizeof(uint32_t));
197 memcpy(&patch_checksum, patch.data() + patch.size() - 4,
sizeof(uint32_t));
199 if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
201 crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
202 throw std::runtime_error(
"Checksum mismatch");