| // Copyright 2017 The Chromium OS Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef _BSDIFF_ENDSLEY_PATCH_WRITER_H_ | |
| #define _BSDIFF_ENDSLEY_PATCH_WRITER_H_ | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include "bsdiff/compressor_interface.h" | |
| #include "bsdiff/constants.h" | |
| #include "bsdiff/patch_writer_interface.h" | |
| namespace bsdiff { | |
| // A PatchWriterInterface class compatible with the format used by Android Play | |
| // Store's bsdiff implementation, which is based on Matthew Endsley's bsdiff | |
| // implementation. See https://github.com/mendsley/bsdiff for the original | |
| // implementation of this format. See also Google's APK patch size estimator for | |
| // more information on the file-by-file format used by Play Store: | |
| // https://github.com/googlesamples/apk-patch-size-estimator | |
| // This format, identified by the "ENDSLEY/BSDIFF43" magic string, uses a single | |
| // stream with the control entries, diff data and extra data interleaved. After | |
| // the header, each Control Entry is stored in 24 bytes followed by the diff | |
| // stream data for that entry only, and then followed by the extra stream data | |
| // for that entry only. The format doesn't handle the compression of the data, | |
| // instead, the whole file (including the magic string) is compressed with any | |
| // compression algorithm. | |
| // This format is easier to parse and allows the patch to be streamed, but by | |
| // mixing the diff and extra data into the same compression context offers a | |
| // slightly worse compression ratio (about 3.5% compared to upstream's format). | |
| class EndsleyPatchWriter : public PatchWriterInterface { | |
| public: | |
| // Create the patch writer that will write the data to the passed vector | |
| // |patch|, resizing it as needed. The |patch| vector must be valid until | |
| // Close() is called or this patch is destroyed. The data in |patch| will be | |
| // compressed using the compressor type |type|. | |
| EndsleyPatchWriter(std::vector<uint8_t>* patch, | |
| CompressorType type, | |
| int brotli_quality) | |
| : patch_(patch), | |
| compressor_type_(type), | |
| brotli_quality_(brotli_quality) {} | |
| // PatchWriterInterface overrides. | |
| bool Init(size_t new_size) override; | |
| bool WriteDiffStream(const uint8_t* data, size_t size) override; | |
| bool WriteExtraStream(const uint8_t* data, size_t size) override; | |
| bool AddControlEntry(const ControlEntry& entry) override; | |
| bool Close() override; | |
| private: | |
| // Emit at the end of the |patch_| vector the passed control entry. | |
| void EmitControlEntry(const ControlEntry& entry); | |
| // Emit at the end of the |patch_| vector the passed buffer. | |
| void EmitBuffer(const uint8_t* data, size_t size); | |
| // Flush as much as possible of the pending data. | |
| void Flush(); | |
| // The vector we are writing to, owned by the caller. | |
| std::vector<uint8_t>* patch_; | |
| // The compressor type to use and its quality (if any). | |
| CompressorType compressor_type_; | |
| int brotli_quality_; | |
| std::unique_ptr<CompressorInterface> compressor_; | |
| // The pending diff and extra data to be encoded in the file. These vectors | |
| // would not be used whenever is possible to the data directly to the patch_ | |
| // vector; namely when the control, diff and extra stream data are provided in | |
| // that order for each control entry. | |
| std::vector<uint8_t> diff_data_; | |
| std::vector<uint8_t> extra_data_; | |
| std::vector<ControlEntry> control_; | |
| // Defined as the sum of all the diff_size and extra_size values in | |
| // |control_|. This is used to determine whether it is worth Flushing the | |
| // pending data. | |
| size_t pending_control_data_{0}; | |
| // Number of bytes in the diff and extra stream that are pending in the | |
| // last control entry encoded in the |patch_|. If both are zero the last | |
| // control entry was completely emitted. | |
| size_t pending_diff_{0}; | |
| size_t pending_extra_{0}; | |
| }; | |
| } // namespace bsdiff | |
| #endif // _BSDIFF_ENDSLEY_PATCH_WRITER_H_ |