puffdiff.cc 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // Copyright 2017 The Chromium OS Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "puffin/src/include/puffin/puffdiff.h"
  5. #include <inttypes.h>
  6. #include <string>
  7. #include <vector>
  8. #include "base/big_endian.h"
  9. #include "zucchini/buffer_view.h"
  10. #include "zucchini/patch_writer.h"
  11. #include "zucchini/zucchini.h"
  12. #include "puffin/file_stream.h"
  13. #include "puffin/memory_stream.h"
  14. #include "puffin/src/include/puffin/brotli_util.h"
  15. #include "puffin/src/include/puffin/common.h"
  16. #include "puffin/src/include/puffin/puffer.h"
  17. #include "puffin/src/include/puffin/puffpatch.h"
  18. #include "puffin/src/include/puffin/utils.h"
  19. #include "puffin/src/logging.h"
  20. #include "puffin/src/puffin.pb.h"
  21. #include "puffin/src/puffin_stream.h"
  22. using std::string;
  23. using std::vector;
  24. namespace puffin {
  25. namespace {
  26. template <typename T>
  27. void CopyVectorToRpf(
  28. const T& from,
  29. google::protobuf::RepeatedPtrField<metadata::BitExtent>* to,
  30. size_t coef) {
  31. to->Reserve(from.size());
  32. for (const auto& ext : from) {
  33. auto tmp = to->Add();
  34. tmp->set_offset(ext.offset * coef);
  35. tmp->set_length(ext.length * coef);
  36. }
  37. }
  38. // Structure of a Puffin patch
  39. // +-------+------------------+-------------+--------------+
  40. // |P|U|F|1| PatchHeader Size | PatchHeader | raw patch |
  41. // +-------+------------------+-------------+--------------+
  42. bool CreatePatch(const Buffer& raw_patch,
  43. const vector<BitExtent>& src_deflates,
  44. const vector<BitExtent>& dst_deflates,
  45. const vector<ByteExtent>& src_puffs,
  46. const vector<ByteExtent>& dst_puffs,
  47. uint64_t src_puff_size,
  48. uint64_t dst_puff_size,
  49. PatchAlgorithm patchAlgorithm,
  50. Buffer* patch) {
  51. metadata::PatchHeader header;
  52. header.set_version(1);
  53. CopyVectorToRpf(src_deflates, header.mutable_src()->mutable_deflates(), 1);
  54. CopyVectorToRpf(dst_deflates, header.mutable_dst()->mutable_deflates(), 1);
  55. CopyVectorToRpf(src_puffs, header.mutable_src()->mutable_puffs(), 8);
  56. CopyVectorToRpf(dst_puffs, header.mutable_dst()->mutable_puffs(), 8);
  57. header.mutable_src()->set_puff_length(src_puff_size);
  58. header.mutable_dst()->set_puff_length(dst_puff_size);
  59. header.set_type(static_cast<metadata::PatchHeader_PatchType>(patchAlgorithm));
  60. const size_t header_size_long = header.ByteSizeLong();
  61. TEST_AND_RETURN_FALSE(header_size_long <= UINT32_MAX);
  62. const uint32_t header_size = header_size_long;
  63. uint64_t offset = 0;
  64. patch->resize(kMagicLength + sizeof(header_size) + header_size +
  65. raw_patch.size());
  66. memcpy(patch->data() + offset, kMagic, kMagicLength);
  67. offset += kMagicLength;
  68. // Read header size from big-endian mode.
  69. uint32_t be_header_size = 0;
  70. base::ReadBigEndian(reinterpret_cast<const uint8_t*>(&header_size),
  71. &be_header_size);
  72. memcpy(patch->data() + offset, &be_header_size, sizeof(be_header_size));
  73. offset += 4;
  74. TEST_AND_RETURN_FALSE(
  75. header.SerializeToArray(patch->data() + offset, header_size));
  76. offset += header_size;
  77. memcpy(patch->data() + offset, raw_patch.data(), raw_patch.size());
  78. if (raw_patch.size() > patch->size()) {
  79. LOG(ERROR) << "Puffin patch is invalid";
  80. }
  81. return true;
  82. }
  83. } // namespace
  84. bool PuffDiff(UniqueStreamPtr src,
  85. UniqueStreamPtr dst,
  86. const vector<BitExtent>& src_deflates,
  87. const vector<BitExtent>& dst_deflates,
  88. const vector<puffin::CompressorType>& compressors,
  89. PatchAlgorithm patchAlgorithm,
  90. const string& tmp_filepath,
  91. Buffer* patch) {
  92. auto puffer = std::make_shared<Puffer>();
  93. auto puff_deflate_stream =
  94. [&puffer](UniqueStreamPtr stream, const vector<BitExtent>& deflates,
  95. Buffer* puff_buffer, vector<ByteExtent>* puffs) {
  96. uint64_t puff_size;
  97. TEST_AND_RETURN_FALSE(stream->Seek(0));
  98. TEST_AND_RETURN_FALSE(
  99. FindPuffLocations(stream, deflates, puffs, &puff_size));
  100. TEST_AND_RETURN_FALSE(stream->Seek(0));
  101. auto src_puffin_stream = PuffinStream::CreateForPuff(
  102. std::move(stream), puffer, puff_size, deflates, *puffs);
  103. puff_buffer->resize(puff_size);
  104. TEST_AND_RETURN_FALSE(
  105. src_puffin_stream->Read(puff_buffer->data(), puff_buffer->size()));
  106. return true;
  107. };
  108. Buffer src_puff_buffer;
  109. Buffer dst_puff_buffer;
  110. vector<ByteExtent> src_puffs, dst_puffs;
  111. TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(src), src_deflates,
  112. &src_puff_buffer, &src_puffs));
  113. TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(dst), dst_deflates,
  114. &dst_puff_buffer, &dst_puffs));
  115. if (patchAlgorithm == PatchAlgorithm::kZucchini) {
  116. zucchini::ConstBufferView src_bytes(src_puff_buffer.data(),
  117. src_puff_buffer.size());
  118. zucchini::ConstBufferView dst_bytes(dst_puff_buffer.data(),
  119. dst_puff_buffer.size());
  120. zucchini::EnsemblePatchWriter patch_writer(src_bytes, dst_bytes);
  121. auto status = zucchini::GenerateBuffer(src_bytes, dst_bytes, &patch_writer);
  122. TEST_AND_RETURN_FALSE(status == zucchini::status::kStatusSuccess);
  123. Buffer zucchini_patch_buf(patch_writer.SerializedSize());
  124. patch_writer.SerializeInto(
  125. {zucchini_patch_buf.data(), zucchini_patch_buf.size()});
  126. // Use brotli to compress the zucchini patch.
  127. // TODO(197361113) respect the CompressorType parameter for zucchini.
  128. Buffer compressed_patch;
  129. TEST_AND_RETURN_FALSE(BrotliEncode(zucchini_patch_buf.data(),
  130. zucchini_patch_buf.size(),
  131. &compressed_patch));
  132. TEST_AND_RETURN_FALSE(CreatePatch(
  133. compressed_patch, src_deflates, dst_deflates, src_puffs, dst_puffs,
  134. src_puff_buffer.size(), dst_puff_buffer.size(), patchAlgorithm, patch));
  135. } else {
  136. LOG(ERROR) << "unsupported type " << static_cast<int>(patchAlgorithm);
  137. return false;
  138. }
  139. return true;
  140. }
  141. bool PuffDiff(UniqueStreamPtr src,
  142. UniqueStreamPtr dst,
  143. const std::vector<BitExtent>& src_deflates,
  144. const std::vector<BitExtent>& dst_deflates,
  145. const std::vector<puffin::CompressorType>& compressors,
  146. const std::string& tmp_filepath,
  147. Buffer* patch) {
  148. return PuffDiff(std::move(src), std::move(dst), src_deflates, dst_deflates,
  149. compressors, PatchAlgorithm::kZucchini, tmp_filepath, patch);
  150. }
  151. bool PuffDiff(const Buffer& src,
  152. const Buffer& dst,
  153. const vector<BitExtent>& src_deflates,
  154. const vector<BitExtent>& dst_deflates,
  155. const vector<puffin::CompressorType>& compressors,
  156. const string& tmp_filepath,
  157. Buffer* patch) {
  158. return PuffDiff(MemoryStream::CreateForRead(src),
  159. MemoryStream::CreateForRead(dst), src_deflates, dst_deflates,
  160. compressors, PatchAlgorithm::kZucchini, tmp_filepath, patch);
  161. }
  162. bool PuffDiff(const Buffer& src,
  163. const Buffer& dst,
  164. const vector<BitExtent>& src_deflates,
  165. const vector<BitExtent>& dst_deflates,
  166. const string& tmp_filepath,
  167. Buffer* patch) {
  168. return PuffDiff(src, dst, src_deflates, dst_deflates,
  169. {puffin::CompressorType::kBrotli}, tmp_filepath, patch);
  170. }
  171. } // namespace puffin