Refactor pinch-zoom viewport.
[chromium-blink-merge.git] / courgette / ensemble_apply.cc
blob475cdf2672e03babfd591d1d36dded560245c398
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This file contains the code to apply a Courgette patch.
7 #include "courgette/ensemble.h"
9 #include "base/basictypes.h"
10 #include "base/file_util.h"
11 #include "base/logging.h"
13 #include "courgette/crc.h"
14 #include "courgette/region.h"
15 #include "courgette/streams.h"
16 #include "courgette/simple_delta.h"
17 #include "courgette/patcher_x86_32.h"
19 namespace courgette {
21 // EnsemblePatchApplication is all the logic and data required to apply the
22 // multi-stage patch.
23 class EnsemblePatchApplication {
24 public:
25 EnsemblePatchApplication();
26 ~EnsemblePatchApplication();
28 Status ReadHeader(SourceStream* header_stream);
30 Status InitBase(const Region& region);
32 Status ValidateBase();
34 Status ReadInitialParameters(SourceStream* initial_parameters);
36 Status PredictTransformParameters(SinkStreamSet* predicted_parameters);
38 Status SubpatchTransformParameters(SinkStreamSet* prediction,
39 SourceStream* correction,
40 SourceStreamSet* corrected_parameters);
42 Status TransformUp(SourceStreamSet* parameters,
43 SinkStreamSet* transformed_elements);
45 Status SubpatchTransformedElements(SinkStreamSet* elements,
46 SourceStream* correction,
47 SourceStreamSet* corrected_elements);
49 Status TransformDown(SourceStreamSet* transformed_elements,
50 SinkStream* basic_elements);
52 Status SubpatchFinalOutput(SourceStream* original,
53 SourceStream* correction,
54 SinkStream* corrected_ensemble);
56 private:
57 Status SubpatchStreamSets(SinkStreamSet* predicted_items,
58 SourceStream* correction,
59 SourceStreamSet* corrected_items,
60 SinkStream* corrected_items_storage);
62 Region base_region_; // Location of in-memory copy of 'old' version.
64 uint32 source_checksum_;
65 uint32 target_checksum_;
66 uint32 final_patch_input_size_prediction_;
68 std::vector<TransformationPatcher*> patchers_;
70 SinkStream corrected_parameters_storage_;
71 SinkStream corrected_elements_storage_;
73 DISALLOW_COPY_AND_ASSIGN(EnsemblePatchApplication);
76 EnsemblePatchApplication::EnsemblePatchApplication()
77 : source_checksum_(0), target_checksum_(0),
78 final_patch_input_size_prediction_(0) {
81 EnsemblePatchApplication::~EnsemblePatchApplication() {
82 for (size_t i = 0; i < patchers_.size(); ++i) {
83 delete patchers_[i];
87 Status EnsemblePatchApplication::ReadHeader(SourceStream* header_stream) {
88 uint32 magic;
89 if (!header_stream->ReadVarint32(&magic))
90 return C_BAD_ENSEMBLE_MAGIC;
92 if (magic != CourgettePatchFile::kMagic)
93 return C_BAD_ENSEMBLE_MAGIC;
95 uint32 version;
96 if (!header_stream->ReadVarint32(&version))
97 return C_BAD_ENSEMBLE_VERSION;
99 if (version != CourgettePatchFile::kVersion)
100 return C_BAD_ENSEMBLE_VERSION;
102 if (!header_stream->ReadVarint32(&source_checksum_))
103 return C_BAD_ENSEMBLE_HEADER;
105 if (!header_stream->ReadVarint32(&target_checksum_))
106 return C_BAD_ENSEMBLE_HEADER;
108 if (!header_stream->ReadVarint32(&final_patch_input_size_prediction_))
109 return C_BAD_ENSEMBLE_HEADER;
111 return C_OK;
114 Status EnsemblePatchApplication::InitBase(const Region& region) {
115 base_region_.assign(region);
116 return C_OK;
119 Status EnsemblePatchApplication::ValidateBase() {
120 uint32 checksum = CalculateCrc(base_region_.start(), base_region_.length());
121 if (source_checksum_ != checksum)
122 return C_BAD_ENSEMBLE_CRC;
124 return C_OK;
127 Status EnsemblePatchApplication::ReadInitialParameters(
128 SourceStream* transformation_parameters) {
129 uint32 number_of_transformations = 0;
130 if (!transformation_parameters->ReadVarint32(&number_of_transformations))
131 return C_BAD_ENSEMBLE_HEADER;
133 for (size_t i = 0; i < number_of_transformations; ++i) {
134 uint32 kind;
135 if (!transformation_parameters->ReadVarint32(&kind))
136 return C_BAD_ENSEMBLE_HEADER;
138 TransformationPatcher* patcher = NULL;
140 switch (kind)
142 case EXE_WIN_32_X86:
143 patcher = new PatcherX86_32(base_region_);
144 break;
145 case EXE_ELF_32_X86:
146 patcher = new PatcherX86_32(base_region_);
147 break;
150 if (patcher)
151 patchers_.push_back(patcher);
152 else
153 return C_BAD_ENSEMBLE_HEADER;
156 for (size_t i = 0; i < patchers_.size(); ++i) {
157 Status status = patchers_[i]->Init(transformation_parameters);
158 if (status != C_OK)
159 return status;
162 // All transformation_parameters should have been consumed by the above loop.
163 if (!transformation_parameters->Empty())
164 return C_BAD_ENSEMBLE_HEADER;
166 return C_OK;
169 Status EnsemblePatchApplication::PredictTransformParameters(
170 SinkStreamSet* all_predicted_parameters) {
171 for (size_t i = 0; i < patchers_.size(); ++i) {
172 SinkStreamSet single_predicted_parameters;
173 Status status =
174 patchers_[i]->PredictTransformParameters(&single_predicted_parameters);
175 if (status != C_OK)
176 return status;
177 if (!all_predicted_parameters->WriteSet(&single_predicted_parameters))
178 return C_STREAM_ERROR;
180 return C_OK;
183 Status EnsemblePatchApplication::SubpatchTransformParameters(
184 SinkStreamSet* predicted_parameters,
185 SourceStream* correction,
186 SourceStreamSet* corrected_parameters) {
187 return SubpatchStreamSets(predicted_parameters,
188 correction,
189 corrected_parameters,
190 &corrected_parameters_storage_);
193 Status EnsemblePatchApplication::TransformUp(
194 SourceStreamSet* parameters,
195 SinkStreamSet* transformed_elements) {
196 for (size_t i = 0; i < patchers_.size(); ++i) {
197 SourceStreamSet single_parameters;
198 if (!parameters->ReadSet(&single_parameters))
199 return C_STREAM_ERROR;
200 SinkStreamSet single_transformed_element;
201 Status status = patchers_[i]->Transform(&single_parameters,
202 &single_transformed_element);
203 if (status != C_OK)
204 return status;
205 if (!single_parameters.Empty())
206 return C_STREAM_NOT_CONSUMED;
207 if (!transformed_elements->WriteSet(&single_transformed_element))
208 return C_STREAM_ERROR;
211 if (!parameters->Empty())
212 return C_STREAM_NOT_CONSUMED;
213 return C_OK;
216 Status EnsemblePatchApplication::SubpatchTransformedElements(
217 SinkStreamSet* predicted_elements,
218 SourceStream* correction,
219 SourceStreamSet* corrected_elements) {
220 return SubpatchStreamSets(predicted_elements,
221 correction,
222 corrected_elements,
223 &corrected_elements_storage_);
226 Status EnsemblePatchApplication::TransformDown(
227 SourceStreamSet* transformed_elements,
228 SinkStream* basic_elements) {
229 // Construct blob of original input followed by reformed elements.
231 if (!basic_elements->Reserve(final_patch_input_size_prediction_)) {
232 return C_STREAM_ERROR;
235 // The original input:
236 if (!basic_elements->Write(base_region_.start(), base_region_.length()))
237 return C_STREAM_ERROR;
239 for (size_t i = 0; i < patchers_.size(); ++i) {
240 SourceStreamSet single_corrected_element;
241 if (!transformed_elements->ReadSet(&single_corrected_element))
242 return C_STREAM_ERROR;
243 Status status = patchers_[i]->Reform(&single_corrected_element,
244 basic_elements);
245 if (status != C_OK)
246 return status;
247 if (!single_corrected_element.Empty())
248 return C_STREAM_NOT_CONSUMED;
251 if (!transformed_elements->Empty())
252 return C_STREAM_NOT_CONSUMED;
253 // We have totally consumed transformed_elements, so can free the
254 // storage to which it referred.
255 corrected_elements_storage_.Retire();
257 return C_OK;
260 Status EnsemblePatchApplication::SubpatchFinalOutput(
261 SourceStream* original,
262 SourceStream* correction,
263 SinkStream* corrected_ensemble) {
264 Status delta_status = ApplySimpleDelta(original, correction,
265 corrected_ensemble);
266 if (delta_status != C_OK)
267 return delta_status;
269 if (CalculateCrc(corrected_ensemble->Buffer(),
270 corrected_ensemble->Length()) != target_checksum_)
271 return C_BAD_ENSEMBLE_CRC;
273 return C_OK;
276 Status EnsemblePatchApplication::SubpatchStreamSets(
277 SinkStreamSet* predicted_items,
278 SourceStream* correction,
279 SourceStreamSet* corrected_items,
280 SinkStream* corrected_items_storage) {
281 SinkStream linearized_predicted_items;
282 if (!predicted_items->CopyTo(&linearized_predicted_items))
283 return C_STREAM_ERROR;
285 SourceStream prediction;
286 prediction.Init(linearized_predicted_items);
288 Status status = ApplySimpleDelta(&prediction,
289 correction,
290 corrected_items_storage);
291 if (status != C_OK)
292 return status;
294 if (!corrected_items->Init(corrected_items_storage->Buffer(),
295 corrected_items_storage->Length()))
296 return C_STREAM_ERROR;
298 return C_OK;
301 Status ApplyEnsemblePatch(SourceStream* base,
302 SourceStream* patch,
303 SinkStream* output) {
304 Status status;
305 EnsemblePatchApplication patch_process;
307 status = patch_process.ReadHeader(patch);
308 if (status != C_OK)
309 return status;
311 status = patch_process.InitBase(Region(base->Buffer(), base->Remaining()));
312 if (status != C_OK)
313 return status;
315 status = patch_process.ValidateBase();
316 if (status != C_OK)
317 return status;
319 // The rest of the patch stream is a StreamSet.
320 SourceStreamSet patch_streams;
321 patch_streams.Init(patch);
323 SourceStream* transformation_descriptions = patch_streams.stream(0);
324 SourceStream* parameter_correction = patch_streams.stream(1);
325 SourceStream* transformed_elements_correction = patch_streams.stream(2);
326 SourceStream* ensemble_correction = patch_streams.stream(3);
328 status = patch_process.ReadInitialParameters(transformation_descriptions);
329 if (status != C_OK)
330 return status;
332 SinkStreamSet predicted_parameters;
333 status = patch_process.PredictTransformParameters(&predicted_parameters);
334 if (status != C_OK)
335 return status;
337 SourceStreamSet corrected_parameters;
338 status = patch_process.SubpatchTransformParameters(&predicted_parameters,
339 parameter_correction,
340 &corrected_parameters);
341 if (status != C_OK)
342 return status;
344 SinkStreamSet transformed_elements;
345 status = patch_process.TransformUp(&corrected_parameters,
346 &transformed_elements);
347 if (status != C_OK)
348 return status;
350 SourceStreamSet corrected_transformed_elements;
351 status = patch_process.SubpatchTransformedElements(
352 &transformed_elements,
353 transformed_elements_correction,
354 &corrected_transformed_elements);
355 if (status != C_OK)
356 return status;
358 SinkStream original_ensemble_and_corrected_base_elements;
359 status = patch_process.TransformDown(
360 &corrected_transformed_elements,
361 &original_ensemble_and_corrected_base_elements);
362 if (status != C_OK)
363 return status;
365 SourceStream final_patch_prediction;
366 final_patch_prediction.Init(original_ensemble_and_corrected_base_elements);
367 status = patch_process.SubpatchFinalOutput(&final_patch_prediction,
368 ensemble_correction, output);
369 if (status != C_OK)
370 return status;
372 return C_OK;
375 Status ApplyEnsemblePatch(const FilePath::CharType* old_file_name,
376 const FilePath::CharType* patch_file_name,
377 const FilePath::CharType* new_file_name) {
378 // First read enough of the patch file to validate the header is well-formed.
379 // A few varint32 numbers should fit in 100.
380 FilePath patch_file_path(patch_file_name);
381 file_util::MemoryMappedFile patch_file;
382 if (!patch_file.Initialize(patch_file_path))
383 return C_READ_OPEN_ERROR;
385 // 'Dry-run' the first step of the patch process to validate format of header.
386 SourceStream patch_header_stream;
387 patch_header_stream.Init(patch_file.data(), patch_file.length());
388 EnsemblePatchApplication patch_process;
389 Status status = patch_process.ReadHeader(&patch_header_stream);
390 if (status != C_OK)
391 return status;
393 // Read the old_file.
394 FilePath old_file_path(old_file_name);
395 file_util::MemoryMappedFile old_file;
396 if (!old_file.Initialize(old_file_path))
397 return C_READ_ERROR;
399 // Apply patch on streams.
400 SourceStream old_source_stream;
401 SourceStream patch_source_stream;
402 old_source_stream.Init(old_file.data(), old_file.length());
403 patch_source_stream.Init(patch_file.data(), patch_file.length());
404 SinkStream new_sink_stream;
405 status = ApplyEnsemblePatch(&old_source_stream, &patch_source_stream,
406 &new_sink_stream);
407 if (status != C_OK)
408 return status;
410 // Write the patched data to |new_file_name|.
411 FilePath new_file_path(new_file_name);
412 int written =
413 file_util::WriteFile(
414 new_file_path,
415 reinterpret_cast<const char*>(new_sink_stream.Buffer()),
416 static_cast<int>(new_sink_stream.Length()));
417 if (written == -1)
418 return C_WRITE_OPEN_ERROR;
419 if (static_cast<size_t>(written) != new_sink_stream.Length())
420 return C_WRITE_ERROR;
422 return C_OK;
425 } // namespace