web_bundle_chunked_buffer.cc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // Copyright 2021 The Chromium 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 "components/web_package/web_bundle_chunked_buffer.h"
  5. #include <algorithm>
  6. #include "base/check.h"
  7. #include "base/memory/ptr_util.h"
  8. #include "base/numerics/checked_math.h"
  9. namespace web_package {
  10. namespace {
  11. class ChunkedBufferDataSource : public mojo::DataPipeProducer::DataSource {
  12. public:
  13. ChunkedBufferDataSource(std::unique_ptr<const WebBundleChunkedBuffer> buffer,
  14. uint64_t offset,
  15. uint64_t length)
  16. : buffer_(std::move(buffer)), offset_(offset), length_(length) {
  17. DCHECK(buffer_);
  18. }
  19. ~ChunkedBufferDataSource() override = default;
  20. // Disallow copy and assign.
  21. ChunkedBufferDataSource(const ChunkedBufferDataSource&) = delete;
  22. ChunkedBufferDataSource& operator=(const ChunkedBufferDataSource&) = delete;
  23. uint64_t GetLength() const override { return length_; }
  24. ReadResult Read(uint64_t offset, base::span<char> buffer) override {
  25. ReadResult result;
  26. if (offset >= length_) {
  27. result.result = MOJO_RESULT_OUT_OF_RANGE;
  28. return result;
  29. }
  30. uint64_t read_start = offset_ + offset;
  31. uint64_t out_size = buffer.size();
  32. uint64_t len = std::min(length_ - offset, out_size);
  33. buffer_->ReadData(read_start, len,
  34. reinterpret_cast<uint8_t*>(buffer.data()));
  35. result.bytes_read = len;
  36. return result;
  37. }
  38. private:
  39. const std::unique_ptr<const WebBundleChunkedBuffer> buffer_;
  40. const uint64_t offset_;
  41. const uint64_t length_;
  42. };
  43. } // namespace
  44. WebBundleChunkedBuffer::Chunk::Chunk(
  45. uint64_t start_pos,
  46. scoped_refptr<const base::RefCountedBytes> bytes)
  47. : start_pos_(start_pos), bytes_(std::move(bytes)) {
  48. DCHECK(bytes_);
  49. DCHECK(bytes_->size() != 0);
  50. DCHECK(base::CheckAdd<uint64_t>(start_pos_, bytes_->size()).IsValid());
  51. }
  52. WebBundleChunkedBuffer::Chunk::~Chunk() = default;
  53. WebBundleChunkedBuffer::Chunk::Chunk(const WebBundleChunkedBuffer::Chunk&) =
  54. default;
  55. WebBundleChunkedBuffer::Chunk::Chunk(WebBundleChunkedBuffer::Chunk&&) = default;
  56. uint64_t WebBundleChunkedBuffer::Chunk::start_pos() const {
  57. return start_pos_;
  58. }
  59. uint64_t WebBundleChunkedBuffer::Chunk::end_pos() const {
  60. return start_pos_ + bytes_->size();
  61. }
  62. uint64_t WebBundleChunkedBuffer::Chunk::size() const {
  63. return bytes_->size();
  64. }
  65. const uint8_t* WebBundleChunkedBuffer::Chunk::data() const {
  66. return bytes_->data().data();
  67. }
  68. WebBundleChunkedBuffer::WebBundleChunkedBuffer() = default;
  69. WebBundleChunkedBuffer::WebBundleChunkedBuffer(ChunkVector chunks)
  70. : chunks_(std::move(chunks)) {}
  71. WebBundleChunkedBuffer::~WebBundleChunkedBuffer() = default;
  72. void WebBundleChunkedBuffer::Append(const uint8_t* data, size_t num_bytes) {
  73. DCHECK(base::CheckAdd<uint64_t>(end_pos(), num_bytes).IsValid());
  74. if (num_bytes == 0)
  75. return;
  76. auto bytes = base::MakeRefCounted<base::RefCountedBytes>(data, num_bytes);
  77. chunks_.push_back(Chunk(end_pos(), std::move(bytes)));
  78. }
  79. bool WebBundleChunkedBuffer::ContainsAll(uint64_t offset,
  80. uint64_t length) const {
  81. DCHECK(base::CheckAdd<uint64_t>(offset, length).IsValid());
  82. if (length == 0)
  83. return true;
  84. if (offset < start_pos())
  85. return false;
  86. if (offset + length > end_pos())
  87. return false;
  88. return true;
  89. }
  90. std::unique_ptr<mojo::DataPipeProducer::DataSource>
  91. WebBundleChunkedBuffer::CreateDataSource(uint64_t offset,
  92. uint64_t max_length) const {
  93. uint64_t length = GetAvailableLength(offset, max_length);
  94. if (length == 0)
  95. return nullptr;
  96. return std::make_unique<ChunkedBufferDataSource>(
  97. CreatePartialBuffer(offset, length), offset, length);
  98. }
  99. uint64_t WebBundleChunkedBuffer::size() const {
  100. DCHECK_GE(end_pos(), start_pos());
  101. return end_pos() - start_pos();
  102. }
  103. WebBundleChunkedBuffer::ChunkVector::const_iterator
  104. WebBundleChunkedBuffer::FindChunk(uint64_t pos) const {
  105. if (empty())
  106. return chunks_.end();
  107. // |pos| ls before everything
  108. if (pos < chunks_.begin()->start_pos())
  109. return chunks_.end();
  110. // As an optimization, check the last region first
  111. if (chunks_.back().start_pos() <= pos) {
  112. if (chunks_.back().end_pos() <= pos)
  113. return chunks_.end();
  114. return chunks_.end() - 1;
  115. }
  116. // Binary search
  117. return std::partition_point(
  118. chunks_.begin(), chunks_.end(),
  119. [pos](const Chunk& chunk) { return chunk.end_pos() <= pos; });
  120. }
  121. std::unique_ptr<const WebBundleChunkedBuffer>
  122. WebBundleChunkedBuffer::CreatePartialBuffer(uint64_t offset,
  123. uint64_t length) const {
  124. DCHECK(ContainsAll(offset, length));
  125. ChunkVector::const_iterator it = FindChunk(offset);
  126. CHECK(it != chunks_.end());
  127. ChunkVector new_chunks;
  128. while (it != chunks_.end() && it->start_pos() < offset + length) {
  129. new_chunks.push_back(*it);
  130. ++it;
  131. }
  132. return base::WrapUnique(new WebBundleChunkedBuffer(std::move(new_chunks)));
  133. }
  134. bool WebBundleChunkedBuffer::empty() const {
  135. return chunks_.empty();
  136. }
  137. uint64_t WebBundleChunkedBuffer::start_pos() const {
  138. if (empty())
  139. return 0;
  140. return chunks_.front().start_pos();
  141. }
  142. uint64_t WebBundleChunkedBuffer::end_pos() const {
  143. if (empty())
  144. return 0;
  145. return chunks_.back().end_pos();
  146. }
  147. uint64_t WebBundleChunkedBuffer::GetAvailableLength(uint64_t offset,
  148. uint64_t max_length) const {
  149. if (offset < start_pos())
  150. return 0;
  151. if (end_pos() <= offset)
  152. return 0;
  153. return std::min(max_length, end_pos() - offset);
  154. }
  155. uint64_t WebBundleChunkedBuffer::ReadData(uint64_t offset,
  156. uint64_t max_length,
  157. uint8_t* out) const {
  158. uint64_t length = GetAvailableLength(offset, max_length);
  159. if (length == 0)
  160. return 0;
  161. ChunkVector::const_iterator it = FindChunk(offset);
  162. DCHECK(it != chunks_.end());
  163. uint64_t written = 0;
  164. while (length > written) {
  165. DCHECK(it != chunks_.end());
  166. uint64_t offset_in_chunk = offset + written - it->start_pos();
  167. uint64_t length_in_chunk =
  168. std::min(it->size() - offset_in_chunk, length - written);
  169. memcpy(out + written, it->data() + offset_in_chunk, length_in_chunk);
  170. written += length_in_chunk;
  171. ++it;
  172. }
  173. return written;
  174. }
  175. } // namespace web_package