You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
3.9 KiB

// 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_