// 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 #include #include #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* 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* patch_; // The compressor type to use and its quality (if any). CompressorType compressor_type_; int brotli_quality_; std::unique_ptr 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 diff_data_; std::vector extra_data_; std::vector 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_