| // Copyright 2017 The Chromium 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 COMPONENTS_ZUCCHINI_PATCH_WRITER_H_ |
| #define COMPONENTS_ZUCCHINI_PATCH_WRITER_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "components/zucchini/buffer_sink.h" |
| #include "components/zucchini/buffer_view.h" |
| #include "components/zucchini/image_utils.h" |
| #include "components/zucchini/patch_utils.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace zucchini { |
| |
| namespace patch { |
| |
| // If sufficient space is available, serializes |element_match| into |sink| and |
| // returns true. Otherwise returns false, and |sink| will be in an undefined |
| // state. |
| bool SerializeElementMatch(const ElementMatch& element_match, BufferSink* sink); |
| |
| // Returns the size in bytes required to serialize |element_match|. |
| size_t SerializedElementMatchSize(const ElementMatch& element_match); |
| |
| // If sufficient space is available, serializes |buffer| into |sink| and returns |
| // true. Otherwise returns false, and |sink| will be in an undefined state. |
| bool SerializeBuffer(const std::vector<uint8_t>& buffer, BufferSink* sink); |
| |
| // Returns the size in bytes required to serialize |buffer|. |
| size_t SerializedBufferSize(const std::vector<uint8_t>& buffer); |
| |
| } // namespace patch |
| |
| // Each of *Sink classes below has an associated "main type", and performs the |
| // following: |
| // - Receives multiple "main type" elements (hence "Sink" in the name). |
| // - Encodes list of received data, and writes them to internal storage (e.g., |
| // applying delta encoding). |
| // - Writes encoded data to BufferSink. |
| // |
| // Common "core functions" implemented for *Sink classes are: |
| // - void PutNext(const MAIN_TYPE& inst): Encodes and writes an instance of |
| // MAIN_TYPE to internal storage. Assumptions may be applied to successive |
| // |inst| provided. |
| // - size_t SerializedSize() const: Returns the serialized size in bytes of |
| // internal storage. |
| // - bool SerializeInto(BufferSink* sink) const: If |sink| has enough space, |
| // serializes internal storage into |sink|, and returns true. Otherwise |
| // returns false. |
| // |
| // Usage of *Sink instances don't mix, and PuttNext() have dissimilar |
| // interfaces. Therefore we do not use inheritance to relate *Sink classes, |
| // simply implement "core functions" with matching names. |
| |
| // Sink for equivalences. |
| class EquivalenceSink { |
| public: |
| EquivalenceSink(); |
| EquivalenceSink(const std::vector<uint8_t>& src_skip, |
| const std::vector<uint8_t>& dst_skip, |
| const std::vector<uint8_t>& copy_count); |
| |
| EquivalenceSink(EquivalenceSink&&); |
| ~EquivalenceSink(); |
| |
| // Core functions. |
| // Equivalences must be given by increasing |Equivalence::dst_offset|. |
| void PutNext(const Equivalence& equivalence); |
| size_t SerializedSize() const; |
| bool SerializeInto(BufferSink* sink) const; |
| |
| private: |
| // Offset in source, delta-encoded starting from end of last equivalence, and |
| // stored as signed varint. |
| std::vector<uint8_t> src_skip_; |
| // Offset in destination, delta-encoded starting from end of last equivalence, |
| // and stored as unsigned varint. |
| std::vector<uint8_t> dst_skip_; |
| // Length of equivalence stored as unsigned varint. |
| // TODO(etiennep): Investigate on bias. |
| std::vector<uint8_t> copy_count_; |
| |
| offset_t src_offset_ = 0; // Last offset in source. |
| offset_t dst_offset_ = 0; // Last offset in destination. |
| }; |
| |
| // Sink for extra data. |
| class ExtraDataSink { |
| public: |
| ExtraDataSink(); |
| explicit ExtraDataSink(const std::vector<uint8_t>& extra_data); |
| ExtraDataSink(ExtraDataSink&&); |
| ~ExtraDataSink(); |
| |
| // Core functions. |
| void PutNext(ConstBufferView region); |
| size_t SerializedSize() const; |
| bool SerializeInto(BufferSink* sink) const; |
| |
| private: |
| std::vector<uint8_t> extra_data_; |
| }; |
| |
| // Sink for raw delta. |
| class RawDeltaSink { |
| public: |
| RawDeltaSink(); |
| RawDeltaSink(const std::vector<uint8_t>& raw_delta_skip, |
| const std::vector<uint8_t>& raw_delta_diff); |
| RawDeltaSink(RawDeltaSink&&); |
| ~RawDeltaSink(); |
| |
| // Core functions. |
| // Deltas must be given by increasing |RawDeltaUnit::copy_offset|. |
| void PutNext(const RawDeltaUnit& delta); |
| size_t SerializedSize() const; |
| bool SerializeInto(BufferSink* sink) const; |
| |
| private: |
| std::vector<uint8_t> raw_delta_skip_; // Copy offset stating from last delta. |
| std::vector<uint8_t> raw_delta_diff_; // Bytewise difference. |
| |
| // We keep track of the compensation needed for next copy offset, taking into |
| // accound delta encoding and bias of -1. Stored delta are biased by -1, so a |
| // sequence of single byte deltas is represented as a string of 0's. |
| offset_t copy_offset_compensation_ = 0; |
| }; |
| |
| // Sink for reference delta. |
| class ReferenceDeltaSink { |
| public: |
| ReferenceDeltaSink(); |
| explicit ReferenceDeltaSink(const std::vector<uint8_t>& reference_delta); |
| ReferenceDeltaSink(ReferenceDeltaSink&&); |
| ~ReferenceDeltaSink(); |
| |
| // Core functions. |
| void PutNext(int32_t diff); |
| size_t SerializedSize() const; |
| bool SerializeInto(BufferSink* sink) const; |
| |
| private: |
| std::vector<uint8_t> reference_delta_; |
| }; |
| |
| // Sink for additional targets. |
| class TargetSink { |
| public: |
| TargetSink(); |
| explicit TargetSink(const std::vector<uint8_t>& extra_targets); |
| TargetSink(TargetSink&&); |
| ~TargetSink(); |
| |
| // Core functions. |
| // Targets must be given by increasing order. |
| void PutNext(uint32_t target); |
| size_t SerializedSize() const; |
| bool SerializeInto(BufferSink* sink) const; |
| |
| private: |
| // Targets are delta-encoded and biaised by 1, stored as unsigned varint. |
| std::vector<uint8_t> extra_targets_; |
| |
| // We keep track of the compensation needed for next target, taking into |
| // accound delta encoding and bias of -1. |
| offset_t target_compensation_ = 0; |
| }; |
| |
| // Following are utility classes to write structured data forming a patch. |
| |
| // Utility to write a patch element. A patch element contains all the |
| // information necessary to patch a single element. This class |
| // provides an interface to individually set different building blocks of data |
| // in the patch element. |
| class PatchElementWriter { |
| public: |
| PatchElementWriter(); |
| explicit PatchElementWriter(ElementMatch element_match); |
| PatchElementWriter(PatchElementWriter&&); |
| ~PatchElementWriter(); |
| |
| const ElementMatch& element_match() const { return element_match_; } |
| const Element& old_element() const { return element_match_.old_element; } |
| const Element& new_element() const { return element_match_.new_element; } |
| |
| // Following methods set individual blocks for this element. Previous |
| // corresponding block is replaced. All streams must be set before call to |
| // SerializedSize() of SerializeInto(). |
| |
| void SetEquivalenceSink(EquivalenceSink&& equivalences) { |
| equivalences_.emplace(std::move(equivalences)); |
| } |
| void SetExtraDataSink(ExtraDataSink&& extra_data) { |
| extra_data_.emplace(std::move(extra_data)); |
| } |
| void SetRawDeltaSink(RawDeltaSink&& raw_delta) { |
| raw_delta_.emplace(std::move(raw_delta)); |
| } |
| void SetReferenceDeltaSink(ReferenceDeltaSink reference_delta) { |
| reference_delta_.emplace(std::move(reference_delta)); |
| } |
| // Set additional targets for pool identified with |pool_tag|. |
| void SetTargetSink(PoolTag pool_tag, TargetSink&& extra_targets) { |
| DCHECK(pool_tag != kNoPoolTag); |
| extra_targets_.emplace(pool_tag, std::move(extra_targets)); |
| } |
| |
| // Returns the serialized size in bytes of the data this object is holding. |
| size_t SerializedSize() const; |
| |
| // If sufficient space is available, serializes data into |sink|, which is at |
| // least SerializedSize() bytes, and returns true. Otherwise returns false. |
| bool SerializeInto(BufferSink* sink) const; |
| |
| private: |
| ElementMatch element_match_; |
| absl::optional<EquivalenceSink> equivalences_; |
| absl::optional<ExtraDataSink> extra_data_; |
| absl::optional<RawDeltaSink> raw_delta_; |
| absl::optional<ReferenceDeltaSink> reference_delta_; |
| std::map<PoolTag, TargetSink> extra_targets_; |
| }; |
| |
| // Utility to write a Zucchini ensemble patch. An ensemble patch is the |
| // concatenation of a patch header with a vector of patch elements. |
| class EnsemblePatchWriter { |
| public: |
| explicit EnsemblePatchWriter(const PatchHeader& header); |
| EnsemblePatchWriter(ConstBufferView old_image, ConstBufferView new_image); |
| EnsemblePatchWriter(const EnsemblePatchWriter&) = delete; |
| const EnsemblePatchWriter& operator=(const EnsemblePatchWriter&) = delete; |
| ~EnsemblePatchWriter(); |
| |
| // Reserves space for |count| patch elements. |
| void ReserveElements(size_t count) { elements_.reserve(count); } |
| |
| // Adds an patch element into the patch. Patch elements must be ordered by |
| // their location in the new image file. |
| void AddElement(PatchElementWriter&& patch_element); |
| |
| // Returns the serialized size in bytes of the data this object is holding. |
| size_t SerializedSize() const; |
| |
| // If sufficient space is available, serializes data into |sink|, which is at |
| // least SerializedSize() bytes, and returns true. Otherwise returns false. |
| bool SerializeInto(BufferSink* sink) const; |
| |
| // If sufficient space is available, serializes data into |buffer|, which is |
| // at least SerializedSize() bytes, and returns true. Otherwise returns false. |
| bool SerializeInto(MutableBufferView buffer) const { |
| BufferSink sink(buffer); |
| return SerializeInto(&sink); |
| } |
| |
| private: |
| PatchHeader header_; |
| std::vector<PatchElementWriter> elements_; |
| offset_t current_dst_offset_ = 0; |
| }; |
| |
| } // namespace zucchini |
| |
| #endif // COMPONENTS_ZUCCHINI_PATCH_WRITER_H_ |