C++程序  |  99行  |  3.89 KB

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